Add Cargo libraries source code. 06/58106/2
authorDariusz Michaluk <d.michaluk@samsung.com>
Mon, 4 Jan 2016 14:04:33 +0000 (15:04 +0100)
committerDariusz Michaluk <d.michaluk@samsung.com>
Wed, 27 Jan 2016 14:57:05 +0000 (15:57 +0100)
Cargo provides json/gvartiant/sqlite/socket/fd serialization,
deserialization, data validation, IPC and logging.

Cargo repository can be found on tizen.org and github.com:
https://review.tizen.org/git/platform/core/security/cargo.git
https://github.com/Samsung/cargo.git

Cargo libraries are used by Vasum project:
https://review.tizen.org/git/platform/core/security/vasum.git
https://github.com/Samsung/vasum.git

Change-Id: Iff45718261bd180f2d3c516b94ba346f24a06b98

231 files changed:
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
common/CMakeLists.txt [new file with mode: 0644]
common/config.hpp [new file with mode: 0644]
common/utils/c-args.hpp [new file with mode: 0644]
common/utils/callback-guard.cpp [new file with mode: 0644]
common/utils/callback-guard.hpp [new file with mode: 0644]
common/utils/callback-wrapper.hpp [new file with mode: 0644]
common/utils/ccolor.cpp [new file with mode: 0644]
common/utils/ccolor.hpp [new file with mode: 0644]
common/utils/channel.cpp [new file with mode: 0644]
common/utils/channel.hpp [new file with mode: 0644]
common/utils/counting-map.hpp [new file with mode: 0644]
common/utils/daemon.cpp [new file with mode: 0644]
common/utils/daemon.hpp [new file with mode: 0644]
common/utils/environment.cpp [new file with mode: 0644]
common/utils/environment.hpp [new file with mode: 0644]
common/utils/eventfd.cpp [new file with mode: 0644]
common/utils/eventfd.hpp [new file with mode: 0644]
common/utils/exception.cpp [new file with mode: 0644]
common/utils/exception.hpp [new file with mode: 0644]
common/utils/execute.cpp [new file with mode: 0644]
common/utils/execute.hpp [new file with mode: 0644]
common/utils/fd-utils.cpp [new file with mode: 0644]
common/utils/fd-utils.hpp [new file with mode: 0644]
common/utils/file-wait.cpp [new file with mode: 0644]
common/utils/file-wait.hpp [new file with mode: 0644]
common/utils/fs.cpp [new file with mode: 0644]
common/utils/fs.hpp [new file with mode: 0644]
common/utils/glib-loop.cpp [new file with mode: 0644]
common/utils/glib-loop.hpp [new file with mode: 0644]
common/utils/glib-utils.cpp [new file with mode: 0644]
common/utils/glib-utils.hpp [new file with mode: 0644]
common/utils/img.cpp [new file with mode: 0644]
common/utils/img.hpp [new file with mode: 0644]
common/utils/initctl.cpp [new file with mode: 0644]
common/utils/initctl.hpp [new file with mode: 0644]
common/utils/inotify.cpp [new file with mode: 0644]
common/utils/inotify.hpp [new file with mode: 0644]
common/utils/latch.cpp [new file with mode: 0644]
common/utils/latch.hpp [new file with mode: 0644]
common/utils/make-clean.hpp [new file with mode: 0644]
common/utils/paths.hpp [new file with mode: 0644]
common/utils/same-thread-guard.cpp [new file with mode: 0644]
common/utils/same-thread-guard.hpp [new file with mode: 0644]
common/utils/scoped-daemon.cpp [new file with mode: 0644]
common/utils/scoped-daemon.hpp [new file with mode: 0644]
common/utils/scoped-dir.cpp [new file with mode: 0644]
common/utils/scoped-dir.hpp [new file with mode: 0644]
common/utils/scoped-gerror.cpp [new file with mode: 0644]
common/utils/scoped-gerror.hpp [new file with mode: 0644]
common/utils/signal.cpp [new file with mode: 0644]
common/utils/signal.hpp [new file with mode: 0644]
common/utils/signalfd.cpp [new file with mode: 0644]
common/utils/signalfd.hpp [new file with mode: 0644]
common/utils/spin-wait-for.hpp [new file with mode: 0644]
common/utils/text.cpp [new file with mode: 0644]
common/utils/text.hpp [new file with mode: 0644]
common/utils/typeinfo.cpp [new file with mode: 0644]
common/utils/typeinfo.hpp [new file with mode: 0644]
common/utils/value-latch.hpp [new file with mode: 0644]
common/utils/vt.cpp [new file with mode: 0644]
common/utils/vt.hpp [new file with mode: 0644]
common/utils/worker.cpp [new file with mode: 0644]
common/utils/worker.hpp [new file with mode: 0644]
doc/cargo_usage.md [new file with mode: 0644]
doc/custom.css [new file with mode: 0644]
doc/doxygen.cfg [new file with mode: 0644]
doc/footer.html [new file with mode: 0644]
doc/generate_documentation.sh [new file with mode: 0755]
doc/header.html [new file with mode: 0644]
libs/cargo-fd/CMakeLists.txt [new file with mode: 0644]
libs/cargo-fd/cargo-fd.hpp [new file with mode: 0644]
libs/cargo-fd/internals/fdstore.cpp [new file with mode: 0644]
libs/cargo-fd/internals/fdstore.hpp [new file with mode: 0644]
libs/cargo-fd/internals/from-fdstore-internet-visitor.hpp [new file with mode: 0644]
libs/cargo-fd/internals/from-fdstore-visitor-base.hpp [new file with mode: 0644]
libs/cargo-fd/internals/from-fdstore-visitor.hpp [new file with mode: 0644]
libs/cargo-fd/internals/to-fdstore-internet-visitor.hpp [new file with mode: 0644]
libs/cargo-fd/internals/to-fdstore-visitor-base.hpp [new file with mode: 0644]
libs/cargo-fd/internals/to-fdstore-visitor.hpp [new file with mode: 0644]
libs/cargo-fd/libcargo-fd.pc.in [new file with mode: 0644]
libs/cargo-gvariant/CMakeLists.txt [new file with mode: 0644]
libs/cargo-gvariant/cargo-gvariant.hpp [new file with mode: 0644]
libs/cargo-gvariant/internals/from-gvariant-visitor.hpp [new file with mode: 0644]
libs/cargo-gvariant/internals/to-gvariant-visitor.hpp [new file with mode: 0644]
libs/cargo-gvariant/libcargo-gvariant.pc.in [new file with mode: 0644]
libs/cargo-ipc/CMakeLists.txt [new file with mode: 0644]
libs/cargo-ipc/client.cpp [new file with mode: 0644]
libs/cargo-ipc/client.hpp [new file with mode: 0644]
libs/cargo-ipc/epoll/event-poll.cpp [new file with mode: 0644]
libs/cargo-ipc/epoll/event-poll.hpp [new file with mode: 0644]
libs/cargo-ipc/epoll/events.cpp [new file with mode: 0644]
libs/cargo-ipc/epoll/events.hpp [new file with mode: 0644]
libs/cargo-ipc/epoll/glib-dispatcher.cpp [new file with mode: 0644]
libs/cargo-ipc/epoll/glib-dispatcher.hpp [new file with mode: 0644]
libs/cargo-ipc/epoll/thread-dispatcher.cpp [new file with mode: 0644]
libs/cargo-ipc/epoll/thread-dispatcher.hpp [new file with mode: 0644]
libs/cargo-ipc/exception.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/acceptor.cpp [new file with mode: 0644]
libs/cargo-ipc/internals/acceptor.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/add-peer-request.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/event-queue.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/finish-request.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/method-request.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/processor.cpp [new file with mode: 0644]
libs/cargo-ipc/internals/processor.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/remove-method-request.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/remove-peer-request.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/request-queue.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/result-builder.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/send-result-request.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/signal-request.hpp [new file with mode: 0644]
libs/cargo-ipc/internals/socket.cpp [new file with mode: 0644]
libs/cargo-ipc/internals/socket.hpp [new file with mode: 0644]
libs/cargo-ipc/libcargo-ipc.pc.in [new file with mode: 0644]
libs/cargo-ipc/method-result.cpp [new file with mode: 0644]
libs/cargo-ipc/method-result.hpp [new file with mode: 0644]
libs/cargo-ipc/result.hpp [new file with mode: 0644]
libs/cargo-ipc/service.cpp [new file with mode: 0644]
libs/cargo-ipc/service.hpp [new file with mode: 0644]
libs/cargo-ipc/types.cpp [new file with mode: 0644]
libs/cargo-ipc/types.hpp [new file with mode: 0644]
libs/cargo-ipc/unique-id.cpp [new file with mode: 0644]
libs/cargo-ipc/unique-id.hpp [new file with mode: 0644]
libs/cargo-json/CMakeLists.txt [new file with mode: 0644]
libs/cargo-json/cargo-json.hpp [new file with mode: 0644]
libs/cargo-json/internals/from-json-visitor.hpp [new file with mode: 0644]
libs/cargo-json/internals/fs-utils.cpp [new file with mode: 0644]
libs/cargo-json/internals/fs-utils.hpp [new file with mode: 0644]
libs/cargo-json/internals/to-json-visitor.hpp [new file with mode: 0644]
libs/cargo-json/libcargo-json.pc.in [new file with mode: 0644]
libs/cargo-sqlite-json/CMakeLists.txt [new file with mode: 0644]
libs/cargo-sqlite-json/cargo-sqlite-json.hpp [new file with mode: 0644]
libs/cargo-sqlite-json/libcargo-sqlite-json.pc.in [new file with mode: 0644]
libs/cargo-sqlite/CMakeLists.txt [new file with mode: 0644]
libs/cargo-sqlite/cargo-sqlite.hpp [new file with mode: 0644]
libs/cargo-sqlite/internals/from-kvstore-ignoring-visitor.hpp [new file with mode: 0644]
libs/cargo-sqlite/internals/from-kvstore-visitor-base.hpp [new file with mode: 0644]
libs/cargo-sqlite/internals/from-kvstore-visitor.hpp [new file with mode: 0644]
libs/cargo-sqlite/internals/kvstore-visitor-utils.hpp [new file with mode: 0644]
libs/cargo-sqlite/internals/kvstore.cpp [new file with mode: 0644]
libs/cargo-sqlite/internals/kvstore.hpp [new file with mode: 0644]
libs/cargo-sqlite/internals/to-kvstore-visitor.hpp [new file with mode: 0644]
libs/cargo-sqlite/libcargo-sqlite.pc.in [new file with mode: 0644]
libs/cargo-sqlite/sqlite3/connection.cpp [new file with mode: 0644]
libs/cargo-sqlite/sqlite3/connection.hpp [new file with mode: 0644]
libs/cargo-sqlite/sqlite3/statement.cpp [new file with mode: 0644]
libs/cargo-sqlite/sqlite3/statement.hpp [new file with mode: 0644]
libs/cargo-validator/CMakeLists.txt [new file with mode: 0644]
libs/cargo-validator/exception.hpp [new file with mode: 0644]
libs/cargo-validator/internals/is-validable.hpp [new file with mode: 0644]
libs/cargo-validator/internals/validator-visitor.hpp [new file with mode: 0644]
libs/cargo-validator/libcargo-validator.pc.in [new file with mode: 0644]
libs/cargo-validator/validator.cpp [new file with mode: 0644]
libs/cargo-validator/validator.hpp [new file with mode: 0644]
libs/cargo/CMakeLists.txt [new file with mode: 0644]
libs/cargo/exception.hpp [new file with mode: 0644]
libs/cargo/fields-union.hpp [new file with mode: 0644]
libs/cargo/fields.hpp [new file with mode: 0644]
libs/cargo/internals/is-like-tuple.hpp [new file with mode: 0644]
libs/cargo/internals/is-streamable.hpp [new file with mode: 0644]
libs/cargo/internals/is-union.hpp [new file with mode: 0644]
libs/cargo/internals/is-visitable.hpp [new file with mode: 0644]
libs/cargo/internals/visit-fields.hpp [new file with mode: 0644]
libs/cargo/libcargo.pc.in [new file with mode: 0644]
libs/cargo/types.hpp [new file with mode: 0644]
libs/logger/CMakeLists.txt [new file with mode: 0644]
libs/logger/backend-file.cpp [new file with mode: 0644]
libs/logger/backend-file.hpp [new file with mode: 0644]
libs/logger/backend-journal.cpp [new file with mode: 0644]
libs/logger/backend-journal.hpp [new file with mode: 0644]
libs/logger/backend-null.hpp [new file with mode: 0644]
libs/logger/backend-persistent-file.cpp [new file with mode: 0644]
libs/logger/backend-persistent-file.hpp [new file with mode: 0644]
libs/logger/backend-stderr.cpp [new file with mode: 0644]
libs/logger/backend-stderr.hpp [new file with mode: 0644]
libs/logger/backend-syslog.cpp [new file with mode: 0644]
libs/logger/backend-syslog.hpp [new file with mode: 0644]
libs/logger/backend.hpp [new file with mode: 0644]
libs/logger/formatter.cpp [new file with mode: 0644]
libs/logger/formatter.hpp [new file with mode: 0644]
libs/logger/level.cpp [new file with mode: 0644]
libs/logger/level.hpp [new file with mode: 0644]
libs/logger/libLogger.pc.in [new file with mode: 0644]
libs/logger/logger-scope.cpp [new file with mode: 0644]
libs/logger/logger-scope.hpp [new file with mode: 0644]
libs/logger/logger.cpp [new file with mode: 0644]
libs/logger/logger.hpp [new file with mode: 0644]
packaging/cargo.spec [new file with mode: 0644]
tests/CMakeLists.txt [new file with mode: 0644]
tests/cppcheck/cppcheck.sh [new file with mode: 0755]
tests/cppcheck/cppcheck.suppress [new file with mode: 0644]
tests/scripts/CMakeLists.txt [new file with mode: 0644]
tests/scripts/cargo_all_tests.py [new file with mode: 0755]
tests/scripts/cargo_launch_test.py [new file with mode: 0755]
tests/scripts/cargo_test_parser.py [new file with mode: 0644]
tests/unit_tests/CMakeLists.txt [new file with mode: 0644]
tests/unit_tests/cargo-ipc/ut-ipc.cpp [new file with mode: 0644]
tests/unit_tests/cargo-ipc/ut-socket.cpp [new file with mode: 0644]
tests/unit_tests/cargo-ipc/ut-unique-id.cpp [new file with mode: 0644]
tests/unit_tests/cargo/testconfig-example.hpp [new file with mode: 0644]
tests/unit_tests/cargo/ut-cargo.cpp [new file with mode: 0644]
tests/unit_tests/cargo/ut-dynvisit.cpp [new file with mode: 0644]
tests/unit_tests/cargo/ut-kvstore.cpp [new file with mode: 0644]
tests/unit_tests/cargo/ut-validator.cpp [new file with mode: 0644]
tests/unit_tests/configs/CMakeLists.txt [new file with mode: 0644]
tests/unit_tests/configs/systemd/cargo-socket-test.service.in [new file with mode: 0644]
tests/unit_tests/configs/systemd/cargo-socket-test.socket [new file with mode: 0644]
tests/unit_tests/configs/utils/file.txt [new file with mode: 0644]
tests/unit_tests/epoll/ut-event-poll.cpp [new file with mode: 0644]
tests/unit_tests/log/ut-logger.cpp [new file with mode: 0644]
tests/unit_tests/socket_test_service/socket-test.cpp [new file with mode: 0644]
tests/unit_tests/socket_test_service/socket-test.hpp [new file with mode: 0644]
tests/unit_tests/ut.cpp [new file with mode: 0644]
tests/unit_tests/ut.hpp [new file with mode: 0644]
tests/unit_tests/utils/ut-callback-guard.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-cargs.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-channel.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-counting-map.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-fd-utils.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-fs.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-glib-loop.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-inotify.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-paths.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-same-thread-guard.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-signalfd.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-text.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-value-latch.cpp [new file with mode: 0644]
tests/unit_tests/utils/ut-worker.cpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..077ef01
--- /dev/null
@@ -0,0 +1,21 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+
+# Compiled Dynamic libraries
+*.so
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+
+# CMake/Makefile files
+*.cmake
+CMakeCache.txt
+CMakeFiles
+Makefile
+
+# Others
+doc/html
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..26899aa
--- /dev/null
@@ -0,0 +1,182 @@
+#Copyright (c) 2014 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Jan Olszak (j.olszak@samsung.com)
+#
+
+CMAKE_MINIMUM_REQUIRED (VERSION 2.6.2)
+PROJECT(cargo)
+
+IF(NOT DEFINED VERSION)
+    SET(VERSION "0.1.2")
+ENDIF(NOT DEFINED VERSION)
+
+## pkgconfig ###################################################################
+INCLUDE(FindPkgConfig)
+
+## default CMAKE_INSTALL_* variables ###########################################
+INCLUDE(GNUInstallDirs)
+
+## Color output if it's possible:
+IF (( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9))
+      OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ))
+
+    IF (CARGO_BUILD_FORCE_COMPILER_COLORS)
+        SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always")
+    ENDIF()
+ENDIF()
+
+## Compiler flags, depending on the build type #################################
+IF(NOT CMAKE_BUILD_TYPE)
+    SET(CMAKE_BUILD_TYPE "DEBUG")
+ENDIF(NOT CMAKE_BUILD_TYPE)
+
+## Print build information #####################################################
+MESSAGE(STATUS "-------------------------------------------------")
+MESSAGE(STATUS "Compiler:          " ${CMAKE_CXX_COMPILER_ID})
+MESSAGE(STATUS "Compiler version:  " ${CMAKE_CXX_COMPILER_VERSION})
+MESSAGE(STATUS "Build type:        " ${CMAKE_BUILD_TYPE})
+MESSAGE(STATUS "-------------------------------------------------")
+
+# special case for a GCC < 4.7, assume rest is fine
+IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7)
+    SET(CXX_11_STD "c++0x")
+else()
+    SET(CXX_11_STD "c++11")
+endif()
+
+SET(CMAKE_C_FLAGS_PROFILING    "-O0 -pg")
+SET(CMAKE_CXX_FLAGS_PROFILING  "-std=${CXX_11_STD} -O0 -pg")
+SET(CMAKE_C_FLAGS_DEBUG        "-O0 -ggdb")
+SET(CMAKE_CXX_FLAGS_DEBUG      "-std=${CXX_11_STD} -O0 -ggdb")
+SET(CMAKE_C_FLAGS_RELEASE      "-O2 -DNDEBUG")
+SET(CMAKE_CXX_FLAGS_RELEASE    "-std=${CXX_11_STD} -O2 -DNDEBUG")
+SET(CMAKE_C_FLAGS_CCOV         "-O0 --coverage")
+SET(CMAKE_CXX_FLAGS_CCOV       "-std=${CXX_11_STD} -O0 --coverage")
+
+IF(DEFINED SANITIZE)
+    # Enable sanitize build.
+    # It works with clang and gcc>=4.8
+    # Possible arguments: address, thread and others (see doc.)
+    # Note on thread sanitizer bugs left in out code:
+    #  - we use non thread save boost test library
+    #  - there are some mysterious problems with glib
+    SET(SANITIZE_FLAGS "-fsanitize=${SANITIZE}")
+    MESSAGE(STATUS "Sanitize flags: ${SANITIZE_FLAGS}")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZE_FLAGS} -fPIE -fno-omit-frame-pointer")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZE_FLAGS} -fPIE -fno-omit-frame-pointer")
+    SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie ${SANITIZE_FLAGS}")
+ENDIF(DEFINED SANITIZE)
+
+ADD_DEFINITIONS("-fPIC")   # Position Independent Code
+ADD_DEFINITIONS("-Werror") # Make all warnings into errors
+ADD_DEFINITIONS("-Wall")   # Generate all warnings
+ADD_DEFINITIONS("-Wextra") # Generate even more extra warnings
+ADD_DEFINITIONS("-pedantic") # Be pedantic
+ADD_DEFINITIONS("-pedantic-errors") # Make pedantic warnings into errors
+ADD_DEFINITIONS(-DPROGRAM_VERSION="${VERSION}")
+ADD_DEFINITIONS(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}")
+ADD_DEFINITIONS(-D__STDC_LIMIT_MACROS)
+
+IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+    # Warn about documentation problems
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdocumentation")
+
+    IF(ALL_WARNINGS)
+        # turn on every -W flags except a few explicitly mentioned
+        ADD_DEFINITIONS("-Wno-error")
+        ADD_DEFINITIONS("-Weverything")
+        ADD_DEFINITIONS("-Wno-c++98-compat")
+        ADD_DEFINITIONS("-Wno-c++98-compat-pedantic")
+        ADD_DEFINITIONS("-Wno-padded")
+        ADD_DEFINITIONS("-Wno-global-constructors")
+        ADD_DEFINITIONS("-Wno-exit-time-destructors")
+        ADD_DEFINITIONS("-Wno-weak-vtables")
+    ENDIF(ALL_WARNINGS)
+ENDIF()
+
+IF(NOT WITHOUT_SYSTEMD)
+    ADD_DEFINITIONS(-DHAVE_SYSTEMD)
+ENDIF(NOT WITHOUT_SYSTEMD)
+
+## Python packages directory ###################################################
+IF(NOT DEFINED PYTHON_SITELIB)
+    EXECUTE_PROCESS(COMMAND python -c
+    "from distutils.sysconfig import get_python_lib; import sys; sys.stdout.write(get_python_lib())"
+    OUTPUT_VARIABLE PYTHON_SITELIB)
+ENDIF(NOT DEFINED PYTHON_SITELIB)
+
+## Subdirectories ##############################################################
+SET(COMMON_FOLDER ${PROJECT_SOURCE_DIR}/common)
+SET(LIBS_FOLDER ${PROJECT_SOURCE_DIR}/libs)
+SET(LOGGER_FOLDER ${PROJECT_SOURCE_DIR}/libs/logger)
+SET(CARGO_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo)
+SET(CARGO_JSON_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo-json)
+SET(CARGO_SQLITE_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo-sqlite)
+SET(CARGO_SQLITE_JSON_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo-sqlite-json)
+SET(CARGO_FD_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo-fd)
+SET(CARGO_GVARIANT_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo-gvariant)
+SET(CARGO_IPC_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo-ipc)
+SET(CARGO_VALIDATOR_FOLDER ${PROJECT_SOURCE_DIR}/libs/cargo-validator)
+SET(TESTS_FOLDER ${PROJECT_SOURCE_DIR}/tests)
+
+
+IF(NOT DEFINED SYSCONF_INSTALL_DIR)
+    SET(SYSCONF_INSTALL_DIR "/etc")
+ENDIF(NOT DEFINED SYSCONF_INSTALL_DIR)
+
+IF(NOT DEFINED LIB_INSTALL_DIR)
+    SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+ENDIF(NOT DEFINED LIB_INSTALL_DIR)
+
+IF(NOT DEFINED INCLUDE_INSTALL_DIR)
+    SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
+ENDIF(NOT DEFINED INCLUDE_INSTALL_DIR)
+
+
+IF(NOT DEFINED SCRIPT_INSTALL_DIR)
+    SET(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SBINDIR}")
+ENDIF(NOT DEFINED SCRIPT_INSTALL_DIR)
+
+IF(NOT DEFINED SYSTEMD_UNIT_DIR)
+    SET(SYSTEMD_UNIT_DIR "${CMAKE_INSTALL_PREFIX}/lib/systemd/system")
+ENDIF(NOT DEFINED SYSTEMD_UNIT_DIR)
+
+IF(NOT DEFINED DATA_DIR)
+    SET(DATA_DIR "${CMAKE_INSTALL_PREFIX}/share")
+ENDIF(NOT DEFINED DATA_DIR)
+
+IF(NOT DEFINED LIBEXEC_DIR)
+    SET(LIBEXEC_DIR "${CMAKE_INSTALL_PREFIX}/libexec")
+ENDIF(NOT DEFINED LIBEXEC_DIR)
+
+IF(NOT DEFINED RUN_DIR)
+    SET(RUN_DIR "/var/run")
+ENDIF(NOT DEFINED RUN_DIR)
+
+SET(CARGO_CONFIG_INSTALL_DIR ${SYSCONF_INSTALL_DIR}/cargo)
+
+ADD_SUBDIRECTORY(${COMMON_FOLDER})
+ADD_SUBDIRECTORY(${LOGGER_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_JSON_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_SQLITE_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_SQLITE_JSON_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_FD_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_GVARIANT_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_IPC_FOLDER})
+ADD_SUBDIRECTORY(${CARGO_VALIDATOR_FOLDER})
+ADD_SUBDIRECTORY(${TESTS_FOLDER})
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c38e144
--- /dev/null
@@ -0,0 +1,44 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Pawel Kubik (p.kubik@samsung.com)
+#
+
+PROJECT(cargo-utils)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the libcargo-utils...")
+FILE(GLOB_RECURSE SRCS *.cpp *.hpp)
+
+## Setup target ################################################################
+ADD_LIBRARY(${PROJECT_NAME} STATIC ${SRCS})
+
+FIND_PACKAGE (Boost REQUIRED COMPONENTS system filesystem)
+PKG_CHECK_MODULES(CARGO_UTILS_DEPS REQUIRED glib-2.0)
+
+INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${LIBS_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS} ${CARGO_UTILS_DEPS_INCLUDE_DIRS})
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${Boost_LIBRARIES} ${CARGO_UTILS_DEPS_LIBRARIES})
+
+## Install #####################################################################
+INSTALL(TARGETS     ${PROJECT_NAME}
+        DESTINATION ${LIB_INSTALL_DIR}
+        COMPONENT   DevelopmentLibraries)
+
+INSTALL(DIRECTORY . DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}
+        FILES_MATCHING PATTERN "*.hpp"
+                       PATTERN "CMakeFiles" EXCLUDE)
diff --git a/common/config.hpp b/common/config.hpp
new file mode 100644 (file)
index 0000000..753430e
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ * @brief   Configuration file for the code
+ */
+
+
+#ifndef COMMON_CONFIG_HPP
+#define COMMON_CONFIG_HPP
+
+
+#ifdef __clang__
+#define CLANG_VERSION (__clang__major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
+#endif // __clang__
+
+#if defined __GNUC__ && !defined __clang__  // clang also defines GCC versions
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#endif // __GNUC__
+
+
+#ifdef GCC_VERSION
+
+#if GCC_VERSION < 40800
+// GCC 4.8 is the first where those defines are not required for
+// std::this_thread::sleep_for() and ::yield(). They might exist though
+// in previous versions depending on the build configuration of the GCC.
+#ifndef _GLIBCXX_USE_NANOSLEEP
+#define _GLIBCXX_USE_NANOSLEEP
+#endif // _GLIBCXX_USE_NANOSLEEP
+#ifndef _GLIBCXX_USE_SCHED_YIELD
+#define _GLIBCXX_USE_SCHED_YIELD
+#endif // _GLIBCXX_USE_SCHED_YIELD
+#endif // GCC_VERSION < 40800
+
+#if GCC_VERSION < 40700
+// Those appeared in 4.7 with full c++11 support
+#define final
+#define override
+#define thread_local __thread  // use GCC extension instead of C++11
+#define steady_clock monotonic_clock
+#endif // GCC_VERSION < 40700
+
+#endif // GCC_VERSION
+
+// Variadic macros support for boost preprocessor should be enabled
+// manually for clang since they are marked as untested feature
+// (boost trunk if fixed but the latest 1.55 version is not,
+// see boost/preprocessor/config/config.hpp)
+#ifdef __clang__
+#define BOOST_PP_VARIADICS 1
+#endif
+
+// This has to be defined always when the boost has not been compiled
+// using C++11. Headers detect that you are compiling using C++11 and
+// blindly and wrongly assume that boost has been as well.
+#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
+#define BOOST_NO_CXX11_SCOPED_ENUMS 1
+#endif
+
+#endif // COMMON_CONFIG_HPP
diff --git a/common/utils/c-args.hpp b/common/utils/c-args.hpp
new file mode 100644 (file)
index 0000000..726235c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Krzysztof Dynowski (k.dynowski@samsumg.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief   C arguments array builder (null terminated array of strings)
+ */
+
+#ifndef COMMON_UTILS_C_ARGS_HPP
+#define COMMON_UTILS_C_ARGS_HPP
+
+#include <vector>
+#include <iostream>
+
+namespace utils {
+
+class CArgsBuilder {
+public:
+    CArgsBuilder() { }
+
+    template<typename T>
+    CArgsBuilder& add(T v) {
+        return add(std::to_string(v));
+    }
+
+    CArgsBuilder& add(const std::vector<std::string>& v) {
+        mArray.reserve(size() + v.size());
+        mArray.insert(mArray.end(), v.begin(), v.end());
+        return *this;
+    }
+
+    CArgsBuilder& add(const std::string& v) {
+        mArray.push_back(v);
+        return *this;
+    }
+
+    CArgsBuilder& add(char *v) {
+        return add(std::string(v));
+    }
+
+    CArgsBuilder& add(const char *v) {
+        return add(std::string(v));
+    }
+
+    const char* const* c_array() const {
+        regenerate();
+        return mArgs.data();
+    }
+
+    size_t size() const {
+        return mArray.size();
+    }
+
+    bool empty() const {
+        return size() == 0;
+    }
+
+    const char *operator[](int i) const {
+        return mArray[i].c_str();
+    }
+
+private:
+    void regenerate() const
+    {
+        std::vector<const char*>& args = *const_cast<std::vector<const char*>*>(&mArgs);
+        args.clear();
+        args.reserve(mArray.size() + 1);
+        for (const auto& a : mArray) {
+            args.push_back(a.c_str());
+        }
+        args.push_back(nullptr);
+    }
+
+    std::vector<std::string> mArray;
+    std::vector<const char*> mArgs;
+};
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_C_ARGS_HPP
diff --git a/common/utils/callback-guard.cpp b/common/utils/callback-guard.cpp
new file mode 100644 (file)
index 0000000..d80e37d
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Callback guard
+ */
+
+#include "config.hpp"
+
+#include "utils/callback-guard.hpp"
+#include "logger/logger.hpp"
+
+#include <mutex>
+#include <condition_variable>
+#include <cassert>
+
+
+namespace utils {
+
+// Reference counting class like shared_ptr but with the ability to wait for it.
+class CallbackGuard::SharedState {
+public:
+    SharedState() : mCounter(0) {}
+
+    void inc()
+    {
+        std::unique_lock<std::mutex> lock(mMutex);
+        ++mCounter;
+    }
+
+    void dec()
+    {
+        std::unique_lock<std::mutex> lock(mMutex);
+        --mCounter;
+        mEmptyCondition.notify_all();
+    }
+
+    long size()
+    {
+        std::unique_lock<std::mutex> lock(mMutex);
+        return mCounter;
+    }
+
+    bool wait(const unsigned int timeoutMs)
+    {
+        std::unique_lock<std::mutex> lock(mMutex);
+        return mEmptyCondition.wait_for(lock,
+                                        std::chrono::milliseconds(timeoutMs),
+                                        [this] {return mCounter == 0;});
+    }
+private:
+    std::mutex mMutex;
+    std::condition_variable mEmptyCondition;
+    long mCounter;
+};
+
+namespace {
+
+template<class SharedState>
+class TrackerImpl {
+public:
+    TrackerImpl(const std::shared_ptr<SharedState>& sharedState)
+        : mSharedState(sharedState)
+    {
+        mSharedState->inc();
+    }
+
+    ~TrackerImpl()
+    {
+        mSharedState->dec();
+    }
+private:
+    std::shared_ptr<SharedState> mSharedState;
+};
+
+// Relatively big timeout in case of deadlock.
+// This timeout should never be exceeded with
+// a properly written code.
+const unsigned int TIMEOUT = 5000;
+
+} // namespace
+
+
+CallbackGuard::CallbackGuard()
+    : mSharedState(new SharedState())
+{
+}
+
+CallbackGuard::~CallbackGuard()
+{
+    if (!waitForTrackers(TIMEOUT)) {
+        LOGE("==== DETECTED INVALID CALLBACK USE ====");
+        assert(0 && "Invalid callback use");
+    }
+}
+
+CallbackGuard::Tracker CallbackGuard::spawn() const
+{
+    return Tracker(new TrackerImpl<SharedState>(mSharedState));
+}
+
+long CallbackGuard::getTrackersCount() const
+{
+    return mSharedState->size();
+}
+
+bool CallbackGuard::waitForTrackers(const unsigned int timeoutMs)
+{
+    return mSharedState->wait(timeoutMs);
+}
+
+} // namespace utils
diff --git a/common/utils/callback-guard.hpp b/common/utils/callback-guard.hpp
new file mode 100644 (file)
index 0000000..7b889be
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Callback guard
+ */
+
+#ifndef COMMON_UTILS_CALLBACK_GUARD_HPP
+#define COMMON_UTILS_CALLBACK_GUARD_HPP
+
+#include <memory>
+
+
+namespace utils {
+
+/**
+ * Callback guard.
+ * An utility class to control and/or monitor callback lifecycle.
+ */
+class CallbackGuard {
+public:
+    typedef std::shared_ptr<void> Tracker;
+
+    /**
+     * Creates a guard.
+     */
+    CallbackGuard();
+
+    /**
+     * Waits for all trackers.
+     */
+    ~CallbackGuard();
+
+    /**
+     * Creates a tracker.
+     */
+    Tracker spawn() const;
+
+    /**
+     * Gets trackers count
+     */
+    long getTrackersCount() const;
+
+    /**
+     * Wait for all trackers.
+     */
+    bool waitForTrackers(const unsigned int timeoutMs);
+private:
+    class SharedState;
+    std::shared_ptr<SharedState> mSharedState;
+
+    CallbackGuard(const CallbackGuard&) = delete;
+    CallbackGuard& operator=(const CallbackGuard&) = delete;
+};
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_CALLBACK_GUARD_HPP
diff --git a/common/utils/callback-wrapper.hpp b/common/utils/callback-wrapper.hpp
new file mode 100644 (file)
index 0000000..1ed2b46
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Callback wrapper
+ */
+
+#ifndef COMMON_UTILS_CALLBACK_WRAPPER_HPP
+#define COMMON_UTILS_CALLBACK_WRAPPER_HPP
+
+#include "utils/callback-guard.hpp"
+
+
+namespace utils {
+
+
+/**
+ * Wraps callback and callback tracker into single object
+ */
+template<class Callback>
+class CallbackWrapper {
+public:
+    CallbackWrapper(const Callback& callback, const CallbackGuard::Tracker& tracker)
+        : mCallback(callback)
+        , mTracker(tracker)
+    {
+    }
+
+    /**
+     * @return Wrapped callback
+     */
+    const Callback& get() const
+    {
+        return mCallback;
+    }
+private:
+    Callback mCallback;
+    CallbackGuard::Tracker mTracker;
+};
+
+/**
+ * Creates callback wrapper. Useful for C callback api.
+ */
+template<class Callback>
+CallbackWrapper<Callback>* createCallbackWrapper(const Callback& callback,
+                                                 const CallbackGuard::Tracker& tracker)
+{
+    return new CallbackWrapper<Callback>(callback, tracker);
+}
+
+/**
+ * Deletes callback wrapper. Useful for C callback api.
+ */
+template<class Callback>
+void deleteCallbackWrapper(void* pointer)
+{
+    delete reinterpret_cast<CallbackWrapper<Callback>*>(pointer);
+}
+
+/**
+ * Recovers callback from wrapper pointer. Useful for C callback api.
+ */
+template<class Callback>
+const Callback& getCallbackFromPointer(const void* pointer)
+{
+    return reinterpret_cast<const CallbackWrapper<Callback>*>(pointer)->get();
+}
+
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_CALLBACK_WRAPPER_HPP
diff --git a/common/utils/ccolor.cpp b/common/utils/ccolor.cpp
new file mode 100644 (file)
index 0000000..795da3e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk (d.michaluk@samsung.com)
+ * @brief   Console colors utility
+ */
+
+#include "config.hpp"
+
+#include "utils/ccolor.hpp"
+
+#include <stdio.h>
+
+namespace utils {
+
+std::string getConsoleEscapeSequence(Attributes attr, Color color)
+{
+    char command[10];
+
+    // Command is the control command to the terminal
+    snprintf(command, sizeof(command), "%c[%u;%um", 0x1B, (unsigned int)attr, (unsigned int)color);
+    return std::string(command);
+}
+
+} // namespace utils
diff --git a/common/utils/ccolor.hpp b/common/utils/ccolor.hpp
new file mode 100644 (file)
index 0000000..b579354
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk (d.michaluk@samsung.com)
+ * @brief   Console colors utility
+ */
+
+#ifndef COMMON_UTILS_CCOLOR_HPP
+#define COMMON_UTILS_CCOLOR_HPP
+
+#include <string>
+
+namespace utils {
+
+enum class Color : unsigned int {
+    DEFAULT     = 0,
+    BLACK       = 30,
+    RED         = 31,
+    GREEN       = 32,
+    YELLOW      = 33,
+    BLUE        = 34,
+    MAGENTA     = 35,
+    CYAN        = 36,
+    WHITE       = 37
+};
+
+enum class Attributes : unsigned int {
+    DEFAULT     = 0,
+    BOLD        = 1
+};
+
+std::string getConsoleEscapeSequence(Attributes attr, Color color);
+
+} // namespace utils
+
+#endif // COMMON_UTILS_CCOLOR_HPP
diff --git a/common/utils/channel.cpp b/common/utils/channel.cpp
new file mode 100644 (file)
index 0000000..98b716d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   IPC implementation for related processes
+ */
+
+#include "config.hpp"
+
+#include "utils/channel.hpp"
+#include "utils/exception.hpp"
+
+#include "logger/logger.hpp"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+namespace {
+const int LEFT = 0;
+const int RIGHT = 1;
+}
+
+namespace utils {
+
+Channel::Channel(const bool closeOnExec)
+    : mSocketIndex(-1)
+{
+    int flags = SOCK_STREAM;
+    if (closeOnExec) {
+        flags |= SOCK_CLOEXEC;
+    };
+
+    if (::socketpair(AF_LOCAL, flags, 0, mSockets.data()) < 0) {
+        const std::string msg = "socketpair() failed: " +
+                                utils::getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+Channel::Channel(const int fd)
+    : mSocketIndex(LEFT),
+      mSockets{{fd, -1}}
+{
+    assert(fd >= 0);
+}
+
+Channel::~Channel()
+{
+    closeSocket(LEFT);
+    closeSocket(RIGHT);
+}
+
+/*
+ * This function has to be safe in regard to signal(7)
+ */
+void Channel::setLeft()
+{
+    mSocketIndex = LEFT;
+    ::close(mSockets[RIGHT]);
+    mSockets[RIGHT] = -1;
+}
+
+/*
+ * This function has to be safe in regard to signal(7)
+ */
+void Channel::setRight()
+{
+    mSocketIndex = RIGHT;
+    ::close(mSockets[LEFT]);
+    mSockets[LEFT] = -1;
+}
+
+void Channel::shutdown()
+{
+    assert(mSocketIndex != -1 && "Channel's end isn't set");
+    closeSocket(mSocketIndex);
+}
+
+int Channel::getFD()
+{
+    assert(mSocketIndex != -1 && "Channel's end isn't set");
+    return mSockets[mSocketIndex];
+}
+
+int Channel::getLeftFD()
+{
+    return mSockets[LEFT];
+}
+
+int Channel::getRightFD()
+{
+    return mSockets[RIGHT];
+}
+
+void Channel::setCloseOnExec(const bool closeOnExec)
+{
+    const int fd = getFD();
+
+    utils::setCloseOnExec(fd, closeOnExec);
+}
+
+void Channel::closeSocket(int socketIndex)
+{
+    utils::shutdown(mSockets[socketIndex]);
+    utils::close(mSockets[socketIndex]);
+    mSockets[socketIndex] = -1;
+}
+
+} // namespace utils
diff --git a/common/utils/channel.hpp b/common/utils/channel.hpp
new file mode 100644 (file)
index 0000000..3b60daa
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   IPC implementation for related processes
+ */
+
+#ifndef COMMON_UTILS_CHANNEL_HPP
+#define COMMON_UTILS_CHANNEL_HPP
+
+#include "utils/fd-utils.hpp"
+
+#include <array>
+#include <cassert>
+
+namespace utils {
+
+/**
+ * Channel is implemented with a pair of anonymous sockets
+ */
+class Channel {
+public:
+    explicit Channel(const bool closeOnExec = true);
+    explicit Channel(const int fd);
+    ~Channel();
+
+    Channel(const Channel&) = delete;
+    Channel& operator=(const Channel&) = delete;
+
+    /**
+     * Use the "left" end of the channel
+     * Closes the "right" end
+     */
+    void setLeft();
+
+    /**
+    * Use the "right" end of the channel
+    * Closes the "left" end
+    */
+    void setRight();
+
+    /**
+     * Gracefully shutdown the used end of the channel
+     */
+    void shutdown();
+
+    /**
+     * Send the data to the other end of the channel
+     *
+     * @param data data to send
+     */
+    template<typename Data>
+    void write(const Data& data);
+
+    /**
+     * Receive data of a given type (size)
+     */
+    template<typename Data>
+    Data read();
+
+    /**
+     * Get an active file descriptor
+     */
+    int getFD();
+
+    /**
+     * Gen the left file descriptor
+     */
+    int getLeftFD();
+
+    /**
+     * Gen the right file descriptor
+     */
+    int getRightFD();
+
+    /**
+     * Sets close on exec on an active fd to either true or false
+     */
+    void setCloseOnExec(const bool closeOnExec);
+
+private:
+
+    void closeSocket(int socketIndex);
+
+    int mSocketIndex;
+    std::array<int, 2> mSockets;
+};
+
+template<typename Data>
+void Channel::write(const Data& data)
+{
+    assert(mSocketIndex != -1 && "Channel's end isn't set");
+
+    utils::write(mSockets[mSocketIndex], &data, sizeof(Data));
+}
+
+template<typename Data>
+Data Channel::read()
+{
+    assert(mSocketIndex != -1 && "Channel's end isn't set");
+
+    Data data;
+    utils::read(mSockets[mSocketIndex], &data, sizeof(Data));
+    return data;
+}
+
+} // namespace utils
+
+#endif // COMMON_UTILS_CHANNEL_HPP
diff --git a/common/utils/counting-map.hpp b/common/utils/counting-map.hpp
new file mode 100644 (file)
index 0000000..a05f76a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Counting map
+ */
+
+#ifndef COMMON_UTILS_COUNTING_MAP_HPP
+#define COMMON_UTILS_COUNTING_MAP_HPP
+
+#include <unordered_map>
+
+namespace utils {
+
+
+/**
+ * Structure used to count elements.
+ * It's like multiset + count but is more efficient.
+ */
+template<class Key>
+class CountingMap {
+public:
+    size_t increment(const Key& key)
+    {
+        auto res = mMap.insert(typename Map::value_type(key, 1));
+        if (!res.second) {
+            ++res.first->second;
+        }
+        return res.first->second;
+    }
+
+    size_t decrement(const Key& key)
+    {
+        auto it = mMap.find(key);
+        if (it == mMap.end()) {
+            return 0;
+        }
+        if (--it->second == 0) {
+            mMap.erase(it);
+            return 0;
+        }
+        return it->second;
+    }
+
+    void clear()
+    {
+        mMap.clear();
+    }
+
+    size_t get(const Key& key) const
+    {
+        auto it = mMap.find(key);
+        return it == mMap.end() ? 0 : it->second;
+    }
+
+    bool empty() const
+    {
+        return mMap.empty();
+    }
+private:
+    typedef std::unordered_map<Key, size_t> Map;
+    Map mMap;
+};
+
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_COUNTING_MAP_HPP
diff --git a/common/utils/daemon.cpp b/common/utils/daemon.cpp
new file mode 100644 (file)
index 0000000..9a40208
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk <d.michaluk@samsung.com>
+ * @brief   Run a process as a daemon
+ */
+
+#include "config.hpp"
+
+#include "utils/fd-utils.hpp"
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <iostream>
+
+namespace utils {
+
+bool daemonize()
+{
+    pid_t pid = ::fork();
+
+    if (pid == -1) {
+        std::cerr << "Fork failed" << std::endl;
+        return false;
+    } else if (pid != 0) {
+        exit (0);
+    }
+
+    if (::setsid() == -1) {
+        return false;
+    }
+
+    pid = ::fork();
+
+    /*
+     * Fork a second child and exit immediately to prevent zombies.
+     * This causes the second child process to be orphaned, making the init
+     * process responsible for its cleanup. And, since the first child is
+     * a session leader without a controlling terminal, it's possible for
+     * it to acquire one by opening a terminal in the future (System V
+     * based systems). This second fork guarantees that the child is no
+     * longer a session leader, preventing the daemon from ever acquiring
+     * a controlling terminal.
+     */
+
+    if (pid == -1) {
+        std::cerr << "Fork failed" << std::endl;
+        return false;
+    } else if (pid != 0) {
+        exit(0);
+    }
+
+    if (::chdir("/") == -1) {
+        return false;
+    }
+
+    ::umask(0);
+
+    /** Close all open standard file descriptors */
+    int fd = ::open("/dev/null", O_RDWR);
+    if (fd == -1) {
+        return false;
+    }
+
+    for (int i = 0; i <= 2; i++) {
+        if (::dup2(fd, i) == -1) {
+            utils::close(fd);
+            return false;
+        }
+    }
+    utils::close(fd);
+
+    return true;
+}
+
+} // namespace utils
diff --git a/common/utils/daemon.hpp b/common/utils/daemon.hpp
new file mode 100644 (file)
index 0000000..4b5924a
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk <d.michaluk@samsung.com>
+ * @brief   Run a process as a daemon
+ */
+
+#ifndef COMMON_UTILS_DAEMON_HPP
+#define COMMON_UTILS_DAEMON_HPP
+
+namespace utils {
+
+bool daemonize();
+
+} // namespace utils
+
+#endif // COMMON_UTILS_DAEMON_HPP
diff --git a/common/utils/environment.cpp b/common/utils/environment.cpp
new file mode 100644 (file)
index 0000000..7ae505b
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Michal Witanowski <m.witanowski@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Michal Witanowski (m.witanowski@samsung.com)
+ * @brief   Implementaion of environment setup routines that require root privileges
+ */
+
+#include "config.hpp"
+
+#include "utils/environment.hpp"
+#include "utils/execute.hpp"
+#include "utils/exception.hpp"
+#include "utils/make-clean.hpp"
+#include "utils/fd-utils.hpp"
+#include "logger/logger.hpp"
+
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <cstring>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <map>
+#include <iomanip>
+#include <cassert>
+#include <features.h>
+#include <linux/capability.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+
+#if !__GLIBC_PREREQ(2, 14)
+
+#ifdef __NR_setns
+static inline int setns(int fd, int nstype)
+{
+    // setns system call are available since v2.6.39-6479-g7b21fdd
+    return syscall(__NR_setns, fd, nstype);
+}
+#else
+#error "setns syscall isn't available"
+#endif
+
+#endif
+
+#ifdef __NR_capset
+static inline int capset(cap_user_header_t header, const cap_user_data_t data)
+{
+    return syscall(__NR_capset, header, data);
+}
+#else
+#error "capset syscall isn't available"
+#endif
+
+using namespace utils;
+
+namespace {
+
+#define CAP_SET_INHERITABLE (1 << 0)
+#define CAP_SET_PERMITTED (1 << 1)
+#define CAP_SET_EFFECTIVE (1 << 2)
+
+// number of __user_cap_data_struct elements needed
+#define CAP_DATA_ELEMENT_COUNT 2
+
+typedef unsigned int CapSet;
+
+const std::map<int, std::string> NAMESPACES = {
+    {CLONE_NEWIPC, "ipc"},
+    {CLONE_NEWNET, "net"},
+    {CLONE_NEWNS, "mnt"},
+    {CLONE_NEWPID, "pid"},
+    {CLONE_NEWUSER, "user"},
+    {CLONE_NEWUTS, "uts"}};
+
+inline bool isValidCap(unsigned int cap)
+{
+    return cap <= CAP_LAST_CAP;
+}
+
+// hasCap assumes that "set" variable will refer to only one set of capabilities
+inline bool hasCap(unsigned int cap, const cap_user_data_t data, CapSet set)
+{
+    // calculate which half of data we need to update
+    int dataInd = 0;
+    if (cap > 31) {
+        dataInd = cap >> 5;
+        cap %= 32;
+    }
+
+    switch (set) {
+    case CAP_SET_INHERITABLE:
+        return CAP_TO_MASK(cap) & data[dataInd].inheritable ? true : false;
+    case CAP_SET_PERMITTED:
+        return CAP_TO_MASK(cap) & data[dataInd].permitted ? true : false;
+    case CAP_SET_EFFECTIVE:
+        return CAP_TO_MASK(cap) & data[dataInd].effective ? true : false;
+    default:
+        return false;
+    };
+}
+
+// these inlines work in-place and update provided "data" array
+// in these inlines, "set" can refer to mulitple sets of capabilities
+inline void addCap(unsigned int cap, cap_user_data_t data, CapSet set)
+{
+    // calculate which half of data we need to update
+    int dataInd = 0;
+    if (cap > 31) {
+        dataInd = cap >> 5;
+        cap %= 32;
+    }
+
+    if ((set & CAP_SET_INHERITABLE) == CAP_SET_INHERITABLE) {
+        data[dataInd].inheritable |= CAP_TO_MASK(cap);
+    }
+    if ((set & CAP_SET_PERMITTED) == CAP_SET_PERMITTED) {
+        data[dataInd].permitted |= CAP_TO_MASK(cap);
+    }
+    if ((set & CAP_SET_EFFECTIVE) == CAP_SET_EFFECTIVE) {
+        data[dataInd].effective |= CAP_TO_MASK(cap);
+    }
+}
+
+inline void removeCap(unsigned int cap, cap_user_data_t data, CapSet set)
+{
+    // calculate which half of data we need to update
+    int dataInd = 0;
+    if (cap > 31) {
+        dataInd = cap >> 5;
+        cap %= 32;
+    }
+
+    if ((set & CAP_SET_INHERITABLE) == CAP_SET_INHERITABLE) {
+        data[dataInd].inheritable &= ~(CAP_TO_MASK(cap));
+    }
+    if ((set & CAP_SET_PERMITTED) == CAP_SET_PERMITTED) {
+        data[dataInd].permitted &= ~(CAP_TO_MASK(cap));
+    }
+    if ((set & CAP_SET_EFFECTIVE) == CAP_SET_EFFECTIVE) {
+        data[dataInd].effective &= ~(CAP_TO_MASK(cap));
+    }
+}
+
+} // namespace
+
+namespace utils {
+
+
+bool setSuppGroups(const std::vector<std::string>& groups)
+{
+    std::vector<gid_t> gids;
+
+    for (const std::string& group : groups) {
+        // get GID from name
+        struct group* grp = ::getgrnam(group.c_str());
+        if (grp == NULL) {
+            LOGE("getgrnam failed to find group '" << group << "'");
+            return false;
+        }
+
+        LOGD("'" << group << "' group ID: " << grp->gr_gid);
+        gids.push_back(grp->gr_gid);
+    }
+
+    if (::setgroups(gids.size(), gids.data()) != 0) {
+        LOGE("setgroups() failed: " << getSystemErrorMessage());
+        return false;
+    }
+
+    return true;
+}
+
+bool dropRoot(uid_t uid, gid_t gid, const std::vector<unsigned int>& caps)
+{
+    ::__user_cap_header_struct header;
+    ::__user_cap_data_struct data[CAP_DATA_ELEMENT_COUNT];
+
+    // initial setup - equivalent to capng_clear
+    header.version = _LINUX_CAPABILITY_VERSION_3;
+    header.pid = ::getpid();
+    memset(data, 0, CAP_DATA_ELEMENT_COUNT*sizeof(__user_cap_data_struct));
+
+    // update cap sets - equivalent to capng_update
+    for (const auto cap : caps) {
+        if (!isValidCap(cap)) {
+            LOGE("Capability " << cap << " is invalid.");
+            return false;
+        }
+
+        addCap(cap, data, CAP_SET_INHERITABLE | CAP_SET_PERMITTED | CAP_SET_EFFECTIVE);
+    }
+
+    // perform some checks and cap updates
+    bool updatedSetUid, updatedSetGid;
+    // check if we are capable of switching our UID
+    if (hasCap(CAP_SETUID, data, CAP_SET_EFFECTIVE)) {
+        // we want to keep CAP_SETUID after change
+        updatedSetUid = false;
+    } else {
+        // we don't have CAP_SETUID and switch is needed - add SETUID to effective and permitted set
+        updatedSetUid = true;
+        addCap(CAP_SETUID, data, CAP_SET_PERMITTED | CAP_SET_EFFECTIVE);
+    }
+
+    // do the same routine for CAP_SETGID
+    if (hasCap(CAP_SETGID, data, CAP_SET_EFFECTIVE)) {
+        updatedSetGid = false;
+    } else {
+        updatedSetGid = true;
+        addCap(CAP_SETGID, data, CAP_SET_PERMITTED | CAP_SET_EFFECTIVE);
+    }
+
+    // we need CAP_SETPCAP as well to clear bounding caps
+    if (!hasCap(CAP_SETPCAP, data, CAP_SET_EFFECTIVE)) {
+        addCap(CAP_SETPCAP, data, CAP_SET_PERMITTED | CAP_SET_EFFECTIVE);
+    }
+
+    // now we can work - first, use prctl to tell system we want to keep our caps when changing UID
+    if (::prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
+        LOGE("prctl failed while trying to enable keepcaps: " << strerror(errno));
+        return false;
+    }
+
+    LOGD("Setting temporary caps to process -" << std::hex << std::setfill('0')
+         << " inh:" << std::setw(8) << data[1].inheritable << std::setw(8) << data[0].inheritable
+         << " prm:" << std::setw(8) << data[1].permitted << std::setw(8) << data[0].permitted
+         << " eff:" << std::setw(8) << data[1].effective << std::setw(8) << data[0].effective);
+
+    // set our modified caps before UID/GID change
+    if (::capset(&header, data)) {
+        LOGE("capset failed: " << strerror(errno));
+        return false;
+    }
+
+    // CAP_SETPCAP is available, drop bounding caps
+    for (int i = 0; i <= CAP_LAST_CAP; ++i) {
+        if (::prctl(PR_CAPBSET_DROP, i, 0, 0, 0)) {
+            LOGE("prctl failed while dropping bounding caps: " << strerror(errno));
+            return false;
+        }
+    }
+
+    // set up GID and UID
+    if (::setresgid(gid, gid, gid)) {
+        LOGE("setresgid failed: " << strerror(errno));
+        return false;
+    }
+    if (::setresuid(uid, uid, uid)) {
+        LOGE("setresuid failed: " << strerror(errno));
+        return false;
+    }
+
+    // we are after switch now - disable PR_SET_KEEPCAPS
+    if (::prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0)) {
+        LOGE("prctl failed while trying to disable keepcaps: " << strerror(errno));
+        return false;
+    }
+
+    // disable rendundant caps
+    if (updatedSetUid) {
+        removeCap(CAP_SETUID, data, CAP_SET_PERMITTED | CAP_SET_EFFECTIVE);
+    }
+    if (updatedSetGid) {
+        removeCap(CAP_SETGID, data, CAP_SET_PERMITTED | CAP_SET_EFFECTIVE);
+    }
+    removeCap(CAP_SETPCAP, data, CAP_SET_PERMITTED | CAP_SET_EFFECTIVE);
+
+    LOGD("Setting final caps to process -" << std::hex << std::setfill('0')
+         << " inh:" << std::setw(8) << data[1].inheritable << std::setw(8) << data[0].inheritable
+         << " prm:" << std::setw(8) << data[1].permitted << std::setw(8) << data[0].permitted
+         << " eff:" << std::setw(8) << data[1].effective << std::setw(8) << data[0].effective);
+
+    // finally, apply correct caps
+    if (::capset(&header, data)) {
+        LOGE("capset failed: " << strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+bool launchAsRoot(const std::vector<std::string>& argv)
+{
+    return executeAndWait(0, argv);
+}
+
+bool joinToNs(int nsPid, int ns)
+{
+    auto ins = NAMESPACES.find(ns);
+    if (ins == NAMESPACES.end()) {
+        LOGE("Namespace isn't supported: " << ns);
+        return false;
+    }
+    std::string nsPath = "/proc/" + std::to_string(nsPid) + "/ns/" + ins->second;
+    int nsFd = ::open(nsPath.c_str(), O_RDONLY);
+    if (nsFd == -1) {
+        LOGE("Can't open namesace: " + getSystemErrorMessage());
+        return false;
+    }
+    int ret = setns(nsFd, ins->first);
+    if (ret != 0) {
+        LOGE("Can't set namesace: " + getSystemErrorMessage());
+        close(nsFd);
+        return false;
+    }
+    close(nsFd);
+    return true;
+}
+
+// FIXME remove (not used in lxcpp)
+int passNamespacedFd(int nsPid, int ns, const std::function<int()>& fdFactory)
+{
+    int fds[2];
+    int ret = socketpair(PF_LOCAL, SOCK_RAW, 0, fds);
+    if (ret == -1) {
+        LOGE("Can't create socket pair: " << getSystemErrorMessage());
+        return -1;
+    }
+    bool success = executeAndWait([&, fds, nsPid, ns]() {
+        close(fds[0]);
+
+        int fd = -1;
+        if (joinToNs(nsPid, ns)) {
+            fd = fdFactory();
+        }
+        if (fd == -1) {
+            close(fds[1]);
+            _exit(EXIT_FAILURE);
+        }
+        LOGT("FD pass, send: " << fd);
+        fdSend(fds[1], fd);
+        close(fds[1]);
+        close(fd);
+    });
+
+    close(fds[1]);
+    int fd = -1;
+    if (success) {
+        fd = fdRecv(fds[0]);
+    }
+    close(fds[0]);
+    LOGT("FD pass, rcv: " << fd);
+    return fd;
+}
+
+} // namespace utils
diff --git a/common/utils/environment.hpp b/common/utils/environment.hpp
new file mode 100644 (file)
index 0000000..c27331c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Michal Witanowski <m.witanowski@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Michal Witanowski (m.witanowski@samsung.com)
+ * @brief   Declaration of environment setup routines that requires root privileges
+ */
+
+#ifndef COMMON_UTILS_ENVIRONMENT_HPP
+#define COMMON_UTILS_ENVIRONMENT_HPP
+
+#include <string>
+#include <vector>
+#include <functional>
+#include <sys/types.h>
+
+
+namespace utils {
+
+
+/**
+ * Set supplementary groups to the current process.
+ */
+bool setSuppGroups(const std::vector<std::string>& groups);
+
+/**
+ * Set effective and permitted capabilities on the current process and drop root privileges.
+ */
+bool dropRoot(uid_t uid, gid_t gid, const std::vector<unsigned int>& caps);
+
+/**
+ * Launch binary as root user
+ *
+ * This function forks, sets UID 0 to child process and calls binary.
+ */
+bool launchAsRoot(const std::vector<std::string>& argv);
+
+/**
+ * Join to namespace
+ */
+bool joinToNs(int nsPid, int ns);
+
+/**
+ * Pass file descriptor from namespace of some process
+ */
+int passNamespacedFd(int nsPid, int ns, const std::function<int()>& fdFactory);
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_ENVIRONMENT_HPP
diff --git a/common/utils/eventfd.cpp b/common/utils/eventfd.cpp
new file mode 100644 (file)
index 0000000..627b773
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Linux socket wrapper
+ */
+
+#include "config.hpp"
+
+#include "utils/eventfd.hpp"
+#include "utils/exception.hpp"
+#include "utils/fd-utils.hpp"
+#include "logger/logger.hpp"
+
+#include <sys/eventfd.h>
+#include <cerrno>
+#include <cstring>
+#include <cstdint>
+
+namespace utils {
+
+EventFD::EventFD()
+{
+    mFD = ::eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC);
+    if (mFD == -1) {
+        const std::string msg = "Error in eventfd: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw EventFDException(msg);
+    }
+}
+
+EventFD::~EventFD()
+{
+    utils::close(mFD);
+}
+
+int EventFD::getFD() const
+{
+    return mFD;
+}
+
+void EventFD::send()
+{
+    const std::uint64_t toSend = 1;
+    utils::write(mFD, &toSend, sizeof(toSend));
+}
+
+void EventFD::receive()
+{
+    std::uint64_t readBuffer;
+    utils::read(mFD, &readBuffer, sizeof(readBuffer));
+}
+
+
+} // namespace utils
diff --git a/common/utils/eventfd.hpp b/common/utils/eventfd.hpp
new file mode 100644 (file)
index 0000000..698847d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Eventfd wrapper
+ */
+
+#ifndef COMMON_UTILS_EVENTFD_HPP
+#define COMMON_UTILS_EVENTFD_HPP
+
+namespace utils {
+
+class EventFD {
+public:
+
+    EventFD();
+    virtual ~EventFD();
+
+    EventFD(const EventFD& eventfd) = delete;
+    EventFD& operator=(const EventFD&) = delete;
+
+    /**
+    * @return event's file descriptor.
+    */
+    int getFD() const;
+
+    /**
+     * Send an event of a given value
+     */
+    void send();
+
+    /**
+     * Receives the signal.
+     * Blocks if there is no event.
+     */
+    void receive();
+
+private:
+    int mFD;
+};
+
+} // namespace utils
+
+#endif // COMMON_UTILS_EVENTFD_HPP
diff --git a/common/utils/exception.cpp b/common/utils/exception.cpp
new file mode 100644 (file)
index 0000000..7d2a6ac
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Mateusz Malicki  <m.malicki2@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Mateusz Malicki (m.malicki2@samsung.com)
+ * @brief   Utils base exception helper implementation
+ */
+
+#include "config.hpp"
+
+#include "utils/exception.hpp"
+
+#include <string>
+#include <cstring>
+#include <cerrno>
+
+namespace utils {
+
+const int ERROR_MESSAGE_BUFFER_CAPACITY = 256;
+
+std::string getSystemErrorMessage()
+{
+    return getSystemErrorMessage(errno);
+}
+
+std::string getSystemErrorMessage(int err)
+{
+    char buf[ERROR_MESSAGE_BUFFER_CAPACITY];
+    return strerror_r(err, buf, sizeof(buf));
+}
+
+} // namespace utils
diff --git a/common/utils/exception.hpp b/common/utils/exception.hpp
new file mode 100644 (file)
index 0000000..01bb380
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ * @brief   Exceptions for the server
+ */
+
+
+#ifndef COMMON_UTILS_EXCEPTION_HPP
+#define COMMON_UTILS_EXCEPTION_HPP
+
+#include <stdexcept>
+
+namespace utils {
+
+
+/**
+ * Base class for exceptions in utils
+ */
+struct UtilsException: public std::runtime_error {
+
+    explicit UtilsException(const std::string& error) : std::runtime_error(error) {}
+};
+
+struct EventFDException: public UtilsException {
+
+    explicit EventFDException(const std::string& error) : UtilsException(error) {}
+};
+
+struct ProvisionExistsException: public UtilsException {
+
+    explicit ProvisionExistsException(const std::string& error) : UtilsException(error) {}
+};
+
+/**
+ * Return string describing error number
+ * it is wrapper for strerror_r
+ */
+std::string getSystemErrorMessage();
+std::string getSystemErrorMessage(int err);
+
+} // namespace utils
+
+#endif // COMMON_UTILS_EXCEPTION_HPP
diff --git a/common/utils/execute.cpp b/common/utils/execute.cpp
new file mode 100644 (file)
index 0000000..014ee81
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Execute utils
+ */
+
+#include "config.hpp"
+
+#include "utils/exception.hpp"
+#include "utils/execute.hpp"
+#include "logger/logger.hpp"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+namespace utils {
+
+namespace {
+
+const uid_t UNSPEC_UID = static_cast<uid_t>(-1);
+
+
+__attribute__((unused)) std::ostream& operator<< (std::ostream& out, const char* const* argv)
+{
+    if (*argv) {
+        argv++; //skip
+    }
+    while (*argv) {
+        out << " '" << *argv++ << "'";
+    }
+    return out;
+}
+
+bool isExecutionSuccessful(int status)
+{
+    if (!WIFEXITED(status)) {
+        if (WIFSIGNALED(status)) {
+            LOGE("Child terminated by signal, signal: " << WTERMSIG(status));
+        } else if (WIFSTOPPED(status)) {
+            LOGW("Child was stopped by signal " << WSTOPSIG(status));
+        } else {
+            LOGE("Child exited abnormally, status: " << status);
+        }
+        return false;
+    }
+    if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+        LOGE("Child exit status: " << WEXITSTATUS(status));
+        return false;
+    }
+    return true;
+}
+
+} // namespace
+
+bool executeAndWait(const std::function<void()>& func, int& status)
+{
+    LOGD("Execute child process");
+
+    pid_t pid = ::fork();
+    if (pid == -1) {
+        LOGE("Fork failed: " << getSystemErrorMessage());
+        return false;
+    }
+    if (pid == 0) {
+        func();
+        ::_exit(EXIT_SUCCESS);
+    }
+    return waitPid(pid, status);
+}
+
+bool executeAndWait(const std::function<void()>& func)
+{
+    int status;
+    if (!executeAndWait(func, status)) {
+        return false;
+    }
+    return isExecutionSuccessful(status);
+}
+
+bool executeAndWait(const uid_t uid, const char* fname, const char* const* argv, int& status)
+{
+    LOGD("Execute " << fname << argv);
+
+    pid_t pid = ::fork();
+    if (pid == -1) {
+        LOGE("Fork failed: " << getSystemErrorMessage());
+        return false;
+    }
+    if (pid == 0) {
+        if (uid != UNSPEC_UID && ::setuid(uid) < 0) {
+            LOGW("Failed to become uid(" << uid << "): " << getSystemErrorMessage());
+            ::_exit(EXIT_FAILURE);
+        }
+        ::execv(fname, const_cast<char* const*>(argv));
+        LOGE("execv failed: " << getSystemErrorMessage());
+        ::_exit(EXIT_FAILURE);
+    }
+    return waitPid(pid, status);
+}
+
+bool executeAndWait(const char* fname, const char* const* argv, int& status)
+{
+    return executeAndWait(-1, fname, argv, status);
+}
+
+bool executeAndWait(const char* fname, const char* const* argv)
+{
+    int status;
+    if (!executeAndWait(UNSPEC_UID, fname, argv, status)) {
+        return false;
+    }
+    return isExecutionSuccessful(status);
+}
+
+bool waitPid(pid_t pid, int& status)
+{
+    while (::waitpid(pid, &status, 0) == -1) {
+        if (errno != EINTR) {
+            LOGE("waitpid() failed: " << getSystemErrorMessage());
+            return false;
+        }
+    }
+    return true;
+}
+
+bool executeAndWait(const uid_t uid, const std::vector<std::string>& argv, int& status)
+{
+    std::vector<const char *> args;
+    args.reserve(argv.size() + 1);
+
+    for (const auto& arg : argv) {
+        args.push_back(arg.c_str());
+    }
+    args.push_back(nullptr);
+
+    return executeAndWait(uid, args[0], args.data(), status);
+}
+
+bool executeAndWait(const uid_t uid, const std::vector<std::string>& argv)
+{
+    int status;
+    if (!executeAndWait(uid, argv, status)) {
+        return false;
+    }
+    return isExecutionSuccessful(status);
+}
+
+bool executeAndWait(const std::vector<std::string>& argv)
+{
+    return executeAndWait(UNSPEC_UID, argv);
+}
+
+} // namespace utils
diff --git a/common/utils/execute.hpp b/common/utils/execute.hpp
new file mode 100644 (file)
index 0000000..9db55c2
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Execute utils
+ */
+
+#ifndef COMMON_UTILS_EXECUTE_HPP
+#define COMMON_UTILS_EXECUTE_HPP
+
+#include <sys/types.h>
+#include <vector>
+#include <functional>
+
+namespace utils {
+
+/**
+ * Execute function - deprecated
+ */
+bool executeAndWait(const std::function<void()>& func, int& status);
+
+bool executeAndWait(const std::function<void()>& func);
+
+
+/**
+ * Execute binary
+ */
+///@{
+bool executeAndWait(uid_t uid, const char* fname, const char* const* argv, int& status);
+
+bool executeAndWait(const char* fname, const char* const* argv);
+
+bool executeAndWait(const char* fname, const char* const* argv, int& status);
+
+bool executeAndWait(uid_t uid, const std::vector<std::string>& argv, int& status);
+
+bool executeAndWait(uid_t uid, const std::vector<std::string>& argv);
+
+bool executeAndWait(const std::vector<std::string>& argv);
+///@}
+
+/**
+ * Wait until child processes ends
+ */
+bool waitPid(pid_t pid, int& status);
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_EXECUTE_HPP
diff --git a/common/utils/fd-utils.cpp b/common/utils/fd-utils.cpp
new file mode 100644 (file)
index 0000000..2c37a13
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   File descriptor utility functions
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "config.hpp"
+
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+#include "logger/logger.hpp"
+
+#include <cerrno>
+#include <cstring>
+#include <chrono>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <boost/filesystem.hpp>
+
+namespace fs = boost::filesystem;
+namespace chr = std::chrono;
+
+namespace utils {
+
+// TODO: Add here various fixes from cargo::FDStore
+
+namespace {
+
+void waitForEvent(int fd,
+                  short event,
+                  const chr::high_resolution_clock::time_point deadline)
+{
+    // Wait for the rest of the data
+    struct pollfd fds[1];
+    fds[0].fd = fd;
+    fds[0].events = event;
+
+    for (;;) {
+        chr::milliseconds timeoutMS = chr::duration_cast<chr::milliseconds>(deadline - chr::high_resolution_clock::now());
+        if (timeoutMS.count() < 0) {
+            LOGE("Timeout while waiting for event: " << std::hex << event <<
+                 " on fd: " << std::dec << fd);
+            throw UtilsException("Timeout");
+        }
+
+        int ret = ::poll(fds, 1 /*fds size*/, timeoutMS.count());
+
+        if (ret == -1) {
+            if (errno == EINTR) {
+                continue;
+            }
+            const std::string msg = "Error in poll: " + getSystemErrorMessage();
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+
+        if (ret == 0) {
+            const std::string msg = "Timeout in read";
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+
+        if (fds[0].revents & event) {
+            // Here Comes the Sun
+            break;
+        }
+
+        if (fds[0].revents & POLLHUP) {
+            const std::string msg = "Peer disconnected";
+            LOGW(msg);
+            throw UtilsException(msg);
+        }
+    }
+}
+
+void setFDFlag(const int fd, const int getOp, const int setOp, const int flag, const bool set)
+{
+    int ret = ::fcntl(fd, getOp);
+    if (ret == -1) {
+        std::string msg = "fcntl(): Failed to get FD flags: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    if (set) {
+        ret = ::fcntl(fd, setOp, ret | flag);
+    } else {
+        ret = ::fcntl(fd, setOp, ret & ~flag);
+    }
+
+    if (ret == -1) {
+        std::string msg = "fcntl(): Failed to set FD flag: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+} // namespace
+
+
+int open(const std::string &path, int flags, mode_t mode)
+{
+    assert(!((flags & O_CREAT) == O_CREAT || (flags & O_TMPFILE) == O_TMPFILE) || mode != static_cast<unsigned>(-1));
+
+    int fd;
+
+    for (;;) {
+        fd = ::open(path.c_str(), flags, mode);
+
+        if (-1 == fd) {
+            if (errno == EINTR) {
+                LOGT("open() interrupted by a signal, retrying");
+                continue;
+            }
+            const std::string msg = "open() failed: " + path + ": " + getSystemErrorMessage();
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+        break;
+    }
+
+    return fd;
+}
+
+void close(int fd)
+{
+    if (fd < 0) {
+        return;
+    }
+
+    for (;;) {
+        if (-1 == ::close(fd)) {
+            if (errno == EINTR) {
+                LOGT("close() interrupted by a signal, retrying");
+                continue;
+            }
+            LOGE("Error in close: " << getSystemErrorMessage());
+        }
+        break;
+    }
+}
+
+void shutdown(int fd)
+{
+    if (fd < 0) {
+        return;
+    }
+
+    if (-1 == ::shutdown(fd, SHUT_RDWR)) {
+        std::string msg = "shutdown() failed: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+int ioctl(int fd, unsigned long request, void *argp)
+{
+    int ret = ::ioctl(fd, request, argp);
+    if (ret == -1) {
+        const std::string msg = "ioctl() failed: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+    return ret;
+}
+
+int dup2(int oldFD, int newFD, bool closeOnExec)
+{
+    int flags = 0;
+    if (closeOnExec) {
+        flags |= O_CLOEXEC;
+    }
+    int fd = dup3(oldFD, newFD, flags);
+    if (fd == -1) {
+        const std::string msg = "dup3() failed: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+    return fd;
+}
+
+void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS)
+{
+    chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() +
+            chr::milliseconds(timeoutMS);
+
+    size_t nTotal = 0;
+    for (;;) {
+        int n  = ::write(fd,
+                         reinterpret_cast<const char*>(bufferPtr) + nTotal,
+                         size - nTotal);
+        if (n >= 0) {
+            nTotal += n;
+            if (nTotal == size) {
+                // All data is written, break loop
+                break;
+            }
+        } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+            // Neglected errors
+            LOGD("Retrying write");
+        } else {
+            const std::string msg = "Error during writing: " + getSystemErrorMessage();
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+
+        waitForEvent(fd, POLLOUT, deadline);
+    }
+}
+
+void read(int fd, void* bufferPtr, const size_t size, int timeoutMS)
+{
+    chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() +
+            chr::milliseconds(timeoutMS);
+
+    size_t nTotal = 0;
+    for (;;) {
+        int n  = ::read(fd,
+                        reinterpret_cast<char*>(bufferPtr) + nTotal,
+                        size - nTotal);
+        if (n >= 0) {
+            nTotal += n;
+            if (nTotal == size) {
+                // All data is read, break loop
+                break;
+            }
+            if (n == 0) {
+                const std::string msg = "Peer disconnected";
+                LOGW(msg);
+                throw UtilsException(msg);
+            }
+        } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+            // Neglected errors
+            LOGD("Retrying read");
+        } else {
+            const std::string msg = "Error during reading: " + getSystemErrorMessage();
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+
+        waitForEvent(fd, POLLIN, deadline);
+    }
+}
+
+unsigned int getMaxFDNumber()
+{
+    struct rlimit rlim;
+    if (-1 ==  getrlimit(RLIMIT_NOFILE, &rlim)) {
+        const std::string msg = "Error during getrlimit: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+    return rlim.rlim_cur;
+}
+
+void setMaxFDNumber(unsigned int limit)
+{
+    struct rlimit rlim;
+    rlim.rlim_cur = limit;
+    rlim.rlim_max = limit;
+    if (-1 ==  setrlimit(RLIMIT_NOFILE, &rlim)) {
+        const std::string msg = "Error during setrlimit: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+unsigned int getFDNumber()
+{
+    const std::string path = "/proc/self/fd/";
+    return std::distance(fs::directory_iterator(path),
+                         fs::directory_iterator());
+}
+
+int fdRecv(int socket, const unsigned int timeoutMS)
+{
+    std::chrono::high_resolution_clock::time_point deadline =
+        std::chrono::high_resolution_clock::now() +
+        std::chrono::milliseconds(timeoutMS);
+
+    // Space for the file descriptor
+    union {
+        struct cmsghdr cmh;
+        char   control[CMSG_SPACE(sizeof(int))];
+    } controlUnion;
+
+    // Describe the data that we want to recive
+    controlUnion.cmh.cmsg_len = CMSG_LEN(sizeof(int));
+    controlUnion.cmh.cmsg_level = SOL_SOCKET;
+    controlUnion.cmh.cmsg_type = SCM_RIGHTS;
+
+    // Setup the input buffer
+    // Ensure at least 1 byte is transmited via the socket
+    char buf;
+    struct iovec iov;
+    iov.iov_base = &buf;
+    iov.iov_len = sizeof(char);
+
+    // Set the ancillary data buffer
+    // The socket has to be connected, so we don't need to specify the name
+    struct msghdr msgh;
+    ::memset(&msgh, 0, sizeof(msgh));
+
+    msgh.msg_iov = &iov;
+    msgh.msg_iovlen = 1;
+
+    msgh.msg_control = controlUnion.control;
+    msgh.msg_controllen = sizeof(controlUnion.control);
+
+    // Receive
+    for(;;) {
+        ssize_t ret = ::recvmsg(socket, &msgh, MSG_WAITALL);
+        if (ret < 0) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+                // Neglected errors, retry
+            } else {
+                throw UtilsException("Error during recvmsg: " + getSystemErrorMessage());
+            }
+        } else if (ret == 0) {
+            throw UtilsException("Peer disconnected");
+        } else {
+            // We receive only 1 byte of data. No need to repeat
+            break;
+        }
+
+        waitForEvent(socket, POLLIN, deadline);
+    }
+
+    struct cmsghdr *cmhp;
+    cmhp = CMSG_FIRSTHDR(&msgh);
+    if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(int))) {
+        throw UtilsException("Bad cmsg length");
+    } else if (cmhp->cmsg_level != SOL_SOCKET) {
+        throw UtilsException("cmsg_level != SOL_SOCKET");
+    } else if (cmhp->cmsg_type != SCM_RIGHTS) {
+        throw UtilsException("cmsg_type != SCM_RIGHTS");
+    }
+
+    return *(reinterpret_cast<int*>(CMSG_DATA(cmhp)));
+}
+
+bool fdSend(int socket, int fd, const unsigned int timeoutMS)
+{
+    std::chrono::high_resolution_clock::time_point deadline =
+        std::chrono::high_resolution_clock::now() +
+        std::chrono::milliseconds(timeoutMS);
+
+    // Space for the file descriptor
+    union {
+        struct cmsghdr cmh;
+        char   control[CMSG_SPACE(sizeof(int))];
+    } controlUnion;
+
+    // Ensure at least 1 byte is transmited via the socket
+    struct iovec iov;
+    char buf = '!';
+    iov.iov_base = &buf;
+    iov.iov_len = sizeof(char);
+
+    // Fill the message to send:
+    // The socket has to be connected, so we don't need to specify the name
+    struct msghdr msgh;
+    ::memset(&msgh, 0, sizeof(msgh));
+
+    // Only iovec to transmit one element
+    msgh.msg_iov = &iov;
+    msgh.msg_iovlen = 1;
+
+    // Ancillary data buffer
+    msgh.msg_control = controlUnion.control;
+    msgh.msg_controllen = sizeof(controlUnion.control);
+
+    // Describe the data that we want to send
+    struct cmsghdr *cmhp;
+    cmhp = CMSG_FIRSTHDR(&msgh);
+    cmhp->cmsg_len = CMSG_LEN(sizeof(int));
+    cmhp->cmsg_level = SOL_SOCKET;
+    cmhp->cmsg_type = SCM_RIGHTS;
+    *(reinterpret_cast<int*>(CMSG_DATA(cmhp))) = fd;
+
+    // Send
+    for(;;) {
+        ssize_t ret = ::sendmsg(socket, &msgh, MSG_NOSIGNAL);
+        if (ret < 0) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+                // Neglected errors, retry
+            } else {
+                throw UtilsException("Error during sendmsg: " + getSystemErrorMessage());
+            }
+        } else if (ret == 0) {
+            // Retry the sending
+        } else {
+            // We send only 1 byte of data. No need to repeat
+            break;
+        }
+
+        waitForEvent(socket, POLLOUT, deadline);
+    }
+
+    // TODO: It shouldn't return
+    return true;
+}
+
+void setCloseOnExec(int fd, bool closeOnExec)
+{
+    setFDFlag(fd, F_GETFD, F_SETFD, FD_CLOEXEC, closeOnExec);
+}
+
+void setNonBlocking(int fd, bool nonBlocking)
+{
+    setFDFlag(fd, F_GETFL, F_SETFL, O_NONBLOCK, nonBlocking);
+}
+
+} // namespace utils
+
diff --git a/common/utils/fd-utils.hpp b/common/utils/fd-utils.hpp
new file mode 100644 (file)
index 0000000..60aed45
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   File descriptor utility functions
+ */
+
+#ifndef COMMON_UTILS_FD_HPP
+#define COMMON_UTILS_FD_HPP
+
+#include <string>
+#include <cstddef>
+#include <sys/types.h>
+
+namespace utils {
+
+/**
+ * Open a file.
+ */
+int open(const std::string &path, int flags, mode_t mode = -1);
+
+/**
+ * Close the file descriptor.
+ */
+void close(int fd);
+
+/**
+ * Shut down part of a full-duplex connection
+ */
+void shutdown(int fd);
+
+/**
+ * Operation on a special file
+ */
+int ioctl(int fd, unsigned long request, void *argp);
+
+/**
+ * Duplicate one file desciptor onto another
+ */
+int dup2(int olfFD, int newFD, bool closeOnExec = false);
+
+/**
+ * Write to a file descriptor, throw on error.
+ *
+ * @param fd file descriptor
+ * @param bufferPtr pointer to the data buffer
+ * @param size size of data to write
+ * @param timeoutMS timeout in milliseconds
+ */
+void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS = 5000);
+
+/**
+ * Read from a file descriptor, throw on error.
+ *
+ * @param fd file descriptor
+ * @param bufferPtr pointer to the data buffer
+ * @param size size of the data to read
+ * @param timeoutMS timeout in milliseconds
+ */
+void read(int fd, void* bufferPtr, const size_t size, int timeoutMS = 5000);
+
+/**
+ * @return the max number of file descriptors for this process.
+ */
+unsigned int getMaxFDNumber();
+
+/**
+ * Set the software and hardware limit of file descriptors for this process.
+ *
+ * @param limit limit of file descriptors
+ */
+void setMaxFDNumber(unsigned int limit);
+
+/**
+ * @return number of opened file descriptors by this process
+ */
+unsigned int getFDNumber();
+
+/**
+ * Send Socket via Unix Domain socket
+ */
+bool fdSend(int socket, int fd, const unsigned int timeoutMS = 5000);
+
+/**
+ * Receive fd via Unix Domain socket
+ */
+int fdRecv(int socket, const unsigned int timeoutMS = 5000);
+
+/**
+ * Set or remove CLOEXEC on a file descriptor
+ */
+void setCloseOnExec(int fd, bool closeOnExec);
+
+/**
+ * Set or remove NONBLOCK on a file descriptor
+ */
+void setNonBlocking(int fd, bool nonBlocking);
+
+} // namespace utils
+
+#endif // COMMON_UTILS_FD_HPP
diff --git a/common/utils/file-wait.cpp b/common/utils/file-wait.cpp
new file mode 100644 (file)
index 0000000..222a93c
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Wait for file utility function
+ */
+
+#include "config.hpp"
+
+#include "utils/file-wait.hpp"
+#include "utils/exception.hpp"
+#include "utils/inotify.hpp"
+#include "utils/paths.hpp"
+#include "utils/fs.hpp"
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdexcept>
+#include <chrono>
+#include <thread>
+#include "logger/logger.hpp"
+
+namespace utils {
+
+void waitForFile(const std::string& file, const unsigned int timeoutMs)
+{
+    std::string dir = dirName(file);
+    assertExists(dir, S_IFDIR);
+
+    cargo::ipc::epoll::EventPoll poll;
+    Inotify inotify(poll);
+
+    std::string filename = file.substr(dir.size() + 1);
+    bool isWaiting = true;
+    inotify.setHandler(dir, IN_CREATE | IN_ISDIR, [&isWaiting, &filename](const std::string& name, uint32_t) {
+        if (name == filename) {
+            isWaiting = false;
+
+            // There's a race between inotify event and filesystem update.
+            ::sync();
+        }
+    });
+
+    auto deadline = std::chrono::steady_clock::now() +
+                    std::chrono::milliseconds(timeoutMs);
+
+    while (isWaiting && std::chrono::steady_clock::now() < deadline ) {
+        auto duration = deadline - std::chrono::steady_clock::now();
+        poll.dispatchIteration(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
+    }
+
+    if(isWaiting) {
+        throw UtilsException("No such file: " + file);
+    }
+}
+
+} // namespace utils
diff --git a/common/utils/file-wait.hpp b/common/utils/file-wait.hpp
new file mode 100644 (file)
index 0000000..02b361e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Wait for file utility function
+ */
+
+#ifndef COMMON_UTILS_FILE_WAIT_HPP
+#define COMMON_UTILS_FILE_WAIT_HPP
+
+#include <string>
+
+
+namespace utils {
+
+
+//TODO It is used in unit tests now, but it is unclear
+//     whether the same solution will be used in daemon.
+void waitForFile(const std::string& filename, const unsigned int timeoutMs);
+
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_FILE_WAIT_HPP
diff --git a/common/utils/fs.cpp b/common/utils/fs.cpp
new file mode 100644 (file)
index 0000000..12f4f7f
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ * @brief   File utility functions
+ */
+
+#include "config.hpp"
+
+#include "logger/logger.hpp"
+#include "utils/fs.hpp"
+#include "utils/paths.hpp"
+#include "utils/exception.hpp"
+
+#include <boost/filesystem.hpp>
+#include <dirent.h>
+#include <fstream>
+#include <streambuf>
+#include <cstring>
+#include <cerrno>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <iostream>
+
+
+namespace fs = boost::filesystem;
+
+namespace utils {
+
+std::string readFileStream(const std::string& path)
+{
+    std::ifstream file(path);
+
+    if (!file) {
+        throw UtilsException("Read failed");
+    }
+    // 2 x faster then std::istreambuf_iterator
+    std::stringstream content;
+    content << file.rdbuf();
+    return content.str();
+}
+
+bool readFileStream(const std::string& path, std::string& result)
+{
+    std::ifstream file(path);
+
+    if (!file) {
+        return false;
+    }
+    std::stringstream content;
+    content << file.rdbuf();
+    result = content.str();
+    return true;
+}
+
+std::string readFileContent(const std::string& path)
+{
+    std::string result;
+
+    if (!readFileContent(path, result)) {
+        throw UtilsException("Read failed");
+    }
+    return result;
+}
+
+bool readFileContent(const std::string& path, std::string& result)
+{
+    std::ifstream file(path);
+
+    if (!file) {
+        LOGD(path << ": could not open for reading");
+        return false;
+    }
+
+    file.seekg(0, std::ios::end);
+    std::streampos length = file.tellg();
+    if (length < 0) {
+        LOGD(path << ": tellg failed");
+        return false;
+    }
+    result.resize(static_cast<size_t>(length));
+    file.seekg(0, std::ios::beg);
+
+    file.read(&result[0], length);
+    if (!file) {
+        LOGD(path << ": read error");
+        result.clear();
+        return false;
+    }
+
+    return true;
+}
+
+bool saveFileContent(const std::string& path, const std::string& content)
+{
+    std::ofstream file(path);
+    if (!file) {
+        LOGD(path << ": could not open for writing");
+        return false;
+    }
+    file.write(content.data(), static_cast<std::streamsize>(content.size()));
+    if (!file) {
+        LOGD(path << ": could not write to");
+        return false;
+    }
+    return true;
+}
+
+bool readFirstLineOfFile(const std::string& path, std::string& ret)
+{
+    std::ifstream file(path);
+    if (!file) {
+        LOGD(path << ": could not open for reading");
+        return false;
+    }
+
+    std::getline(file, ret);
+    if (!file) {
+        LOGD(path << ": read error");
+        return false;
+    }
+    return true;
+}
+
+bool removeFile(const std::string& path)
+{
+    if (::remove(path.c_str())) {
+        if (errno != ENOENT) {
+            LOGE(path << ": failed to delete: " << getSystemErrorMessage());
+            return false;
+        }
+    }
+    LOGD(path << ": successfuly removed.");
+
+    return true;
+}
+
+bool exists(const std::string& path, int inodeType)
+{
+    try {
+        assertExists(path, inodeType);
+        return true;
+    } catch(...) {
+        return false;
+    }
+}
+
+void assertExists(const std::string& path, int inodeType)
+{
+    if (path.empty()) {
+        const std::string msg = "Empty path";
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    struct stat s;
+    if (::stat(path.c_str(), &s)) {
+        const std::string msg = "Error in stat() " + path + ": " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    if (inodeType != 0) {
+        if (!(s.st_mode & inodeType)) {
+            const std::string msg = "Not an expected inode type, expected: " + std::to_string(inodeType) +
+                                    ", while actual: " + std::to_string(s.st_mode);
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+
+        if (inodeType == S_IFDIR && ::access(path.c_str(), X_OK) < 0) {
+            const std::string msg = "Not a traversable directory";
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+    }
+}
+
+bool isCharDevice(const std::string& path)
+{
+    return utils::exists(path, S_IFCHR);
+}
+
+bool isRegularFile(const std::string& path)
+{
+    return utils::exists(path, S_IFREG);
+}
+
+void assertIsRegularFile(const std::string& path)
+{
+    assertExists(path, S_IFREG);
+}
+
+bool isDir(const std::string& path)
+{
+    return utils::exists(path, S_IFDIR);
+}
+
+void assertIsDir(const std::string& path)
+{
+    assertExists(path, S_IFDIR);
+}
+
+bool isAbsolute(const std::string& path)
+{
+    return fs::path(path).is_absolute();
+}
+
+void assertIsAbsolute(const std::string& path)
+{
+    if (!isAbsolute(path)) {
+        const std::string msg = "Given path '" + path + "' must be absolute!";
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+
+namespace {
+// NOTE: Should be the same as in systemd/src/core/mount-setup.c
+const std::string RUN_MOUNT_POINT_OPTIONS = "mode=755,smackfstransmute=System::Run";
+const std::string RUN_MOUNT_POINT_OPTIONS_NO_SMACK = "mode=755";
+const unsigned long RUN_MOUNT_POINT_FLAGS = MS_NOSUID | MS_NODEV | MS_STRICTATIME;
+
+bool mountTmpfs(const std::string& path, unsigned long flags, const std::string& options)
+{
+    if (::mount("tmpfs", path.c_str(), "tmpfs", flags, options.c_str()) != 0) {
+        LOGD("Mount failed for '" << path << "', options=" << options << ": " << getSystemErrorMessage());
+        return false;
+    }
+    return true;
+}
+
+} // namespace
+
+bool mountRun(const std::string& path)
+{
+    return utils::mountTmpfs(path, RUN_MOUNT_POINT_FLAGS, RUN_MOUNT_POINT_OPTIONS)
+           || utils::mountTmpfs(path, RUN_MOUNT_POINT_FLAGS, RUN_MOUNT_POINT_OPTIONS_NO_SMACK);
+}
+
+bool mount(const std::string& source,
+           const std::string& target,
+           const std::string& filesystemtype,
+           unsigned long mountflags,
+           const std::string& data)
+{
+    int ret = ::mount(source.c_str(),
+                      target.c_str(),
+                      filesystemtype.c_str(),
+                      mountflags,
+                      data.c_str());
+    if (ret < 0) {
+        LOGE("Mount operation failure: "
+             << "source path: "
+             << source
+             << ", target path: "
+             << target
+             << ", filesystemtype: "
+             << filesystemtype
+             << ", mountflags: "
+             << mountflags
+             << ", data: "
+             << data
+             << ", msg: "
+             << getSystemErrorMessage());
+        return false;
+    }
+    return true;
+}
+
+bool umount(const std::string& path)
+{
+    if (::umount(path.c_str()) != 0) {
+        LOGE("Umount failed for '" << path << "': " << getSystemErrorMessage());
+        return false;
+    }
+    return true;
+}
+
+bool isMountPoint(const std::string& path, bool& result)
+{
+    std::string parentPath = dirName(path);
+    bool newResult;
+    if (!hasSameMountPoint(path, parentPath, newResult)) {
+        LOGE("Failed to check the files' mount points");
+        return false;
+    }
+
+    result = !newResult;
+    return true;
+}
+
+bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool& result)
+{
+    struct stat s1, s2;
+
+    if (::stat(path1.c_str(), &s1)) {
+        LOGD("Failed to get stat of " << path1 << ": " << getSystemErrorMessage());
+        return false;
+    }
+
+    if (::stat(path2.c_str(), &s2)) {
+        LOGD("Failed to get stat of " << path2 << ": " << getSystemErrorMessage());
+        return false;
+    }
+
+    result = (s1.st_dev == s2.st_dev);
+    return true;
+}
+
+bool moveFile(const std::string& src, const std::string& dst)
+{
+    bool bResult;
+
+    boost::system::error_code error;
+
+    // The destination has to be a full path (including a file name)
+    // so it doesn't exist yet, we need to check upper level dir instead.
+    if (!hasSameMountPoint(src, dirName(dst), bResult)) {
+        LOGE("Failed to check the files' mount points");
+        return false;
+    }
+
+    if (bResult) {
+        fs::rename(src, dst, error);
+        if (error) {
+            LOGE("Failed to rename the file: " << error);
+            return false;
+        }
+    } else {
+        fs::copy_file(src, dst, error);
+        if (error) {
+            LOGE("Failed to copy the file: " << error);
+            return false;
+        }
+        fs::remove(src, error);
+        if (error) {
+            LOGE("Failed to remove the file: " << error);
+            fs::remove(dst, error);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+namespace {
+
+bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesystem::path& dst)
+{
+    try {
+        for (fs::directory_iterator file(src);
+                file != fs::directory_iterator();
+                ++file) {
+            fs::path current(file->path());
+            fs::path destination = dst / current.filename();
+
+            boost::system::error_code ec;
+
+            if (!fs::is_symlink(current) && fs::is_directory(current)) {
+                fs::create_directory(destination, ec);
+            } else {
+                fs::copy(current, destination, ec);
+            }
+
+            if (ec.value() != boost::system::errc::success) {
+                LOGW("Failed to copy " << current << ": " << ec.message());
+                continue;
+            }
+
+            if (!fs::is_symlink(current) && fs::is_directory(current)) {
+                if (!copyDirContentsRec(current, destination)) {
+                    return false;
+                }
+
+                // apply permissions coming from source file/directory
+                fs::file_status stat = status(current);
+                fs::permissions(destination, stat.permissions(), ec);
+
+                if (ec.value() != boost::system::errc::success) {
+                    LOGW("Failed to set permissions for " << destination << ": " << ec.message());
+                }
+            }
+
+            // change owner
+            struct stat info;
+            ::stat(current.string().c_str(), &info);
+            if (fs::is_symlink(destination)) {
+                if (::lchown(destination.string().c_str(), info.st_uid, info.st_gid) < 0) {
+                    LOGW("Failed to change owner of symlink " << destination.string() << ": " << getSystemErrorMessage());
+                }
+            } else {
+                if (::chown(destination.string().c_str(), info.st_uid, info.st_gid) < 0) {
+                    LOGW("Failed to change owner of file " << destination.string() << ": " << getSystemErrorMessage());
+                }
+            }
+        }
+    } catch (fs::filesystem_error& e) {
+        LOGW(e.what());
+    }
+
+    return true;
+}
+
+boost::filesystem::perms getPerms(const mode_t& mode)
+{
+    return static_cast<boost::filesystem::perms>(mode);
+}
+
+bool copySmackLabel(const std::string& /* src */, const std::string& /* dst */)
+{
+    //TODO: fill copySmackLabel function
+    return true;
+}
+
+
+} // namespace
+
+bool copyDirContents(const std::string& src, const std::string& dst)
+{
+    return copyDirContentsRec(fs::path(src), fs::path(dst));
+}
+
+bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode)
+{
+    fs::path dirPath(path);
+    boost::system::error_code errorCode;
+    bool runDirCreated = false;
+    if (!fs::exists(dirPath)) {
+        if (!fs::create_directory(dirPath, errorCode)) {
+            LOGE("Failed to create directory '" << path << "': "
+                 << errorCode.message());
+            return false;
+        }
+        runDirCreated = true;
+    } else if (!fs::is_directory(dirPath)) {
+        LOGE("Path '" << path << " already exists");
+        return false;
+    }
+
+    // set permissions
+    fs::permissions(dirPath, mode, errorCode);
+    if (fs::status(dirPath).permissions() != mode) {
+        LOGE("Failed to set permissions to '" << path << "': "
+             << errorCode.message());
+        return false;
+    }
+
+    // set owner
+    if (::chown(path.c_str(), uid, gid) != 0) {
+        int err = errno;
+        // remove the directory only if it hadn't existed before
+        if (runDirCreated) {
+            fs::remove(dirPath);
+        }
+        LOGE("chown() failed for path '" << path << "': " << getSystemErrorMessage(err));
+        return false;
+    }
+
+    return true;
+}
+
+bool createDirs(const std::string& path, mode_t mode)
+{
+    const boost::filesystem::perms perms = getPerms(mode);
+    std::vector<fs::path> dirsCreated;
+    fs::path prefix;
+    const fs::path dirPath = fs::path(path);
+    for (const auto& dirSegment : dirPath) {
+        prefix /= dirSegment;
+        if (!fs::exists(prefix)) {
+            bool created = createDir(prefix.string(), -1, -1, perms);
+            if (created) {
+                dirsCreated.push_back(prefix);
+            } else {
+                LOGE("Failed to create dir");
+                // undo
+                for (auto iter = dirsCreated.rbegin(); iter != dirsCreated.rend(); ++iter) {
+                    boost::system::error_code errorCode;
+                    fs::remove(*iter, errorCode);
+                    if (errorCode) {
+                        LOGE("Error during cleaning: dir: " << *iter
+                             << ", msg: " << errorCode.message());
+                    }
+                }
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool createEmptyDir(const std::string& path)
+{
+    fs::path dirPath(path);
+    boost::system::error_code ec;
+    bool cleanDirCreated = false;
+
+    if (!fs::exists(dirPath)) {
+        if (!fs::create_directory(dirPath, ec)) {
+            LOGE("Failed to create dir. Error: " << ec.message());
+            return false;
+        }
+        cleanDirCreated = true;
+    } else if (!fs::is_directory(dirPath)) {
+        LOGE("Provided path already exists and is not a dir, cannot create.");
+        return false;
+    }
+
+    if (!cleanDirCreated) {
+        // check if directory is empty if it was already created
+        if (!fs::is_empty(dirPath)) {
+            LOGE("Directory has some data inside, cannot be used.");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool createFile(const std::string& path, int flags, mode_t mode)
+{
+    int ret = ::open(path.c_str(), flags, mode);
+    if (ret < 0) {
+        LOGE("Failed to create file: path=host:"
+             << path
+             << ", msg: "
+             << getSystemErrorMessage());
+        return false;
+    }
+    close(ret);
+    return true;
+}
+
+bool createFifo(const std::string& path, mode_t mode)
+{
+    int ret = ::mkfifo(path.c_str(), mode);
+    if (ret < 0) {
+        LOGE("Failed to make fifo: path=host:" << path);
+        return false;
+    }
+    return true;
+}
+
+bool copyFile(const std::string& src, const std::string& dest)
+{
+    boost::system::error_code errorCode;
+    fs::copy_file(src, dest, errorCode);
+    if (errorCode) {
+        LOGE("Failed to copy file: msg: "
+             << errorCode.message()
+             << ", path=host:"
+             << src
+             << ", path=host:"
+             << dest);
+        return false;
+    }
+    bool retSmack = copySmackLabel(src, dest);
+    if (!retSmack) {
+        LOGE("Failed to copy file: msg: (can't copy smacklabel) "
+             << ", path=host:"
+             << src
+             << ", path=host:"
+             << dest);
+        fs::remove(src, errorCode);
+        if (errorCode) {
+            LOGE("Failed to clean after copy failure: path=host:"
+                 << src
+                 << ", msg: "
+                 << errorCode.message());
+        }
+        return false;
+    }
+    return true;
+}
+
+bool createLink(const std::string& src, const std::string& dest)
+{
+    int retLink = ::link(src.c_str(), dest.c_str());
+    if (retLink < 0) {
+        LOGE("Failed to hard link: path=host:"
+             << src
+             << ", path=host:"
+             << dest
+             << ", msg:"
+             << getSystemErrorMessage());
+        return false;
+    }
+    bool retSmack = copySmackLabel(src, dest);
+    if (!retSmack) {
+        LOGE("Failed to copy smack label: path=host:"
+             << src
+             << ", path=host:"
+             << dest);
+        boost::system::error_code ec;
+        fs::remove(dest, ec);
+        if (!ec) {
+            LOGE("Failed to clean after hard link creation failure: path=host:"
+                 << src
+                 << ", to: "
+                 << dest
+                 << ", msg: "
+                 << ec.message());
+        }
+        return false;
+    }
+    return true;
+}
+
+} // namespace utils
diff --git a/common/utils/fs.hpp b/common/utils/fs.hpp
new file mode 100644 (file)
index 0000000..8a532df
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ * @brief   File utility functions declaration
+ */
+
+#ifndef COMMON_UTILS_FS_HPP
+#define COMMON_UTILS_FS_HPP
+
+#include <string>
+#include <sys/types.h>
+#include <vector>
+#include <boost/filesystem.hpp>
+
+
+namespace utils {
+
+/**
+ * Reads the content of file stream (no seek); Throws exception on error
+ */
+std::string readFileStream(const std::string& path);
+
+/**
+ * Reads the content of file stream (no seek)
+ */
+bool readFileStream(const std::string& path, std::string& result);
+
+/**
+ * Reads the content of a file (performs seek); Throws exception on error
+ */
+std::string readFileContent(const std::string& path);
+
+/**
+ * Reads the content of a file
+ */
+bool readFileContent(const std::string& path, std::string& content);
+
+/**
+ * Save the content to the file
+ */
+bool saveFileContent(const std::string& path, const std::string& content);
+
+/**
+ * Read a line from file
+ * Its goal is to read a kernel config files (eg. from /proc, /sys/)
+ */
+bool readFirstLineOfFile(const std::string& path, std::string& ret);
+
+/**
+ * Remove file
+ */
+bool removeFile(const std::string& path);
+
+/**
+ * Checks if a path exists and points to an expected item type.
+ * @return: true if exists and is a directory, false otherwise
+ */
+bool exists(const std::string& path, int inodeType = 0);
+
+/**
+ * Checks if a path exists and points to an expected item type.
+ */
+void assertExists(const std::string& path, int inodeType = 0);
+
+/**
+ * Checks if a char device exists
+ */
+bool isCharDevice(const std::string& path);
+
+/**
+ * Checks if a path exists and points to a directory
+ * @return: true if exists and is a directory, false otherwise
+ */
+bool isDir(const std::string& path);
+
+/**
+ * Checks if a path exists and points to a directory
+ */
+void assertIsDir(const std::string& path);
+
+/**
+ * Checks if a path exists and points to a regular file or link
+ * @return: true if exists and is a regular file, false otherwise
+ */
+bool isRegularFile(const std::string& path);
+
+/**
+ * Checks if a path exists and points to a regular file or link
+ */
+void assertIsRegularFile(const std::string& path);
+
+/**
+ * Checks if path is absolute
+ * @return: true if path is valid absolute path, false otherwise
+ */
+bool isAbsolute(const std::string& path);
+
+/**
+ * Checks if path is absolute
+ */
+void assertIsAbsolute(const std::string& path);
+
+/**
+ * List all (including '.' and '..' entries) dir entries
+ */
+bool listDir(const std::string& path, std::vector<std::string>& files);
+
+/**
+ * Mounts run as a tmpfs on a given path
+ */
+bool mountRun(const std::string& path);
+
+/**
+ * Creates mount point
+ */
+bool mount(const std::string& source,
+           const std::string& target,
+           const std::string& filesystemtype,
+           unsigned long mountflags,
+           const std::string& data);
+
+/**
+ * Umounts a filesystem
+ */
+bool umount(const std::string& path);
+
+/**
+ * Check if given path is a mount point
+ */
+bool isMountPoint(const std::string& path, bool& result);
+
+/**
+ * Checks whether the given paths are under the same mount point
+ */
+bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool& result);
+
+/**
+ * Moves the file either by rename if under the same mount point
+ * or by copy&delete if under a different one.
+ * The destination has to be a full path including file name.
+ */
+bool moveFile(const std::string& src, const std::string& dst);
+
+/**
+ * Recursively copy contents of src dir to dst dir.
+ */
+bool copyDirContents(const std::string& src, const std::string& dst);
+
+/**
+ * Creates a directory with specific UID, GID and permissions set.
+ */
+bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode);
+
+/**
+ * Recursively creates a directory with specific permissions set.
+ */
+bool createDirs(const std::string& path, mode_t mode);
+
+/**
+ * Creates an empty directory, ready to serve as mount point.
+ * Succeeds either if path did not exist and was created successfully, or if already existing dir
+ * under the same path is empty and is not a mount point.
+ */
+bool createEmptyDir(const std::string& path);
+
+/**
+ * Creates an empty file
+ */
+bool createFile(const std::string& path, int flags, mode_t mode);
+
+/**
+ * Creates an FIFO special file
+ */
+bool createFifo(const std::string& path, mode_t mode);
+
+/**
+ * Copy an file
+ */
+bool copyFile(const std::string& src, const std::string& dest);
+
+/**
+ * Create hard link
+ */
+bool createLink(const std::string& src, const std::string& dest);
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_FS_HPP
diff --git a/common/utils/glib-loop.cpp b/common/utils/glib-loop.cpp
new file mode 100644 (file)
index 0000000..7623ede
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   C++ wrapper of glib main loop
+ */
+
+#include "config.hpp"
+
+#include "utils/glib-loop.hpp"
+#include "utils/callback-wrapper.hpp"
+
+#include <atomic>
+#include <cassert>
+#include <glib-object.h>
+
+namespace utils {
+
+namespace {
+std::atomic_bool gLoopPresent(false);
+}
+
+
+ScopedGlibLoop::ScopedGlibLoop()
+    : mLoop(g_main_loop_new(NULL, FALSE), g_main_loop_unref)
+{
+    if (gLoopPresent.exchange(true)) {
+        // only one loop per process
+        assert(0 && "Loop is already running");
+    }
+#if !GLIB_CHECK_VERSION(2,36,0)
+    g_type_init();
+#endif
+    mLoopThread = std::thread(g_main_loop_run, mLoop.get());
+}
+
+ScopedGlibLoop::~ScopedGlibLoop()
+{
+    // ensure loop is running (avoid race condition when stop is called to early)
+    while (!g_main_loop_is_running(mLoop.get())) {
+        std::this_thread::yield();
+    }
+    //stop loop and wait
+    g_main_loop_quit(mLoop.get());
+    mLoopThread.join();
+    gLoopPresent = false;
+}
+
+void Glib::addTimerEvent(const unsigned int intervalMs,
+                         const OnTimerEventCallback& callback,
+                         const CallbackGuard& guard)
+{
+    g_timeout_add_full(G_PRIORITY_DEFAULT,
+                       intervalMs,
+                       &Glib::onTimerEvent,
+                       utils::createCallbackWrapper(callback, guard.spawn()),
+                       &utils::deleteCallbackWrapper<OnTimerEventCallback>);
+}
+
+gboolean Glib::onTimerEvent(gpointer data)
+{
+    const OnTimerEventCallback& callback = getCallbackFromPointer<OnTimerEventCallback>(data);
+    if (callback) {
+        return (gboolean)callback();
+    }
+    return FALSE;
+}
+
+
+} // namespace utils
diff --git a/common/utils/glib-loop.hpp b/common/utils/glib-loop.hpp
new file mode 100644 (file)
index 0000000..0c5a459
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   C++ wrapper of glib main loop
+ */
+
+#ifndef COMMON_UTILS_GLIB_LOOP_HPP
+#define COMMON_UTILS_GLIB_LOOP_HPP
+
+#include "utils/callback-guard.hpp"
+
+#include <thread>
+#include <memory>
+#include <glib.h>
+
+
+namespace utils {
+
+
+/**
+ * Glib loop controller. Loop is running in separate thread.
+ */
+class ScopedGlibLoop {
+public:
+    /**
+     * Starts a loop in separate thread.
+     */
+    ScopedGlibLoop();
+
+    /**
+     * Stops loop and waits for a thread.
+     */
+    ~ScopedGlibLoop();
+
+private:
+    std::unique_ptr<GMainLoop, void(*)(GMainLoop*)> mLoop;
+    std::thread mLoopThread;
+};
+
+/**
+ * Miscellaneous helpers for the Glib library
+ */
+class Glib {
+public:
+    /**
+     * A user provided function that will be called succesively after an interval has passed.
+     *
+     * Return true if the callback is supposed to be called further,
+     *        false if the callback is not to be called anymore and be destroyed.
+     */
+    typedef std::function<bool()> OnTimerEventCallback;
+
+    /**
+     * Adds a timer event to the glib main loop.
+     */
+    static void addTimerEvent(const unsigned int intervalMs,
+                              const OnTimerEventCallback& callback,
+                              const CallbackGuard& guard);
+
+private:
+    static gboolean onTimerEvent(gpointer data);
+};
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_GLIB_LOOP_HPP
diff --git a/common/utils/glib-utils.cpp b/common/utils/glib-utils.cpp
new file mode 100644 (file)
index 0000000..c53ec16
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   C++ wrapper of glib main loop
+ */
+
+#include "config.hpp"
+
+#include "utils/glib-utils.hpp"
+#include "utils/callback-wrapper.hpp"
+
+#include <glib-object.h>
+
+namespace utils {
+
+namespace {
+
+gboolean onIddle(gpointer data)
+{
+    const VoidCallback& callback = getCallbackFromPointer<VoidCallback>(data);
+    callback();
+    return FALSE;
+}
+
+} // namespace
+
+void executeInGlibThread(const VoidCallback& callback, const CallbackGuard& guard)
+{
+    if (!callback) {
+        return;
+    }
+    g_idle_add_full(G_PRIORITY_DEFAULT,
+                    &onIddle,
+                    utils::createCallbackWrapper(callback, guard.spawn()),
+                    &utils::deleteCallbackWrapper<VoidCallback>);
+
+}
+
+
+} // namespace utils
diff --git a/common/utils/glib-utils.hpp b/common/utils/glib-utils.hpp
new file mode 100644 (file)
index 0000000..a818230
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Miscellaneous helpers for the Glib library
+ */
+
+#ifndef COMMON_UTILS_GLIB_UTILS_HPP
+#define COMMON_UTILS_GLIB_UTILS_HPP
+
+#include "utils/callback-guard.hpp"
+
+namespace utils {
+
+typedef std::function<void()> VoidCallback;
+
+/**
+ * Executes a callback in glib thread (adds an iddle event to glib)
+ */
+void executeInGlibThread(const VoidCallback& callback, const CallbackGuard& guard);
+
+
+} // namespace utils
+
+#endif // COMMON_UTILS_GLIB_UTILS_HPP
diff --git a/common/utils/img.cpp b/common/utils/img.cpp
new file mode 100644 (file)
index 0000000..d43805b
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Image utility functions declaration
+ */
+
+#include "config.hpp"
+
+#include "logger/logger.hpp"
+#include "utils/img.hpp"
+#include "utils/fs.hpp"
+#include "utils/paths.hpp"
+#include "utils/exception.hpp"
+
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <linux/loop.h>
+
+namespace utils {
+
+namespace {
+
+const std::string LOOP_DEV_PREFIX = "/dev/loop";
+const std::string LOOP_MOUNT_POINT_OPTIONS = "";
+const std::string LOOP_MOUNT_POINT_TYPE = "ext4";
+const unsigned long LOOP_MOUNT_POINT_FLAGS = MS_RDONLY;
+
+// Writes to ret if loop device (provided in loopdev arg) is free to use.
+// Returns true if check was successful, false if loop device FD was unavailable for some reason.
+bool isLoopDevFree(const std::string& loopdev, bool& ret)
+{
+    // initialize
+    ret = false;
+
+    // open loop device FD
+    int loopFD = ::open(loopdev.c_str(), O_RDWR);
+    if (loopFD < 0) {
+        LOGD("Failed to open loop device descriptor: " << getSystemErrorMessage());
+        return false;
+    }
+
+    // if ioctl with LOOP_GET_STATUS fails, device is not assigned and free to use
+    struct loop_info linfo;
+    if (::ioctl(loopFD, LOOP_GET_STATUS, &linfo)) {
+        ret = true;
+    }
+
+    ::close(loopFD);
+    return true;
+}
+
+bool mountLoop(const std::string& img,
+               const std::string& loopdev,
+               const std::string& path,
+               const std::string& type,
+               unsigned long flags,
+               const std::string& options)
+{
+    // to mount an image, we need to connect image FD with loop device FD
+    // get image file  FD
+    int fileFD = ::open(img.c_str(), O_RDWR);
+    if (fileFD < 0) {
+        LOGD("Failed to open image file descriptor: " << getSystemErrorMessage());
+        return false;
+    }
+
+    // get loop device FD
+    int loopFD = ::open(loopdev.c_str(), O_RDWR);
+    if (loopFD < 0) {
+        LOGD("Failed to open loop device descriptor: " << getSystemErrorMessage());
+        ::close(fileFD);
+        return false;
+    }
+
+    // set loop device
+    if (::ioctl(loopFD, LOOP_SET_FD, fileFD)) {
+        LOGD("Failed to assign loop device to image: " << getSystemErrorMessage());
+        ::close(fileFD);
+        ::close(loopFD);
+        return false;
+    }
+
+    // mount loop device to path
+    if (::mount(loopdev.c_str(), path.c_str(), type.c_str(), flags, options.c_str()) != 0) {
+        LOGD("Mount failed for '" << path << "', options=" << options << ": " << getSystemErrorMessage());
+        ::ioctl(loopFD, LOOP_CLR_FD, 0);
+        ::close(fileFD);
+        ::close(loopFD);
+        return false;
+    }
+
+    ::close(fileFD);
+    ::close(loopFD);
+    return true;
+}
+
+} // namespace
+
+// Finds first available loop device and returns its path through ret.
+// Returns false if an error occurs, or if all available loop devices are taken.
+bool getFreeLoopDevice(std::string& ret)
+{
+    for (unsigned int i = 0; i < 8; ++i) {
+        // build path to loop device
+        const std::string loopdev = LOOP_DEV_PREFIX + std::to_string(i);
+        bool isFree = false;
+
+        // check if it is free
+        if (!isLoopDevFree(loopdev, isFree)) {
+            LOGD("Failed to check status of " << loopdev);
+            return false;
+        }
+
+        // if checked loop device is free, we can exit the function and return it
+        if (isFree) {
+            ret = loopdev;
+            return true;
+        }
+    }
+
+    LOGD("All loop devices are taken.");
+    return false;
+}
+
+bool mountImage(const std::string& image, const std::string& path, const std::string& loopdev)
+{
+    return mountLoop(image, path, loopdev,
+                     LOOP_MOUNT_POINT_TYPE,
+                     LOOP_MOUNT_POINT_FLAGS,
+                     LOOP_MOUNT_POINT_OPTIONS);
+}
+
+bool umountImage(const std::string& path, const std::string& loopdev)
+{
+    if (::umount(path.c_str()) != 0) {
+        LOGD("Umount failed for '" << path << "': " << getSystemErrorMessage());
+        return false;
+    }
+
+    // clear loop device
+    int loopFD = ::open(loopdev.c_str(), O_RDWR);
+    if (loopFD < 0) {
+        LOGD("Failed to open fd for loop device 0");
+        return false;
+    }
+
+    if (::ioctl(loopFD, LOOP_CLR_FD, 0) < 0) {
+        LOGD("Failed to clear loop device.");
+        close(loopFD);
+        return false;
+    }
+
+    close(loopFD);
+    return true;
+}
+
+bool copyImageContents(const std::string& img, const std::string& dst)
+{
+    namespace fs = boost::filesystem;
+    boost::system::error_code ec;
+
+    // make sure that image exists
+    if (!fs::exists(fs::path(img))) {
+        LOGE("Image " << img << " does not exist");
+        return false;
+    }
+
+    const std::string mountPoint = createFilePath(dirName(img), "/mp/");
+    // create a mount point for copied image
+    if (!createEmptyDir(mountPoint)) {
+        LOGE("Cannot create mount point for copied image.");
+        return false;
+    }
+
+    // create dst directory
+    if (!createEmptyDir(dst)) {
+        LOGE("Cannot create directory for data.");
+        return false;
+    }
+
+    // find free loop device for image
+    std::string loopdev;
+    if (!utils::getFreeLoopDevice(loopdev)) {
+        LOGE("Failed to get free loop device.");
+        return false;
+    }
+
+    LOGT("Using " << loopdev << " to mount image");
+    // mount an image
+    if (!utils::mountImage(img, loopdev, mountPoint)) {
+        LOGE("Cannot mount image.");
+        return false;
+    }
+
+    // copy data
+    LOGI("Beginning image copy");
+    if (!utils::copyDirContents(mountPoint, dst)) {
+        LOGE("Failed to copy image.");
+        utils::umountImage(mountPoint, loopdev);
+        LOGD("Removing already copied data");
+        fs::remove_all(fs::path(dst));
+        return false;
+    }
+    LOGI("Finished image copy");
+
+    // umount image
+    if (!utils::umountImage(mountPoint, loopdev)) {
+        LOGE("Failed to umount image");
+        LOGD("Removing copied data");
+        fs::remove_all(fs::path(dst));
+        return false;
+    }
+
+    // remove mount point
+    if (!fs::remove(fs::path(mountPoint), ec)) {
+        LOGW("Failed to remove mount point: " << ec.message());
+    }
+
+    return true;
+}
+
+} // namespace utils
diff --git a/common/utils/img.hpp b/common/utils/img.hpp
new file mode 100644 (file)
index 0000000..de04fdc
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Image utility functions declaration
+ */
+
+#ifndef COMMON_UTILS_IMG_HPP
+#define COMMON_UTILS_IMG_HPP
+
+namespace utils {
+
+/**
+ * Returns string with first free loop device.
+ */
+bool getFreeLoopDevice(std::string& ret);
+
+/**
+ * Mount an ext4 image from file on a given path by using a loop device.
+ */
+bool mountImage(const std::string& image, const std::string& loopdev, const std::string& path);
+
+/**
+ * Umounts previously mounted image.
+ * This call will also free loop device used to mount specified path.
+ */
+bool umountImage(const std::string& path, const std::string& loopdev);
+
+/**
+ * Mounts an image and copies its contents to dst directory.
+ */
+bool copyImageContents(const std::string& img, const std::string& dst);
+
+} // namespace utils
+
+#endif // COMMON_UTILS_IMG_HPP
diff --git a/common/utils/initctl.cpp b/common/utils/initctl.cpp
new file mode 100644 (file)
index 0000000..026ab24
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Api for talking to init via initctl
+ */
+
+#include "config.hpp"
+
+#include "utils/initctl.hpp"
+
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+namespace utils {
+
+namespace {
+    struct InitctlRequest {
+        int magic;
+        int cmd;
+        int runlevel;
+        int sleeptime;
+        char data[368];
+    };
+    const int INITCTL_MAGIC = 0x03091969;
+    const int INITCTL_CMD_RUNLVL = 1;
+
+    bool write(int fd, const void* data, size_t size)
+    {
+        while (size > 0) {
+            ssize_t r = ::write(fd, data, size);
+            if (r < 0) {
+                if (errno == EINTR) {
+                    continue;
+                }
+                return false;
+            }
+            size -= static_cast<size_t>(r);
+            data = reinterpret_cast<const char*>(data) + r;
+        }
+        return true;
+    }
+
+    void close(int fd)
+    {
+        while (::close(fd) == -1 && errno == EINTR) {}
+    }
+}
+
+bool setRunLevel(RunLevel runLevel)
+{
+    int fd = ::open("/dev/initctl", O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
+    if (fd < 0) {
+        return false;
+    }
+
+    InitctlRequest req;
+    memset(&req, 0, sizeof(req));
+    req.magic = INITCTL_MAGIC;
+    req.cmd = INITCTL_CMD_RUNLVL;
+    req.runlevel = '0' + runLevel;
+    req.sleeptime = 0;
+
+    bool ret = write(fd, &req, sizeof(req));
+    close(fd);
+    return ret;
+}
+
+
+} // namespace utils
diff --git a/common/utils/initctl.hpp b/common/utils/initctl.hpp
new file mode 100644 (file)
index 0000000..d81c7bc
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Api for talking to init via initctl
+ */
+
+#ifndef COMMON_UTILS_INITCTL_HPP
+#define COMMON_UTILS_INITCTL_HPP
+
+
+namespace utils {
+
+enum RunLevel : int {
+    RUNLEVEL_POWEROFF = 0,
+    RUNLEVEL_REBOOT = 6
+};
+
+bool setRunLevel(RunLevel runLevel);
+
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_INITCTL_HPP
diff --git a/common/utils/inotify.cpp b/common/utils/inotify.cpp
new file mode 100644 (file)
index 0000000..1c4f84d
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Inotify wrapper
+ */
+
+#include "config.hpp"
+
+#include "utils/inotify.hpp"
+#include "utils/paths.hpp"
+#include "utils/fs.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+
+#include "logger/logger.hpp"
+#include "logger/logger-scope.hpp"
+
+#include <sys/ioctl.h>
+
+#include <functional>
+
+
+namespace utils {
+
+Inotify::Inotify(cargo::ipc::epoll::EventPoll& eventPoll)
+    :mEventPoll(eventPoll)
+{
+    mFD = ::inotify_init1(IN_CLOEXEC);
+    if (mFD == -1) {
+        const std::string msg = "Error in inotify_init1: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    mEventPoll.addFD(mFD, EPOLLIN, std::bind(&Inotify::handleInternal, this));
+}
+
+Inotify::~Inotify()
+{
+    LOGS("~Inotify");
+    {
+        Lock lock(mMutex);
+
+        for(const auto& handler: mHandlers) {
+            if (-1 == ::inotify_rm_watch(mFD, handler.watchID)) {
+                LOGE("Error in inotify_rm_watch: " + getSystemErrorMessage());
+            }
+        }
+    }
+
+    mEventPoll.removeFD(mFD);
+    utils::close(mFD);
+}
+
+int Inotify::getFD() const
+{
+    return mFD;
+}
+
+void Inotify::setHandler(const std::string& path,
+                         const uint32_t eventMask,
+                         const Callback&& callback)
+{
+    LOGT("Added inotify for: " << path);
+    Lock lock(mMutex);
+
+    removeHandlerInternal(path);
+
+    int watchID = ::inotify_add_watch(mFD, path.c_str(), eventMask);
+    if (-1 == watchID) {
+        const std::string msg = "Error in inotify_add_watch: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    mHandlers.push_back({path, watchID, callback});
+}
+
+void Inotify::removeHandlerInternal(const std::string& path)
+{
+    // Find the corresponding handler's data
+    auto it = std::find_if(mHandlers.begin(), mHandlers.end(), [&path](const Handler& h) {
+        return path == h.path;
+    });
+
+    if (it == mHandlers.end()) {
+        return;
+    }
+
+    // Unwatch the path
+    if (-1 == ::inotify_rm_watch(mFD, it->watchID)) {
+        const std::string msg = "Error in inotify_rm_watch: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    mHandlers.erase(it);
+}
+
+void Inotify::removeHandler(const std::string& path)
+{
+    LOGT("Removed inotify for: " << path);
+    Lock lock(mMutex);
+    removeHandlerInternal(path);
+}
+
+void Inotify::handleInternal()
+{
+    Lock lock(mMutex);
+
+    // Get how much data is awaiting
+    unsigned int bufferSize;
+    utils::ioctl(mFD, FIONREAD, &bufferSize);
+
+    // Read all events into a buffer
+    std::vector<char> buffer(bufferSize);
+    utils::read(mFD, buffer.data(), bufferSize);
+
+    // Handle all events
+    unsigned int offset = 0;
+    while (offset < bufferSize) {
+        struct ::inotify_event *event = reinterpret_cast<struct ::inotify_event*>(&buffer[offset]);
+        offset = offset + sizeof(inotify_event) + event->len;
+
+        if(event->mask & IN_IGNORED) {
+            // Watch was removed - ignore
+            continue;
+        }
+
+        auto it = std::find_if(mHandlers.begin(), mHandlers.end(), [event](const Handler& h) {
+            return event->wd == h.watchID;
+        });
+        if (it == mHandlers.end()) {
+            // Meantime the callback was deleted by another callback
+            LOGE("No callback for file: " << event->name);
+            continue;
+        }
+
+        LOGT("Handling inotify: " << event->name);
+        it->call(event->name, event->mask);
+    }
+}
+
+} // namespace utils
diff --git a/common/utils/inotify.hpp b/common/utils/inotify.hpp
new file mode 100644 (file)
index 0000000..bd4370c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Inotify wrapper
+ */
+
+#ifndef COMMON_UTILS_INOTIFY_HPP
+#define COMMON_UTILS_INOTIFY_HPP
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include <sys/inotify.h>
+
+
+namespace utils {
+
+/**
+ * Inotify monitors a directory and when a specified file or folder
+ * is created or deleted it calls a corresponding handler.
+ */
+class Inotify {
+public:
+    typedef std::function<void(const std::string&, const uint32_t)> Callback;
+
+    Inotify(cargo::ipc::epoll::EventPoll& eventPoll);
+    virtual ~Inotify();
+
+    Inotify(const Inotify&) = delete;
+    Inotify& operator=(const Inotify&) = delete;
+
+    /**
+     * Add a callback for a specified path
+     */
+    void setHandler(const std::string& path, const uint32_t eventMask, const Callback&& callback);
+
+    /**
+     * Stop watching the path
+     */
+    void removeHandler(const std::string& path);
+
+    /**
+     * @return inotify file descriptor
+     */
+    int getFD() const;
+
+private:
+    struct Handler {
+        std::string path;
+        int watchID;
+        Callback call;
+    };
+
+    typedef std::lock_guard<std::recursive_mutex> Lock;
+    std::recursive_mutex mMutex;
+
+    int mFD;
+    cargo::ipc::epoll::EventPoll& mEventPoll;
+    std::vector<Handler> mHandlers;
+
+    void handleInternal();
+    void removeHandlerInternal(const std::string& path);
+};
+
+} // namespace utils
+
+#endif // COMMON_UTILS_INOTIFY_HPP
diff --git a/common/utils/latch.cpp b/common/utils/latch.cpp
new file mode 100644 (file)
index 0000000..273d076
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Synchronization latch
+ */
+
+#include "config.hpp"
+
+#include "utils/latch.hpp"
+
+#include <cassert>
+
+
+namespace utils {
+
+
+Latch::Latch()
+    : mCount(0)
+{
+}
+
+void Latch::set()
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    ++mCount;
+    mCondition.notify_one();
+}
+
+void Latch::wait()
+{
+    waitForN(1);
+}
+
+bool Latch::wait(const unsigned int timeoutMs)
+{
+    return waitForN(1, timeoutMs);
+}
+
+void Latch::waitForN(const unsigned int n)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    mCondition.wait(lock, [this, &n] {return mCount >= n;});
+    mCount -= n;
+}
+
+bool Latch::waitForN(const unsigned int n, const unsigned int timeoutMs)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (!mCondition.wait_for(lock, std::chrono::milliseconds(timeoutMs),
+                             [this, &n] {return mCount >= n;})) {
+        return false;
+    }
+    mCount -= n;
+    return true;
+}
+
+
+bool Latch::empty()
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    return mCount == 0;
+}
+
+
+} // namespace utils
diff --git a/common/utils/latch.hpp b/common/utils/latch.hpp
new file mode 100644 (file)
index 0000000..4c46daf
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Synchronization latch
+ */
+
+#ifndef COMMON_UTILS_LATCH_HPP
+#define COMMON_UTILS_LATCH_HPP
+
+#include <mutex>
+#include <condition_variable>
+
+
+namespace utils {
+
+
+/**
+ * A synchronization aid that allows one thread to wait until
+ * an operation being performed in other thread completes.
+ * It has a similar function as std::promise<void> but allows
+ * multiple calls to set.
+ */
+class Latch {
+public:
+    Latch();
+
+    /**
+     * Sets an event occurred.
+     */
+    void set();
+
+    /**
+     * Waits for a single occurrence of event.
+     */
+    void wait();
+
+    /**
+     * Waits for a single occurrence of event with timeout.
+     *
+     * @param timeoutMs  timeout in ms to wait for
+     * @return           false on timeout
+     */
+    bool wait(const unsigned int timeoutMs);
+
+    /**
+     * Waits for @n occurrences of event.
+     *
+     * @param n number of occurrences to wait for
+     */
+    void waitForN(const unsigned int n);
+
+    /**
+     * Waits for @n occurrences of event with timeout.
+     *
+     * @param n          number of occurrences to wait for
+     * @param timeoutMs  timeout in ms to wait for
+     * @return           false on timeout
+     */
+    bool waitForN(const unsigned int n, const unsigned int timeoutMs);
+
+    /**
+     * Check if there are no pending events.
+     */
+    bool empty();
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    unsigned int mCount;
+};
+
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_LATCH_HPP
diff --git a/common/utils/make-clean.hpp b/common/utils/make-clean.hpp
new file mode 100644 (file)
index 0000000..18fe6d7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Mateusz Malicki <m.malicki@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Mateusz Malicki (m.malicki2@samsung.com)
+ * @brief   Function used to initialize C structures
+ */
+
+#ifndef COMMON_UTILS_MAKE_CLEAN_HPP
+#define COMMON_UTILS_MAKE_CLEAN_HPP
+
+#include <algorithm>
+#include <type_traits>
+
+namespace utils {
+
+template<class T>
+void make_clean(T& value)
+{
+    static_assert(std::is_pod<T>::value, "make_clean require trivial and standard-layout");
+    std::fill_n(reinterpret_cast<char*>(&value), sizeof(value), 0);
+}
+
+template<class T>
+T make_clean()
+{
+    T value;
+    make_clean(value);
+    return value;
+}
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_MAKE_CLEAN_HPP
diff --git a/common/utils/paths.hpp b/common/utils/paths.hpp
new file mode 100644 (file)
index 0000000..2757130
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Path utility functions declaration
+ */
+
+#ifndef COMMON_UTILS_PATHS_HPP
+#define COMMON_UTILS_PATHS_HPP
+
+#include <string>
+#include <vector>
+#include <algorithm>
+
+
+namespace utils {
+
+
+template <class ...Paths> std::string createFilePath(const Paths& ... paths)
+{
+    std::vector<std::string> pathVec = {paths...};
+    std::string retPath = "";
+
+    if (pathVec.empty()) {
+        return retPath;
+    }
+
+    for (std::string& p : pathVec) {
+        // Repeat until retPath is not empty
+        if (retPath.empty() || p.empty()) {
+            retPath += p;
+            continue;
+        }
+
+        // We need a slash
+        if (retPath.back() != '/' && p.front() != '/' && p.front() != '.') {
+            retPath += "/" + p;
+            continue;
+        }
+
+        // Too many slashes
+        if (retPath.back() == '/' && p.front() == '/') {
+            retPath += p.substr(1);
+            continue;
+        }
+
+        retPath += p;
+    }
+
+    return retPath;
+}
+
+namespace {
+
+inline void removeDuplicateSlashes(std::string& path)
+{
+    auto it = std::unique(path.begin(), path.end(),
+                          [](char a, char b) {
+                              return (a == '/' && a == b);
+                          });
+    path.erase(it, path.end());
+}
+
+inline void removeTrailingSlash(std::string& path)
+{
+    size_t size = path.size();
+
+    if (size > 1 && path[size - 1] == '/') {
+        path.resize(size - 1);
+    }
+}
+
+} // namespace
+
+/*
+ * Gets the dir name of a file path, analogous to dirname(1)
+ */
+inline std::string dirName(std::string path)
+{
+    removeDuplicateSlashes(path);
+    removeTrailingSlash(path);
+    path.erase(std::find(path.rbegin(), path.rend(), '/').base(), path.end());
+    removeTrailingSlash(path);
+
+    if (path.empty()) {
+        return ".";
+    }
+
+    return path;
+}
+
+/*
+ * Gets absolute path to specified file (if needed)
+ */
+inline std::string getAbsolutePath(const std::string& path, const std::string& base)
+{
+    if (path[0] == '/') {
+        return path;
+    } else {
+        return utils::createFilePath(base, "/", path);
+    }
+}
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_PATHS_HPP
diff --git a/common/utils/same-thread-guard.cpp b/common/utils/same-thread-guard.cpp
new file mode 100644 (file)
index 0000000..6dd5054
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Same thread guard
+ */
+
+#include "config.hpp"
+
+#include "utils/same-thread-guard.hpp"
+
+#ifdef ENABLE_SAME_THREAD_GUARD
+
+#include "logger/logger.hpp"
+#include "logger/formatter.hpp"
+
+namespace utils {
+
+namespace {
+
+typedef decltype(logger::LogFormatter::getCurrentThread()) ThreadId;
+const ThreadId NOT_SET = 0;
+
+ThreadId getCurrentThreadId() {
+    // use the same thread id numbering mechanism as in logger
+    // to allow analyse accesses in log
+    return logger::LogFormatter::getCurrentThread();
+}
+
+} // namespace
+
+SameThreadGuard::SameThreadGuard() : mThreadId(NOT_SET)
+{
+    static_assert(std::is_same<decltype(mThreadId.load()), ThreadId>::value,
+                  "thread id type mismatch");
+}
+
+bool SameThreadGuard::check()
+{
+    const ThreadId thisThreadId = getCurrentThreadId();
+
+    ThreadId saved = NOT_SET;
+    if (!mThreadId.compare_exchange_strong(saved, thisThreadId) && saved != thisThreadId) {
+        LOGE("Detected thread id mismatch; saved: " << saved << "; current: " << thisThreadId);
+        return false;
+    }
+    return true;
+}
+
+void SameThreadGuard::reset()
+{
+    mThreadId.store(NOT_SET);
+}
+
+} // namespace utils
+
+#endif // ENABLE_SAME_THREAD_GUARD
diff --git a/common/utils/same-thread-guard.hpp b/common/utils/same-thread-guard.hpp
new file mode 100644 (file)
index 0000000..ec62b70
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Same thread guard
+ */
+
+#ifndef COMMON_UTILS_SAME_THREAD_GUARD_HPP
+#define COMMON_UTILS_SAME_THREAD_GUARD_HPP
+
+#ifndef NDEBUG
+#define ENABLE_SAME_THREAD_GUARD
+#endif
+
+#ifdef ENABLE_SAME_THREAD_GUARD
+#include <atomic>
+#include <cassert>
+#endif
+
+namespace utils {
+
+/**
+ * Same thread guard.
+ * There are two purposes of this guard:
+ * - reports invalid assumptions about synchronization needs (only in debug builds)
+ * - acts as an annotation in the source code about the thread safety
+ *
+ * Usage example:
+ * ASSERT_SAME_THREAD(workerThreadGuard);
+ */
+class SameThreadGuard {
+public:
+#ifdef ENABLE_SAME_THREAD_GUARD
+#   define ASSERT_SAME_THREAD(g) assert(g.check())
+    SameThreadGuard();
+
+    /**
+     * On the first call it remembers the current thread id.
+     * On the next call it verifies that current thread is the same as before.
+     */
+    bool check();
+
+    /**
+     * Reset thread id
+     */
+    void reset();
+
+private:
+    std::atomic<unsigned int> mThreadId;
+
+#else // ENABLE_SAME_THREAD_GUARD
+#   define ASSERT_SAME_THREAD(g)
+    static bool check() {return true;}
+    static void reset() {}
+#endif // ENABLE_SAME_THREAD_GUARD
+};
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_SAME_THREAD_GUARD_HPP
diff --git a/common/utils/scoped-daemon.cpp b/common/utils/scoped-daemon.cpp
new file mode 100644 (file)
index 0000000..6dbe93f
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Starts external daemon in constructor, stops it in destructor
+ */
+
+#include "config.hpp"
+
+#include "utils/scoped-daemon.hpp"
+#include "utils/execute.hpp"
+
+#include "logger/logger.hpp"
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <stdexcept>
+
+
+namespace utils {
+
+
+/*
+ * Scoped Daemon - sequence diagram.
+ *
+ *
+ *                |(main process)
+ *                |
+ *   constructor  |
+ *   ------------>|_______
+ *                |       |(launcher process)
+ *                |       |_______
+ *                |       |       |(daemon process)
+ *                |       |       |
+ *                |       |       |
+ *   destructor   |       |       |
+ *   ------------>|  sig  |       |
+ *                |------>|  sig  |
+ *                |       |------>|
+ *                |       |_______|
+ *                |_______|
+ *   destructor   |
+ *      ends      |
+ *
+ *
+ * Launcher helper process is used to monitor main process.
+ * When e.g. it crashes or hits an assert then launcher kills daemon and itself.
+ */
+
+namespace {
+
+volatile pid_t daemonPid = -1;// available in launcher process only;
+
+bool startDaemon(const char* path, const char* const argv[])
+{
+    execv(path, const_cast<char* const*>(argv));
+    perror("exec failed");
+    return false;
+}
+
+bool waitForDaemon()
+{
+    int status;
+    return waitPid(daemonPid, status);
+}
+
+void launcherSignalHandler(int sig)
+{
+    // forward to daemon
+    if (kill(daemonPid, sig) == -1) {
+        perror("kill daemon failed");
+    }
+}
+
+void registerLauncherSignalHandler()
+{
+    signal(SIGTERM, launcherSignalHandler);
+}
+
+void registerParentDiedNotification()
+{
+    prctl(PR_SET_PDEATHSIG, SIGTERM);
+}
+
+void cleanupProcess()
+{
+    signal(SIGCHLD, SIG_DFL);
+    signal(SIGINT, SIG_DFL);
+    signal(SIGTERM, SIG_DFL);
+    signal(SIGHUP, SIG_DFL);
+}
+
+bool startByLauncher(const char* path, const char* const argv[])
+{
+    cleanupProcess();
+    daemonPid = fork();
+    if (daemonPid == -1) {
+        perror("fork failed");
+        return false;
+    }
+    if (daemonPid == 0) {
+        if (!startDaemon(path, argv)) {
+            return false;
+        }
+    }
+    registerLauncherSignalHandler();
+    registerParentDiedNotification();
+    return waitForDaemon();
+}
+
+} // namespace
+
+ScopedDaemon::ScopedDaemon()
+    : mPid(-1)
+{
+}
+
+ScopedDaemon::~ScopedDaemon()
+{
+    stop();
+}
+
+void ScopedDaemon::start(const char* path, const char* const argv[], const bool useLauncher)
+{
+    if (mPid != -1) {
+        throw std::runtime_error("already started");
+    }
+    mPid = fork();
+    if (mPid == -1) {
+        throw std::runtime_error("fork failed");
+    }
+    if (mPid == 0) {
+        bool ret;
+        if (useLauncher) {
+            ret = startByLauncher(path, argv);
+        } else {
+            ret = startDaemon(path, argv);
+        }
+        _exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
+    }
+}
+
+void ScopedDaemon::stop()
+{
+    if (mPid == -1) {
+        return;
+    }
+    if (kill(mPid, SIGTERM) == -1) {
+        LOGE("kill failed");
+    }
+    int status;
+    if (!waitPid(mPid, status)) {
+        throw std::runtime_error("waitpid failed");
+    }
+    if (status != EXIT_SUCCESS) {
+        LOGW("process exit with status " << status);
+    }
+    mPid = -1;
+}
+
+
+} // namespace utils
diff --git a/common/utils/scoped-daemon.hpp b/common/utils/scoped-daemon.hpp
new file mode 100644 (file)
index 0000000..d263b4d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Starts external daemon in constructor, stops it in destructor
+ */
+
+#ifndef UNIT_TESTS_UTILS_SCOPED_DAEMON_HPP
+#define UNIT_TESTS_UTILS_SCOPED_DAEMON_HPP
+
+#include <sys/types.h>
+
+
+namespace utils {
+
+
+/**
+ * External daemon launcher helper.
+ */
+class ScopedDaemon {
+public:
+    ScopedDaemon();
+
+    /**
+     * Stops a daemon if it is not stopped already.
+     */
+    ~ScopedDaemon();
+
+    /**
+     * Starts a daemon.
+     * @param path daemon path
+     * @param argv arguments passed to the daemon
+     * @param useLauncher use additional launcher process
+     */
+    void start(const char* path, const char* const argv[], const bool useLauncher = false);
+
+    /**
+     * Stops a daemon by sending SIGTERM and waits for a process.
+     */
+    void stop();
+private:
+    pid_t mPid;
+};
+
+
+} // namespace utils
+
+
+#endif // UNIT_TESTS_UTILS_SCOPED_DAEMON_HPP
diff --git a/common/utils/scoped-dir.cpp b/common/utils/scoped-dir.cpp
new file mode 100644 (file)
index 0000000..ddec9b6
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Create directory in constructor, delete it in destructor
+ */
+
+#include "config.hpp"
+
+#include "utils/scoped-dir.hpp"
+
+#include <boost/filesystem.hpp>
+
+
+namespace utils {
+
+namespace fs = boost::filesystem;
+
+ScopedDir::ScopedDir()
+{
+}
+
+ScopedDir::ScopedDir(const std::string& path)
+{
+    create(path);
+}
+
+ScopedDir::~ScopedDir()
+{
+    remove();
+}
+
+void ScopedDir::create(const std::string& path)
+{
+    remove();
+    if (!path.empty()) {
+        mPath = path;
+        fs::remove_all(path);
+        fs::create_directories(path);
+    }
+}
+
+void ScopedDir::remove()
+{
+    if (!mPath.empty()) {
+        fs::remove_all(mPath);
+        mPath.clear();
+    }
+}
+
+} // namespace utils
diff --git a/common/utils/scoped-dir.hpp b/common/utils/scoped-dir.hpp
new file mode 100644 (file)
index 0000000..625954e
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Create directory in constructor, delete it in destructor
+ */
+
+#ifndef UNIT_TESTS_UTILS_SCOPED_DIR_HPP
+#define UNIT_TESTS_UTILS_SCOPED_DIR_HPP
+
+#include <string>
+
+
+namespace utils {
+
+
+/**
+ * Scoped directory
+ * To be used in tests only
+ */
+class ScopedDir {
+public:
+    ScopedDir();
+    ScopedDir(const std::string& path);
+    ~ScopedDir();
+
+    /**
+     * Creates a dir or if exists ensures it is empty
+     */
+    void create(const std::string& path);
+
+    /**
+     * Deletes this dir with all content
+     */
+    void remove();
+
+private:
+    std::string mPath;
+};
+
+
+} // namespace utils
+
+
+#endif // UNIT_TESTS_UTILS_SCOPED_DIR_HPP
diff --git a/common/utils/scoped-gerror.cpp b/common/utils/scoped-gerror.cpp
new file mode 100644 (file)
index 0000000..9c17d3c
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Implementation of the wrapper for GError
+ */
+
+#include "config.hpp"
+
+#include "utils/scoped-gerror.hpp"
+
+namespace utils {
+
+ScopedGError::ScopedGError()
+    : mError(NULL)
+{
+}
+
+ScopedGError::ScopedGError(ScopedGError&& other)
+    : mError(other.mError)
+{
+    other.mError = NULL;
+}
+
+ScopedGError::~ScopedGError()
+{
+    if (mError) {
+        g_error_free(mError);
+    }
+}
+
+bool ScopedGError::strip()
+{
+    return g_dbus_error_strip_remote_error(mError);
+}
+
+ScopedGError::operator bool () const
+{
+    return mError != nullptr;
+}
+
+GError** ScopedGError::operator& ()
+{
+    return &mError;
+}
+
+const GError* ScopedGError::operator->() const
+{
+    return mError;
+}
+
+std::ostream& operator<<(std::ostream& os, const ScopedGError& e)
+{
+    os << e->message;
+    return os;
+}
+
+} // namespace utils
diff --git a/common/utils/scoped-gerror.hpp b/common/utils/scoped-gerror.hpp
new file mode 100644 (file)
index 0000000..5d3bcc7
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Declaration of the wrapper for GError
+ */
+
+#ifndef COMMON_SCOPED_GERROR_HPP
+#define COMMON_SCOPED_GERROR_HPP
+
+#include <iostream>
+#include <gio/gio.h>
+
+namespace utils {
+
+class ScopedGError {
+public:
+    ScopedGError();
+    ScopedGError(ScopedGError&&);
+    ~ScopedGError();
+
+    ScopedGError(const ScopedGError&) = delete;
+    ScopedGError& operator=(const ScopedGError&) = delete;
+
+    /**
+     * Strip the error
+     */
+    bool strip();
+
+    /**
+     * Is error pointer NULL?
+     */
+    operator bool () const;
+
+    /**
+     * @return pointer to the GError
+     */
+    GError** operator& ();
+
+    /**
+     * @return  the GError
+     */
+    const GError* operator->() const;
+
+    /**
+     * Writes out the error message
+     * @param os the output stream
+     * @param e  error to write out
+     */
+    friend std::ostream& operator<<(std::ostream& os, const ScopedGError& e);
+
+private:
+    GError* mError;
+
+};
+
+} // namespace utils
+
+#endif // COMMON_SCOPED_GERROR_HPP
diff --git a/common/utils/signal.cpp b/common/utils/signal.cpp
new file mode 100644 (file)
index 0000000..7bb3d23
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Signal related functions
+ */
+
+#include "config.hpp"
+
+#include "utils/signal.hpp"
+#include "utils/exception.hpp"
+#include "logger/logger.hpp"
+
+#include <string>
+#include <cerrno>
+#include <cstring>
+#include <csignal>
+
+namespace utils {
+
+namespace {
+
+void setSignalMask(int how, const ::sigset_t& set)
+{
+    int ret = ::pthread_sigmask(how, &set, nullptr /*&oldSet*/);
+    if(ret != 0) {
+        const std::string msg = "Error in pthread_sigmask: " + getSystemErrorMessage(ret);
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+void changeSignal(int how, const int sigNum) {
+    ::sigset_t set;
+    if(-1 == ::sigemptyset(&set)) {
+        const std::string msg = "Error in sigemptyset: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    if(-1 ==::sigaddset(&set, sigNum)) {
+        const std::string msg = "Error in sigaddset: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    setSignalMask(how, set);
+}
+
+}// namespace
+
+::sigset_t getSignalMask()
+{
+    ::sigset_t set;
+    int ret = ::pthread_sigmask(0 /*ignored*/, nullptr /*get the oldset*/, &set);
+    if(ret != 0) {
+        const std::string msg = "Error in pthread_sigmask: " + getSystemErrorMessage(ret);
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+    return set;
+}
+
+bool isSignalPending(const int sigNum)
+{
+    ::sigset_t set;
+    if (::sigpending(&set) == -1) {
+        const std::string msg = "Error in sigpending: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    int ret = ::sigismember(&set, sigNum);
+    if (ret == -1) {
+        const std::string msg = "Error in sigismember: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    return ret;
+}
+
+bool waitForSignal(const int sigNum, int timeoutMs)
+{
+    int timeoutS = timeoutMs / 1000;
+    timeoutMs -= timeoutS * 1000;
+
+    struct ::timespec timeout = {
+        timeoutS,
+        timeoutMs * 1000000
+    };
+
+    ::sigset_t set;
+    if(-1 == ::sigemptyset(&set)) {
+        const std::string msg = "Error in sigemptyset: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    if(-1 ==::sigaddset(&set, sigNum)) {
+        const std::string msg = "Error in sigaddset: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    ::siginfo_t info;
+    if (::sigtimedwait(&set, &info, &timeout) == -1) {
+        if (errno == EAGAIN) {
+            return false;
+        }
+
+        const std::string msg = "Error in sigtimedwait: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    return true;
+}
+
+bool isSignalBlocked(const int sigNum)
+{
+    ::sigset_t set = getSignalMask();
+
+    int ret = ::sigismember(&set, sigNum);
+    if(-1 == ret) {
+        const std::string msg = "Error in sigismember: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    return ret == 1;
+}
+
+void signalBlock(const int sigNum)
+{
+    changeSignal(SIG_BLOCK, sigNum);
+}
+
+void signalBlockAllExcept(const std::initializer_list<int>& signals)
+{
+    ::sigset_t set;
+    if(-1 == ::sigfillset(&set)) {
+        const std::string msg = "Error in sigfillset: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    for(const int s: signals) {
+        if(-1 == ::sigaddset(&set, s)) {
+            const std::string msg = "Error in sigaddset: " + getSystemErrorMessage();
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+    }
+    setSignalMask(SIG_BLOCK, set);
+}
+
+void signalUnblock(const int sigNum)
+{
+    changeSignal(SIG_UNBLOCK, sigNum);
+}
+
+std::vector<std::pair<int, struct ::sigaction>> signalIgnore(const std::initializer_list<int>& signals)
+{
+    struct ::sigaction act;
+    struct ::sigaction old;
+    act.sa_handler = SIG_IGN;
+    std::vector<std::pair<int, struct ::sigaction>> oldAct;
+
+    for(const int s: signals) {
+        if(-1 == ::sigaction(s, &act, &old)) {
+            const std::string msg = "Error in sigaction: " + getSystemErrorMessage();
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+        oldAct.emplace_back(s, old);
+    }
+
+    return oldAct;
+}
+
+struct ::sigaction signalSet(const int sigNum, const struct ::sigaction *sigAct)
+{
+    struct ::sigaction old;
+
+    if(-1 == ::sigaction(sigNum, sigAct, &old)) {
+        const std::string msg = "Error in sigaction: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    return old;
+}
+
+void sendSignal(const pid_t pid, const int sigNum)
+{
+    if (-1 == ::kill(pid, sigNum)) {
+        const std::string msg = "Error during killing pid: " + std::to_string(pid) +
+                                " sigNum: " + std::to_string(sigNum) +
+                                ": "  + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+} // namespace utils
diff --git a/common/utils/signal.hpp b/common/utils/signal.hpp
new file mode 100644 (file)
index 0000000..cb64419
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Signal related functions
+ */
+
+#ifndef COMMON_UTILS_SIGNAL_HPP
+#define COMMON_UTILS_SIGNAL_HPP
+
+#include <initializer_list>
+#include <csignal>
+#include <vector>
+
+
+namespace utils {
+
+
+::sigset_t getSignalMask();
+bool isSignalPending(const int sigNum);
+bool waitForSignal(const int sigNum, int timeoutMs);
+bool isSignalBlocked(const int sigNum);
+void signalBlockAllExcept(const std::initializer_list<int>& signals);
+void signalBlock(const int sigNum);
+void signalUnblock(const int sigNum);
+std::vector<std::pair<int, struct ::sigaction>> signalIgnore(const std::initializer_list<int>& signals);
+struct ::sigaction signalSet(const int sigNum, const struct ::sigaction *sigAct);
+void sendSignal(const pid_t pid, const int sigNum);
+
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_SIGNAL_HPP
diff --git a/common/utils/signalfd.cpp b/common/utils/signalfd.cpp
new file mode 100644 (file)
index 0000000..6e4aa96
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Eventfd wrapper
+ */
+
+#include "config.hpp"
+
+#include "utils/signalfd.hpp"
+#include "utils/signal.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+#include "logger/logger.hpp"
+
+#include <functional>
+
+namespace utils {
+
+SignalFD::SignalFD(cargo::ipc::epoll::EventPoll& eventPoll)
+    :mEventPoll(eventPoll)
+{
+    int error = ::sigemptyset(&mSet);
+    if (error == -1) {
+        const std::string msg = "Error in sigemptyset: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    mFD = ::signalfd(-1, &mSet, SFD_CLOEXEC);
+    if (mFD == -1) {
+        const std::string msg = "Error in signalfd: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    mEventPoll.addFD(mFD, EPOLLIN, std::bind(&SignalFD::handleInternal, this));
+}
+
+SignalFD::~SignalFD()
+{
+    mEventPoll.removeFD(mFD);
+    utils::close(mFD);
+
+    // Unblock the signals that have been blocked previously, but also eat
+    // them if they were pending. It seems that signals are delivered twice,
+    // independently for signalfd and async. If we don't eat them before
+    // unblocking they will be delivered immediately potentially doing harm.
+    for (const int sigNum : mBlockedSignals) {
+        waitForSignal(sigNum, 0);
+
+        // Yes, there is a race here between waitForSignal and signalUnlock, but if
+        // a signal is sent at this point it's not by us, signalFD is inactive. So
+        // if that is the case I'd expect someone to have set some handler already.
+
+        signalUnblock(sigNum);
+    }
+}
+
+int SignalFD::getFD() const
+{
+    return mFD;
+}
+
+void SignalFD::setHandler(const int sigNum, const Callback&& callback)
+{
+    Lock lock(mMutex);
+
+    bool isBlocked = isSignalBlocked(sigNum);
+    if(!isBlocked) {
+        signalBlock(sigNum);
+        mBlockedSignals.push_back(sigNum);
+    }
+
+    int error = ::sigaddset(&mSet, sigNum);
+    if (error == -1) {
+        const std::string msg = getSystemErrorMessage();
+        LOGE("Error in signalfd: " << msg);
+        if(!isBlocked) {
+            signalUnblock(sigNum);
+            mBlockedSignals.pop_back();
+        }
+        throw UtilsException("Error in signalfd: " + msg);
+    }
+
+    error = ::signalfd(mFD, &mSet, SFD_CLOEXEC);
+    if (error != mFD) {
+        const std::string msg = getSystemErrorMessage();
+        LOGE("Error in signalfd: " << msg);
+        if(!isBlocked) {
+            signalUnblock(sigNum);
+            mBlockedSignals.pop_back();
+        }
+        throw UtilsException("Error in signalfd: " + msg);
+    }
+
+    mCallbacks.insert({sigNum, callback});
+}
+
+void SignalFD::handleInternal()
+{
+    struct ::signalfd_siginfo sigInfo;
+    utils::read(mFD, &sigInfo, sizeof(sigInfo));
+
+    LOGT("Got signal: " << sigInfo.ssi_signo);
+
+    {
+        Lock lock(mMutex);
+        auto it = mCallbacks.find(sigInfo.ssi_signo);
+        if (it == mCallbacks.end()) {
+            // Meantime the callback was deleted
+            LOGE("No callback for signal: " << sigInfo.ssi_signo);
+            return;
+        }
+
+        it->second(sigInfo);
+    }
+}
+
+} // namespace utils
diff --git a/common/utils/signalfd.hpp b/common/utils/signalfd.hpp
new file mode 100644 (file)
index 0000000..8936b8d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Signalfd wrapper
+ */
+
+#ifndef COMMON_UTILS_SIGNALFD_HPP
+#define COMMON_UTILS_SIGNALFD_HPP
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+
+#include <csignal>
+#include <sys/signalfd.h>
+
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+#include <memory>
+#include <vector>
+
+namespace utils {
+
+/**
+ * SignalFD takes control over handling signals
+ * sent to the thread.
+ *
+ * It should be the only place where signal masks are modified.
+ */
+class SignalFD {
+public:
+    typedef std::function<void(struct ::signalfd_siginfo&)> Callback;
+
+    SignalFD(cargo::ipc::epoll::EventPoll& eventPoll);
+    virtual ~SignalFD();
+
+    SignalFD(const SignalFD& signalfd) = delete;
+    SignalFD& operator=(const SignalFD&) = delete;
+
+    /**
+     * Add a callback for a specified signal.
+     * Blocks the async signal handler if it's not already blocked.
+     *
+     * @param sigNum number of the signal
+     * @param callback handler callback
+     */
+    void setHandler(const int sigNum, const Callback&& callback);
+
+    /**
+     * @return signal file descriptor
+     */
+    int getFD() const;
+
+private:
+    typedef std::unique_lock<std::mutex> Lock;
+
+    int mFD;
+    ::sigset_t mSet;
+    std::mutex mMutex;
+    cargo::ipc::epoll::EventPoll& mEventPoll;
+    std::unordered_map<int, Callback> mCallbacks;
+    std::vector<int> mBlockedSignals;
+
+    void handleInternal();
+};
+
+} // namespace utils
+
+#endif // COMMON_UTILS_SIGNALFD_HPP
diff --git a/common/utils/spin-wait-for.hpp b/common/utils/spin-wait-for.hpp
new file mode 100644 (file)
index 0000000..94c9038
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Simple spin lock implementation
+ */
+
+#include <chrono>
+#include <thread>
+
+namespace utils {
+
+template<class Predicate>
+bool spinWaitFor(int timeoutMs, Predicate pred)
+{
+    auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
+    while (!pred()) {
+        if (std::chrono::steady_clock::now() >= until) {
+            return false;
+        }
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    }
+    return true;
+}
+
+} // namespace utils
\ No newline at end of file
diff --git a/common/utils/text.cpp b/common/utils/text.cpp
new file mode 100644 (file)
index 0000000..f0d7af5
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Krzysztof Dynowski (k.dynowski@samsumg.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief   Text related utility
+ */
+
+#include "config.hpp"
+
+#include "utils/text.hpp"
+
+namespace utils {
+namespace {
+const char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                       '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+}
+
+std::string toHexString(const void *data, unsigned len)
+{
+    const unsigned char *d = static_cast<const unsigned char *>(data);
+    std::string s(len * 2, ' ');
+    for (unsigned i = 0; i < len; ++i) {
+        s[2 * i]     = hexmap[(d[i] >> 4) & 0x0F];
+        s[2 * i + 1] = hexmap[d[i] & 0x0F];
+    }
+    return s;
+}
+
+std::vector<std::string> split(const std::string& str, const std::string& delim)
+{
+    std::vector<std::string> tokens;
+    if (str.empty()) {
+        return tokens;
+    }
+
+    for (std::string::size_type startPos = 0; ; ) {
+        std::string::size_type endPos = str.find_first_of(delim, startPos);
+
+        if (endPos == std::string::npos) {
+            tokens.push_back(str.substr(startPos, endPos));
+            break;
+        }
+        tokens.push_back(str.substr(startPos, endPos - startPos));
+
+        startPos = endPos + 1;
+    }
+    return tokens;
+}
+
+} // namespace utils
diff --git a/common/utils/text.hpp b/common/utils/text.hpp
new file mode 100644 (file)
index 0000000..a6219d4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Krzysztof Dynowski (k.dynowski@samsumg.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief   Text related utils
+ */
+
+#ifndef COMMON_UTILS_TEXT_HPP
+#define COMMON_UTILS_TEXT_HPP
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+namespace utils {
+
+inline bool beginsWith(std::string const &value, std::string const &part)
+{
+     if (part.size() > value.size()) {
+         return false;
+     }
+     return std::equal(part.begin(), part.end(), value.begin());
+}
+
+inline bool endsWith(std::string const &value, std::string const &part)
+{
+     if (part.size() > value.size()) {
+         return false;
+     }
+     return std::equal(part.rbegin(), part.rend(), value.rbegin());
+}
+
+/**
+ * Convert binary bytes array to hex string representation
+ */
+std::string toHexString(const void *data, unsigned len);
+
+template<typename T>
+std::string join(const std::vector<T>& vec, const char *delim)
+{
+    std::stringstream res;
+    for (const auto& s : vec) {
+        if (res.tellp()>0) {
+            res << delim;
+        }
+        res << s;
+    }
+    return res.str();
+}
+
+std::vector<std::string> split(const std::string& str, const std::string& delim);
+
+} // namespace utils
+
+#endif // COMMON_UTILS_TEXT_HPP
diff --git a/common/utils/typeinfo.cpp b/common/utils/typeinfo.cpp
new file mode 100644 (file)
index 0000000..a2f7426
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak (j.olszak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Synchronization latch
+ */
+
+#include "config.hpp"
+
+#include "utils/typeinfo.hpp"
+
+#include <string>
+#include <cxxabi.h>
+#include <stdexcept>
+
+namespace utils {
+
+std::string getTypeName(const std::type_info& ti)
+{
+    int status;
+    char* demangled = abi::__cxa_demangle(ti.name() , 0, 0, &status);
+    if (status) {
+        std::string message = "abi::__cxa_demangle failed with ret code: " + std::to_string(status);
+        throw std::runtime_error(message);
+    }
+
+    std::string ret(demangled);
+    free(demangled);
+    return ret;
+}
+
+} // namespace utils
diff --git a/common/utils/typeinfo.hpp b/common/utils/typeinfo.hpp
new file mode 100644 (file)
index 0000000..f1c83a0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak (j.olszak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Synchronization latch
+ */
+
+#ifndef COMMON_TYPE_INFO_HPP
+#define COMMON_TYPE_INFO_HPP
+
+#include <string>
+#include <typeinfo>
+
+namespace utils {
+
+std::string getTypeName(const std::type_info& ti);
+
+template<class T> std::string getTypeName(const T& t)
+{
+    return getTypeName(typeid(t));
+}
+
+} // namespace utils
+
+
+#endif // COMMON_TYPE_INFO_HPP
diff --git a/common/utils/value-latch.hpp b/common/utils/value-latch.hpp
new file mode 100644 (file)
index 0000000..74fc738
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Definition of ValueLatch template, used to wait for variable to be set.
+ */
+
+#ifndef COMMON_UTILS_VALUE_LATCH_H
+#define COMMON_UTILS_VALUE_LATCH_H
+
+#include "utils/exception.hpp"
+
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+
+namespace utils {
+
+template <typename T>
+class ValueLatch {
+public:
+    /**
+     * Assigns value to kept variable and sets Latch.
+     *
+     * @param value Value to set.
+     */
+    void set(const T& value);
+
+    /**
+     * Assigns value to kept variable and sets Latch.
+     *
+     * @param value Value to set.
+     */
+    void set(T&& value);
+
+    /**
+     * Waits until set() is called, then set value is moved to caller.
+     *
+     * @return Value provided by set().
+     */
+    T get();
+
+    /**
+     * Waits until set() is called, or until timeout occurs. Then, set value is moved to caller.
+     *
+     * @param timeoutMs Maximum time to wait for value to be set.
+     *
+     * @return Value provided by set().
+     */
+    T get(const unsigned int timeoutMs);
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    std::unique_ptr<T> mValue;
+};
+
+template <typename T>
+void ValueLatch<T>::set(const T& value)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mValue) {
+        throw UtilsException("Cannot set value multiple times");
+    }
+    mValue.reset(new T(value));
+    mCondition.notify_one();
+}
+
+template <typename T>
+void ValueLatch<T>::set(T&& value)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mValue) {
+        throw UtilsException("Cannot set value multiple times");
+    }
+    mValue.reset(new T(std::move(value)));
+    mCondition.notify_one();
+}
+
+template <typename T>
+T ValueLatch<T>::get()
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    mCondition.wait(lock, [this]() {
+        return (bool)mValue;
+    });
+    std::unique_ptr<T> retValue(std::move(mValue));
+    return T(std::move(*retValue));
+}
+
+template <typename T>
+T ValueLatch<T>::get(const unsigned int timeoutMs)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mCondition.wait_for(lock, std::chrono::milliseconds(timeoutMs), [this]() {
+                                return (bool)mValue;
+                            }) ) {
+        std::unique_ptr<T> retValue(std::move(mValue));
+        return T(std::move(*retValue));
+    } else {
+        throw UtilsException("Timeout occured");
+    }
+}
+
+} // namespace utils
+
+#endif // COMMON_UTILS_VALUE_LATCH_H
diff --git a/common/utils/vt.cpp b/common/utils/vt.cpp
new file mode 100644 (file)
index 0000000..c5b726c
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   VT-related utility functions
+ */
+
+#include "config.hpp"
+
+#include "utils/vt.hpp"
+#include "logger/logger.hpp"
+#include "utils/exception.hpp"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+namespace {
+
+const std::string TTY_DEV = "/dev/tty0";
+
+} // namespace
+
+namespace utils {
+
+bool activateVT(const int& vt)
+{
+    int consoleFD = ::open(TTY_DEV.c_str(), O_WRONLY);
+    if (consoleFD < 0) {
+        LOGE("console open failed: " << errno << " (" << getSystemErrorMessage() << ")");
+        return false;
+    }
+
+    struct vt_stat vtstat;
+    vtstat.v_active = 0;
+    if (::ioctl(consoleFD, VT_GETSTATE, &vtstat)) {
+        LOGE("Failed to get vt state: " << errno << " (" << getSystemErrorMessage() << ")");
+        ::close(consoleFD);
+        return false;
+    }
+
+    if (vtstat.v_active == vt) {
+        LOGW("vt" << vt << " is already active.");
+        ::close(consoleFD);
+        return true;
+    }
+
+    // activate vt
+    if (::ioctl(consoleFD, VT_ACTIVATE, vt)) {
+        LOGE("Failed to activate vt" << vt << ": " << errno << " (" << getSystemErrorMessage() << ")");
+        ::close(consoleFD);
+        return false;
+    }
+
+    // wait until activation is finished
+    if (::ioctl(consoleFD, VT_WAITACTIVE, vt)) {
+        LOGE("Failed to wait for vt" << vt << " activation: " << errno << " (" << getSystemErrorMessage() << ")");
+        ::close(consoleFD);
+        return false;
+    }
+
+    ::close(consoleFD);
+    return true;
+}
+
+} // namespace utils
diff --git a/common/utils/vt.hpp b/common/utils/vt.hpp
new file mode 100644 (file)
index 0000000..a28261c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   VT-related utility functions
+ */
+
+#ifndef COMMON_UTILS_VT_HPP
+#define COMMON_UTILS_VT_HPP
+
+namespace utils {
+
+bool activateVT(const int& vt);
+
+} // namespace utils
+
+#endif // COMMON_UTILS_VT_HPP
diff --git a/common/utils/worker.cpp b/common/utils/worker.cpp
new file mode 100644 (file)
index 0000000..f49feaa
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   A worker thread that executes tasks
+ */
+
+#include "config.hpp"
+
+#include "utils/worker.hpp"
+#include "utils/latch.hpp"
+#include "utils/counting-map.hpp"
+#include "logger/logger.hpp"
+
+#include <atomic>
+#include <deque>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <cassert>
+
+
+namespace utils {
+
+
+class Worker::WorkerQueue {
+public:
+    WorkerQueue()
+        : mLastGroupID(0), mEnding(false)
+    {
+        LOGT("Worker queue created");
+    }
+
+    ~WorkerQueue()
+    {
+        {
+            Lock lock(mMutex);
+            assert(mTaskQueue.empty());
+            assert(mGroupCounter.empty());
+            mEnding = true;
+        }
+        if (mThread.joinable()) {
+            mAddedCondition.notify_all();
+            mThread.join();
+        }
+        LOGT("Worker queue destroyed");
+    }
+
+    GroupID getNextGroupID()
+    {
+        return ++mLastGroupID;
+    }
+
+    void addTask(const Worker::Task& task, GroupID groupID, Latch* latch)
+    {
+        assert(task);
+
+        Lock lock(mMutex);
+        LOGT("Adding task to subgroup " << groupID);
+        mTaskQueue.push_back(TaskInfo{task, groupID, latch});
+        mGroupCounter.increment(groupID);
+        mAddedCondition.notify_one();
+        if (!mThread.joinable()) {
+            mThread = std::thread(&WorkerQueue::workerProc, this);
+        }
+    }
+
+    void waitForGroupEmpty(GroupID groupID)
+    {
+        Lock lock(mMutex);
+        size_t count = mGroupCounter.get(groupID);
+        if (count > 0) {
+            LOGD("Waiting for " << count << " task in group " << groupID);
+        }
+        mEmptyGroupCondition.wait(lock, [this, groupID] {
+            return mGroupCounter.get(groupID) == 0;
+        });
+    }
+private:
+    typedef std::unique_lock<std::mutex> Lock;
+
+    struct TaskInfo {
+        Worker::Task task;
+        GroupID groupID;
+        Latch *latch;
+    };
+
+    std::atomic<GroupID> mLastGroupID;
+    std::condition_variable mAddedCondition;
+    std::condition_variable mEmptyGroupCondition;
+    std::thread mThread;
+
+    std::mutex mMutex; // protects below member variables:
+    bool mEnding;
+    std::deque<TaskInfo> mTaskQueue;
+    CountingMap<GroupID> mGroupCounter;
+
+    void workerProc()
+    {
+        LOGT("Worker thread started");
+        for (;;) {
+            // wait for a task
+            GroupID groupID;
+            {
+                Lock lock(mMutex);
+                mAddedCondition.wait(lock, [this] {
+                    return !mTaskQueue.empty() || mEnding;
+                });
+                if (mTaskQueue.empty()) {
+                    break;
+                }
+                TaskInfo taskInfo = std::move(mTaskQueue.front());
+                mTaskQueue.pop_front();
+
+                lock.unlock();
+
+                // execute
+                execute(taskInfo);
+                groupID = taskInfo.groupID;
+            }
+            // remove from queue
+            {
+                Lock lock(mMutex);
+                if (mGroupCounter.decrement(groupID) == 0) {
+                    mEmptyGroupCondition.notify_all();
+                }
+            }
+        }
+        LOGT("Worker thread exited");
+    }
+
+    static void execute(const TaskInfo& taskInfo)
+    {
+        try {
+            LOGT("Executing task from subgroup " << taskInfo.groupID);
+            taskInfo.task();
+        } catch (const std::exception& e) {
+            LOGE("Unexpected exception while executing task: " << e.what());
+        }
+        if (taskInfo.latch)
+            taskInfo.latch->set();
+    }
+};
+
+
+Worker::Pointer Worker::create()
+{
+    return Pointer(new Worker(std::make_shared<WorkerQueue>()));
+}
+
+Worker::Worker(const std::shared_ptr<WorkerQueue>& workerQueue)
+    : mWorkerQueue(workerQueue), mGroupID(workerQueue->getNextGroupID())
+{
+}
+
+Worker::~Worker()
+{
+    mWorkerQueue->waitForGroupEmpty(mGroupID);
+}
+
+Worker::Pointer Worker::createSubWorker()
+{
+    return Pointer(new Worker(mWorkerQueue));
+}
+
+void Worker::addTask(const Task& task)
+{
+    mWorkerQueue->addTask(task, mGroupID, NULL);
+}
+
+void Worker::addTaskAndWait(const Task& task)
+{
+    Latch latch;
+
+    mWorkerQueue->addTask(task, mGroupID, &latch);
+    latch.wait();
+}
+
+} // namespace utils
diff --git a/common/utils/worker.hpp b/common/utils/worker.hpp
new file mode 100644 (file)
index 0000000..4733d2d
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   A worker thread that executes tasks
+ */
+
+#ifndef COMMON_UTILS_WORKER_HPP
+#define COMMON_UTILS_WORKER_HPP
+
+#include <functional>
+#include <memory>
+
+namespace utils {
+
+/**
+ * A queue with tasks executed in a dedicated thread.
+ * Current implementation creates a thread on the first use.
+ */
+class Worker {
+public:
+    typedef std::shared_ptr<Worker> Pointer;
+    typedef std::function<void()> Task;
+
+    ~Worker();
+
+    /**
+     * Creates a worker with its own thread
+     */
+    static Pointer create();
+
+    /**
+     * Creates a worker that share a thread with its parent
+     */
+    Pointer createSubWorker();
+
+    /**
+     * Adds a task to the queue.
+     */
+    void addTask(const Task& task);
+    void addTaskAndWait(const Task& task);
+
+private:
+    typedef unsigned int GroupID;
+    class WorkerQueue;
+
+    const std::shared_ptr<WorkerQueue> mWorkerQueue;
+    const GroupID mGroupID;
+
+    Worker(const std::shared_ptr<WorkerQueue>& workerQueue);
+};
+
+} // namespace utils
+
+
+#endif // COMMON_UTILS_WORKER_HPP
diff --git a/doc/cargo_usage.md b/doc/cargo_usage.md
new file mode 100644 (file)
index 0000000..9dd5fa6
--- /dev/null
@@ -0,0 +1,70 @@
+cargo libraries usage example {#mainpage}
+=============================
+
+@ingroup libcargo
+
+@code
+#include "cargo/fields.hpp"
+#include "cargo-gvariant/cargo-gvariant.hpp"
+#include "cargo-json/cargo-json.hpp"
+#include "cargo-sqlite/cargo-sqlite.hpp"
+#include "cargo-sqlite-json/cargo-sqlite-json.hpp"
+#include "cargo-fd/cargo-fd.hpp"
+#include <iostream>
+#include <cstdio>
+
+struct Foo
+{
+    std::string bar = "plain-text";
+    std::vector<int> tab = std::vector<int>{1, 2, 4, 8};
+    double number = 3.14;
+
+    CARGO_REGISTER
+    (
+        bar,
+        tab,
+        number
+    )
+};
+
+int main()
+{
+    Foo foo;
+
+    const std::string jsonString = cargo::saveToJsonString(foo);
+    cargo::loadFromJsonString(jsonString, foo);
+
+    const GVariant* gVariantPointer = cargo::saveToGVariant(foo);
+    cargo::loadFromGVariant(gVariantPointer, foo);
+    g_variant_unref(gVariantPointer);
+
+    constexpr std::string jsonFile = "foo.json";
+    cargo::saveToJsonFile(jsonFile, foo);
+    cargo::loadFromJsonFile(jsonFile, foo);
+
+    constexpr std::string kvDBPath = "kvdb";
+    constexpr std::string key = "foo";
+    cargo::saveToKVStore(kvDBPath, foo, key);
+    cargo::loadFromKVStore(kvDBPath, foo, key);
+
+    cargo::loadFromKVStoreWithJson(kvDBPath, jsonString, foo, key);
+    cargo::loadFromKVStoreWithJsonFile(kvDBPath, jsonFile, foo, key);
+
+    FILE* file = fopen("blob", "wb");
+    if (!file)
+    {
+        return EXIT_FAILURE;
+    }
+    const int fd = ::fileno(file);
+    cargo::saveToFD(fd, foo);
+    ::fclose(file);
+    file = ::fopen("blob", "rb");
+    if(!file) {
+        return EXIT_FAILURE;
+    }
+    cargo::loadFromFD(fd, foo);
+    ::fclose(file);
+
+    return 0;
+}
+@endcode
diff --git a/doc/custom.css b/doc/custom.css
new file mode 100644 (file)
index 0000000..726b998
--- /dev/null
@@ -0,0 +1,1314 @@
+/* The standard CSS for doxygen 1.8.6 */
+
+body, table, div, p, dl {
+    font: 400 16px/24px Roboto,sans-serif;
+}
+
+/* @group Heading Levels */
+
+h1.groupheader {
+    font-size: 150%;
+}
+
+.title {
+    font: 400 14px/28px Roboto,sans-serif;
+    font-size: 150%;
+    font-weight: bold;
+    margin: 10px 2px;
+}
+
+h3.groupheader {
+    font-size: 100%;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    -webkit-transition: none;
+    -moz-transition: none;
+    -ms-transition: none;
+    -o-transition: none;
+    transition: none;
+    margin-right: 0px;
+}
+
+h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
+    text-shadow: 0 0 15px cyan;
+}
+
+dt {
+    font-weight: bold;
+}
+
+div.multicol {
+    -moz-column-gap: 1em;
+    -webkit-column-gap: 1em;
+    -moz-column-count: 3;
+    -webkit-column-count: 3;
+}
+
+p.startli, p.startdd {
+    margin-top: 2px;
+}
+
+p.starttd {
+    margin-top: 0px;
+}
+
+p.endli {
+    margin-bottom: 0px;
+}
+
+p.enddd {
+    margin-bottom: 4px;
+}
+
+p.endtd {
+    margin-bottom: 2px;
+}
+
+/* @end */
+
+caption {
+    font-weight: bold;
+}
+
+span.legend {
+    font-size: 70%;
+    text-align: center;
+}
+
+h3.version {
+    font-size: 90%;
+    text-align: center;
+}
+
+div.qindex, div.navtab{
+    background-color: #EBEFF6;
+    border: 1px solid #A3B4D7;
+    text-align: center;
+}
+
+div.qindex, div.navpath {
+    width: 100%;
+    line-height: 140%;
+}
+
+div.navtab {
+    margin-right: 15px;
+}
+
+/* @group Link Styling */
+
+a {
+    color: #2f5399;
+    font-weight: normal;
+    text-decoration: none;
+}
+
+.contents a:visited {
+    color: #4665A2;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+a.qindex {
+    font-weight: bold;
+}
+
+a.qindexHL {
+    font-weight: bold;
+    background-color: #9CAFD4;
+    color: #ffffff;
+    border: 1px double #869DCA;
+}
+
+.contents a.qindexHL:visited {
+    color: #ffffff;
+}
+
+a.el {
+    font-weight: bold;
+}
+
+a.elRef {
+}
+
+a.code, a.code:visited, a.line, a.line:visited {
+    color: #4665A2;
+}
+
+a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited {
+    color: #4665A2;
+}
+
+/* @end */
+
+dl.el {
+    margin-left: -1cm;
+}
+
+pre.fragment {
+    border: 1px solid #C4CFE5;
+    background-color: #FBFCFD;
+    padding: 4px 6px;
+    margin: 4px 8px 4px 2px;
+    overflow: auto;
+    word-wrap: break-word;
+    font-size:  9pt;
+    line-height: 125%;
+    font-family: monospace, fixed;
+    font-size: 105%;
+}
+
+div.fragment {
+    padding: 4px 6px;
+    margin: 4px 8px 4px 2px;
+    background-color: #FBFCFD;
+    border: 1px solid #C4CFE5;
+}
+
+div.line {
+    font-family: monospace, fixed;
+    font-size: 13px;
+    min-height: 13px;
+    line-height: 1.0;
+    text-wrap: unrestricted;
+    white-space: -moz-pre-wrap; /* Moz */
+    white-space: -pre-wrap;     /* Opera 4-6 */
+    white-space: -o-pre-wrap;   /* Opera 7 */
+    white-space: pre-wrap;      /* CSS3  */
+    word-wrap: break-word;      /* IE 5.5+ */
+    text-indent: -53px;
+    padding-left: 53px;
+    padding-bottom: 0px;
+    margin: 0px;
+    -webkit-transition-property: background-color, box-shadow;
+    -webkit-transition-duration: 0.5s;
+    -moz-transition-property: background-color, box-shadow;
+    -moz-transition-duration: 0.5s;
+    -ms-transition-property: background-color, box-shadow;
+    -ms-transition-duration: 0.5s;
+    -o-transition-property: background-color, box-shadow;
+    -o-transition-duration: 0.5s;
+    transition-property: background-color, box-shadow;
+    transition-duration: 0.5s;
+}
+
+div.line.glow {
+    background-color: cyan;
+    box-shadow: 0 0 10px cyan;
+}
+
+
+span.lineno {
+    padding-right: 4px;
+    text-align: right;
+    border-right: 2px solid #0F0;
+    background-color: #E8E8E8;
+    white-space: pre;
+}
+span.lineno a {
+    background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+    background-color: #C8C8C8;
+}
+
+div.ah {
+    background-color: black;
+    font-weight: bold;
+    color: #ffffff;
+    margin-bottom: 3px;
+    margin-top: 3px;
+    padding: 0.2em;
+    border: solid thin #333;
+    border-radius: 0.5em;
+    -webkit-border-radius: .5em;
+    -moz-border-radius: .5em;
+    box-shadow: 2px 2px 3px #999;
+    -webkit-box-shadow: 2px 2px 3px #999;
+    -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
+    background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
+}
+
+div.groupHeader {
+    margin-left: 16px;
+    margin-top: 12px;
+    font-weight: bold;
+}
+
+div.groupText {
+    margin-left: 16px;
+    font-style: italic;
+}
+
+body {
+    background-color: #f1ead2;
+    color: black;
+    margin: 0;
+}
+
+div.contents {
+    background-color: white;
+    margin-top: 0px;
+    margin-left: auto;
+    margin-right: auto;
+    width: 960px;
+    padding: 12px;
+}
+
+td.indexkey {
+    background-color: #EBEFF6;
+    font-weight: bold;
+    border: 1px solid #C4CFE5;
+    margin: 2px 0px 2px 0;
+    padding: 2px 10px;
+    white-space: nowrap;
+    vertical-align: top;
+}
+
+td.indexvalue {
+    background-color: #EBEFF6;
+    border: 1px solid #C4CFE5;
+    padding: 2px 10px;
+    margin: 2px 0px;
+}
+
+tr.memlist {
+    background-color: #EEF1F7;
+}
+
+p.formulaDsp {
+    text-align: center;
+}
+
+img.formulaDsp {
+
+}
+
+img.formulaInl {
+    vertical-align: middle;
+}
+
+div.center {
+    text-align: center;
+    margin-top: 0px;
+    margin-bottom: 0px;
+    padding: 0px;
+}
+
+div.center img {
+    border: 0px;
+}
+
+address.footer {
+    text-align: right;
+    padding-right: 12px;
+}
+
+img.footer {
+    border: 0px;
+    vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+span.keyword {
+    color: #008000
+}
+
+span.keywordtype {
+    color: #604020
+}
+
+span.keywordflow {
+    color: #e08000
+}
+
+span.comment {
+    color: #800000
+}
+
+span.preprocessor {
+    color: #806020
+}
+
+span.stringliteral {
+    color: #002080
+}
+
+span.charliteral {
+    color: #008080
+}
+
+span.vhdldigit {
+    color: #ff00ff
+}
+
+span.vhdlchar {
+    color: #000000
+}
+
+span.vhdlkeyword {
+    color: #700070
+}
+
+span.vhdllogic {
+    color: #ff0000
+}
+
+blockquote {
+    background-color: #F7F8FB;
+    border-left: 2px solid #9CAFD4;
+    margin: 0 24px 0 4px;
+    padding: 0 12px 0 16px;
+}
+
+/* @end */
+
+td.tiny {
+    font-size: 75%;
+}
+
+.dirtab {
+    padding: 4px;
+    border-collapse: collapse;
+    border: 1px solid #A3B4D7;
+}
+
+th.dirtab {
+    background: #EBEFF6;
+    font-weight: bold;
+}
+
+hr {
+    height: 0px;
+    border: none;
+    border-top: 1px solid #4A6AAA;
+}
+
+hr.footer {
+    height: 1px;
+}
+
+/* @group Member Descriptions */
+
+table.memberdecls {
+    border-spacing: 0px;
+    padding: 0px;
+}
+
+.memberdecls td, .fieldtable tr {
+    -webkit-transition-property: background-color, box-shadow;
+    -webkit-transition-duration: 0.5s;
+    -moz-transition-property: background-color, box-shadow;
+    -moz-transition-duration: 0.5s;
+    -ms-transition-property: background-color, box-shadow;
+    -ms-transition-duration: 0.5s;
+    -o-transition-property: background-color, box-shadow;
+    -o-transition-duration: 0.5s;
+    transition-property: background-color, box-shadow;
+    transition-duration: 0.5s;
+}
+
+.memberdecls td.glow, .fieldtable tr.glow {
+    background-color: cyan;
+    box-shadow: 0 0 15px cyan;
+}
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+    border: none;
+    margin: 4px;
+    padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+    padding: 0px 8px 4px 8px;
+    color: #555;
+}
+
+.memSeparator {
+    line-height: 1px;
+    margin: 0px;
+    padding: 0px;
+}
+
+.memItemLeft, .memTemplItemLeft {
+    white-space: nowrap;
+}
+
+.memItemRight {
+    width: 100%;
+}
+
+.memTemplParams {
+    white-space: nowrap;
+    font-size: 80%;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
+.memtemplate {
+    font-size: 80%;
+    font-weight: normal;
+    margin-left: 9px;
+}
+
+.memnav {
+    background-color: #EBEFF6;
+    border: 1px solid #A3B4D7;
+    text-align: center;
+    margin: 2px;
+    margin-right: 15px;
+    padding: 2px;
+}
+
+.mempage {
+    width: 100%;
+}
+
+.memitem {
+    padding: 0;
+    margin-bottom: 10px;
+    margin-right: 5px;
+    -webkit-transition: none;
+    -moz-transition: none;
+    -ms-transition: none;
+    -o-transition: none;
+    transition: none;
+    display: table !important;
+    width: 100%;
+}
+
+.memitem.glow {
+    box-shadow: none;
+}
+
+.memname {
+    font-weight: bold;
+    margin-left: 6px;
+}
+
+.memname td {
+    vertical-align: bottom;
+}
+
+.memproto, dl.reflist dt {
+    /* disable inherited shadows */
+    box-shadow: none;
+    -moz-box-shadow: none;
+    -webkit-box-shadow: none;
+}
+
+.memdoc, dl.reflist dd {
+    /* disable inherited shadows */
+    box-shadow: none;
+    -moz-box-shadow: none;
+    -webkit-box-shadow: none;
+
+    /* disable background shadow-like texture */
+    background-image: none;
+}
+
+dl.reflist dt {
+    padding: 5px;
+}
+
+dl.reflist dd {
+    margin: 0px 0px 10px 0px;
+    padding: 5px;
+}
+
+.paramkey {
+    text-align: right;
+}
+
+.paramtype {
+    white-space: nowrap;
+}
+
+.paramname {
+    color: #602020;
+    white-space: nowrap;
+}
+.paramname em {
+    font-style: normal;
+}
+.paramname code {
+    line-height: 14px;
+}
+
+.params, .retval, .exception, .tparams {
+    margin-left: 0px;
+    padding-left: 0px;
+}
+
+.params .paramname, .retval .paramname {
+    font-weight: bold;
+    vertical-align: top;
+}
+
+.params .paramtype {
+    font-style: italic;
+    vertical-align: top;
+}
+
+.params .paramdir {
+    font-family: "courier new",courier,monospace;
+    vertical-align: top;
+}
+
+table.mlabels {
+    border-spacing: 0px;
+}
+
+td.mlabels-left {
+    width: 100%;
+    padding: 0px;
+}
+
+td.mlabels-right {
+    vertical-align: bottom;
+    padding: 0px;
+    white-space: nowrap;
+}
+
+span.mlabels {
+    margin-left: 8px;
+}
+
+span.mlabel {
+    background-color: #728DC1;
+    border-top:1px solid #5373B4;
+    border-left:1px solid #5373B4;
+    border-right:1px solid #C4CFE5;
+    border-bottom:1px solid #C4CFE5;
+    text-shadow: none;
+    color: white;
+    margin-right: 4px;
+    padding: 2px 3px;
+    border-radius: 3px;
+    font-size: 7pt;
+    white-space: nowrap;
+    vertical-align: middle;
+}
+
+
+
+/* @end */
+
+/* these are for tree view when not used as main index */
+
+div.directory {
+    margin: 10px 0px;
+    border-top: 1px solid #A8B8D9;
+    border-bottom: 1px solid #A8B8D9;
+    width: 100%;
+}
+
+.directory table {
+    border-collapse:collapse;
+}
+
+.directory td {
+    margin: 0px;
+    padding: 0px;
+    vertical-align: top;
+}
+
+.directory td.entry {
+    white-space: nowrap;
+    padding-right: 6px;
+    padding-top: 3px;
+}
+
+.directory td.entry a {
+    outline:none;
+}
+
+.directory td.entry a img {
+    border: none;
+}
+
+.directory td.desc {
+    width: 100%;
+    padding-left: 6px;
+    padding-right: 6px;
+    padding-top: 3px;
+    border-left: 1px solid rgba(0,0,0,0.05);
+}
+
+.directory tr.even {
+    padding-left: 6px;
+    background-color: #F7F8FB;
+}
+
+.directory img {
+    vertical-align: -30%;
+}
+
+.directory .levels {
+    white-space: nowrap;
+    width: 100%;
+    text-align: right;
+    font-size: 9pt;
+}
+
+.directory .levels span {
+    cursor: pointer;
+    padding-left: 2px;
+    padding-right: 2px;
+    color: #3D578C;
+}
+
+div.dynheader {
+    margin-top: 8px;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+address {
+    font-style: normal;
+    color: #2A3D61;
+}
+
+table.doxtable {
+    border-collapse:collapse;
+    margin-top: 4px;
+    margin-bottom: 4px;
+}
+
+table.doxtable td, table.doxtable th {
+    border: 1px solid #2D4068;
+    padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+    background-color: #374F7F;
+    color: #FFFFFF;
+    font-size: 110%;
+    padding-bottom: 4px;
+    padding-top: 5px;
+}
+
+table.fieldtable {
+    margin-bottom: 10px;
+    border: 1px solid #A8B8D9;
+    border-spacing: 0px;
+    -moz-border-radius: 4px;
+    -webkit-border-radius: 4px;
+    border-radius: 4px;
+    -moz-box-shadow: none;
+    -webkit-box-shadow: none;
+    box-shadow: none;
+}
+
+.fieldtable td, .fieldtable th {
+    padding: 3px 7px 2px;
+}
+
+.fieldtable td.fieldtype, .fieldtable td.fieldname {
+    white-space: nowrap;
+    border-right: 1px solid #A8B8D9;
+    border-bottom: 1px solid #A8B8D9;
+    vertical-align: top;
+}
+
+.fieldtable td.fieldname {
+    padding-top: 3px;
+}
+
+.fieldtable td.fielddoc {
+    border-bottom: 1px solid #A8B8D9;
+    /*width: 100%;*/
+}
+
+.fieldtable td.fielddoc p:first-child {
+    margin-top: 0px;
+}
+
+.fieldtable td.fielddoc p:last-child {
+    margin-bottom: 2px;
+}
+
+.fieldtable tr:last-child td {
+    border-bottom: none;
+}
+
+.fieldtable th {
+    background-image:url('nav_f.png');
+    background-repeat:repeat-x;
+    background-color: #E2E8F2;
+    font-size: 90%;
+    color: #253555;
+    padding-bottom: 4px;
+    padding-top: 5px;
+    text-align:left;
+    -moz-border-radius-topleft: 4px;
+    -moz-border-radius-topright: 4px;
+    -webkit-border-top-left-radius: 4px;
+    -webkit-border-top-right-radius: 4px;
+    border-top-left-radius: 4px;
+    border-top-right-radius: 4px;
+    border-bottom: 1px solid #A8B8D9;
+}
+
+
+.tabsearch {
+    top: 0px;
+    left: 10px;
+    height: 36px;
+    background-image: url('tab_b.png');
+    z-index: 101;
+    overflow: hidden;
+    font-size: 13px;
+}
+
+.navpath ul
+{
+    font-size: 11px;
+    background-image:url('tab_b.png');
+    background-repeat:repeat-x;
+    background-position: 0 -5px;
+    height:30px;
+    line-height:30px;
+    color:#8AA0CC;
+    border:solid 1px #C2CDE4;
+    overflow:hidden;
+    margin:0px;
+    padding:0px;
+}
+
+.navpath li
+{
+    list-style-type:none;
+    float:left;
+    padding-left:10px;
+    padding-right:15px;
+    background-image:url('bc_s.png');
+    background-repeat:no-repeat;
+    background-position:right;
+    color:#364D7C;
+}
+
+.navpath li.navelem a
+{
+    height:32px;
+    display:block;
+    text-decoration: none;
+    outline: none;
+    color: #283A5D;
+    font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;
+    text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+    text-decoration: none;
+}
+
+.navpath li.navelem a:hover
+{
+    color:#6884BD;
+}
+
+.navpath li.footer
+{
+    list-style-type:none;
+    float:right;
+    padding-left:10px;
+    padding-right:15px;
+    background-image:none;
+    background-repeat:no-repeat;
+    background-position:right;
+    color:#364D7C;
+    font-size: 8pt;
+}
+
+
+div.summary
+{
+    float: right;
+    font-size: 8pt;
+    padding-right: 5px;
+    width: 50%;
+    text-align: right;
+}
+
+div.summary a
+{
+    white-space: nowrap;
+}
+
+div.ingroups
+{
+    font-size: 8pt;
+    width: 50%;
+    text-align: left;
+}
+
+div.ingroups a
+{
+    white-space: nowrap;
+}
+
+div.header
+{
+    background-image: none;
+    margin:  0px;
+    margin-left: auto;
+    margin-right: auto;
+    width: 960px;
+    padding-left: 12px;
+    padding-right: 12px;
+    border-bottom: none;
+}
+
+div.headertitle
+{
+    padding: 5px 5px 5px 10px;
+}
+
+dl
+{
+    padding: 0 0 0 10px;
+}
+
+/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */
+dl.section
+{
+    margin-left: 0px;
+    padding-left: 0px;
+}
+
+dl.note
+{
+    margin-left:-7px;
+    padding-left: 3px;
+    border-left:4px solid;
+    border-color: #D0C000;
+}
+
+dl.warning, dl.attention
+{
+    margin-left:-7px;
+    padding-left: 3px;
+    border-left:4px solid;
+    border-color: #FF0000;
+}
+
+dl.pre, dl.post, dl.invariant
+{
+    margin-left:-7px;
+    padding-left: 3px;
+    border-left:4px solid;
+    border-color: #00D000;
+}
+
+dl.deprecated
+{
+    margin-left:-7px;
+    padding-left: 3px;
+    border-left:4px solid;
+    border-color: #505050;
+}
+
+dl.todo
+{
+    margin-left:-7px;
+    padding-left: 3px;
+    border-left:4px solid;
+    border-color: #00C0E0;
+}
+
+dl.test
+{
+    margin-left:-7px;
+    padding-left: 3px;
+    border-left:4px solid;
+    border-color: #3030E0;
+}
+
+dl.bug
+{
+    margin-left:-7px;
+    padding-left: 3px;
+    border-left:4px solid;
+    border-color: #C08050;
+}
+
+dl.section dd {
+    margin-bottom: 6px;
+}
+
+
+#projectlogo
+{
+    text-align: center;
+    vertical-align: middle;
+    border-collapse: separate;
+    padding-left: 5px;
+    padding-right: 5px;
+}
+
+#projectlogo img
+{
+    border: 0px none;
+}
+
+#projectname
+{
+    font: 0% Tahoma, Arial,sans-serif;
+    margin: 0px;
+    padding: 2px 0px;
+}
+
+#projectbrief
+{
+    font: 120% Tahoma, Arial,sans-serif;
+    margin: 0px;
+    padding: 0px;
+}
+
+#projectnumber
+{
+    font: 50% Tahoma, Arial,sans-serif;
+    margin: 0px;
+    padding: 0px;
+}
+
+#titlearea
+{
+    background-color: white;
+    padding: 0px;
+    margin: 0px;
+    width: 100%;
+}
+
+.image
+{
+    text-align: center;
+}
+
+.dotgraph
+{
+    text-align: center;
+}
+
+.mscgraph
+{
+    text-align: center;
+}
+
+.diagraph
+{
+    text-align: center;
+}
+
+.caption
+{
+    font-weight: bold;
+}
+
+div.zoom
+{
+    border: 1px solid #90A5CE;
+}
+
+dl.citelist {
+    margin-bottom:50px;
+}
+
+dl.citelist dt {
+    color:#334975;
+    float:left;
+    font-weight:bold;
+    margin-right:10px;
+    padding:5px;
+}
+
+dl.citelist dd {
+    margin:2px 0;
+    padding:5px 0;
+}
+
+div.toc {
+    padding: 14px 25px;
+    background-color: #F4F6FA;
+    border: 1px solid #D8DFEE;
+    border-radius: 7px 7px 7px 7px;
+    float: right;
+    height: auto;
+    margin: 0 20px 10px 10px;
+    width: 200px;
+}
+
+div.toc li {
+    background: url("bdwn.png") no-repeat scroll 0 5px transparent;
+    font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif;
+    margin-top: 5px;
+    padding-left: 10px;
+    padding-top: 2px;
+}
+
+div.toc h3 {
+    font: bold 12px/1.2 Arial,FreeSans,sans-serif;
+    color: #4665A2;
+    border-bottom: 0 none;
+    margin: 0;
+}
+
+div.toc ul {
+    list-style: none outside none;
+    border: medium none;
+    padding: 0px;
+}
+
+div.toc li.level1 {
+    margin-left: 0px;
+}
+
+div.toc li.level2 {
+    margin-left: 15px;
+}
+
+div.toc li.level3 {
+    margin-left: 30px;
+}
+
+div.toc li.level4 {
+    margin-left: 45px;
+}
+
+.inherit_header {
+    font-weight: bold;
+    color: gray;
+    cursor: pointer;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+.inherit_header td {
+    padding: 6px 0px 2px 5px;
+}
+
+.inherit {
+    display: none;
+}
+
+tr.heading h2 {
+    margin-top: 0px;
+    margin-bottom: 4px;
+}
+
+/* tooltip related style info */
+
+.ttc {
+    position: absolute;
+    display: none;
+}
+
+#powerTip {
+    cursor: default;
+    white-space: nowrap;
+    background-color: white;
+    border: 1px solid gray;
+    border-radius: 4px 4px 4px 4px;
+    box-shadow: 1px 1px 7px gray;
+    display: none;
+    font-size: smaller;
+    max-width: 80%;
+    opacity: 0.9;
+    padding: 1ex 1em 1em;
+    position: absolute;
+    z-index: 2147483647;
+}
+
+#powerTip div.ttdoc {
+    color: grey;
+    font-style: italic;
+}
+
+#powerTip div.ttname a {
+    font-weight: bold;
+}
+
+#powerTip div.ttname {
+    font-weight: bold;
+}
+
+#powerTip div.ttdeci {
+    color: #006318;
+}
+
+#powerTip div {
+    margin: 0px;
+    padding: 0px;
+    font: 12px/16px Roboto,sans-serif;
+}
+
+#powerTip:before, #powerTip:after {
+    content: "";
+    position: absolute;
+    margin: 0px;
+}
+
+#powerTip.n:after,  #powerTip.n:before,
+#powerTip.s:after,  #powerTip.s:before,
+#powerTip.w:after,  #powerTip.w:before,
+#powerTip.e:after,  #powerTip.e:before,
+#powerTip.ne:after, #powerTip.ne:before,
+#powerTip.se:after, #powerTip.se:before,
+#powerTip.nw:after, #powerTip.nw:before,
+#powerTip.sw:after, #powerTip.sw:before {
+    border: solid transparent;
+    content: " ";
+    height: 0;
+    width: 0;
+    position: absolute;
+}
+
+#powerTip.n:after,  #powerTip.s:after,
+#powerTip.w:after,  #powerTip.e:after,
+#powerTip.nw:after, #powerTip.ne:after,
+#powerTip.sw:after, #powerTip.se:after {
+    border-color: rgba(255, 255, 255, 0);
+}
+
+#powerTip.n:before,  #powerTip.s:before,
+#powerTip.w:before,  #powerTip.e:before,
+#powerTip.nw:before, #powerTip.ne:before,
+#powerTip.sw:before, #powerTip.se:before {
+    border-color: rgba(128, 128, 128, 0);
+}
+
+#powerTip.n:after,  #powerTip.n:before,
+#powerTip.ne:after, #powerTip.ne:before,
+#powerTip.nw:after, #powerTip.nw:before {
+    top: 100%;
+}
+
+#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after {
+    border-top-color: #ffffff;
+    border-width: 10px;
+    margin: 0px -10px;
+}
+#powerTip.n:before {
+    border-top-color: #808080;
+    border-width: 11px;
+    margin: 0px -11px;
+}
+#powerTip.n:after, #powerTip.n:before {
+    left: 50%;
+}
+
+#powerTip.nw:after, #powerTip.nw:before {
+    right: 14px;
+}
+
+#powerTip.ne:after, #powerTip.ne:before {
+    left: 14px;
+}
+
+#powerTip.s:after,  #powerTip.s:before,
+#powerTip.se:after, #powerTip.se:before,
+#powerTip.sw:after, #powerTip.sw:before {
+    bottom: 100%;
+}
+
+#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after {
+    border-bottom-color: #ffffff;
+    border-width: 10px;
+    margin: 0px -10px;
+}
+
+#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before {
+    border-bottom-color: #808080;
+    border-width: 11px;
+    margin: 0px -11px;
+}
+
+#powerTip.s:after, #powerTip.s:before {
+    left: 50%;
+}
+
+#powerTip.sw:after, #powerTip.sw:before {
+    right: 14px;
+}
+
+#powerTip.se:after, #powerTip.se:before {
+    left: 14px;
+}
+
+#powerTip.e:after, #powerTip.e:before {
+    left: 100%;
+}
+#powerTip.e:after {
+    border-left-color: #ffffff;
+    border-width: 10px;
+    top: 50%;
+    margin-top: -10px;
+}
+#powerTip.e:before {
+    border-left-color: #808080;
+    border-width: 11px;
+    top: 50%;
+    margin-top: -11px;
+}
+
+#powerTip.w:after, #powerTip.w:before {
+    right: 100%;
+}
+#powerTip.w:after {
+    border-right-color: #ffffff;
+    border-width: 10px;
+    top: 50%;
+    margin-top: -10px;
+}
+#powerTip.w:before {
+    border-right-color: #808080;
+    border-width: 11px;
+    top: 50%;
+    margin-top: -11px;
+}
+
+#nav-tree {
+    background-image: none;
+}
+
+#side-nav {
+    width: 210px;
+}
+
+@media print
+{
+    #top { display: none; }
+    #side-nav { display: none; }
+    #nav-path { display: none; }
+    body { overflow:visible; }
+    h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
+    .summary { display: none; }
+    .memitem { page-break-inside: avoid; }
+    #doc-content
+    {
+        margin-left:0 !important;
+        height:auto !important;
+        width:auto !important;
+        overflow:inherit;
+        display:inline;
+    }
+}
+
diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg
new file mode 100644 (file)
index 0000000..022138b
--- /dev/null
@@ -0,0 +1,2307 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = Cargo
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+#PROJECT_LOGO           = ../images/cargo-logo-doc.png
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = ./
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = YES
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        = ../
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = YES
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = ../common          \
+                         ../libs            \
+                         cargo_usage.md
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 1
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            = header.html
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            = footer.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  = custom.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       = ../images/GitHub-Logo-Cut.png             \
+                         ../images/Tizen-Logo-On-Light-RGB-Cut.png \
+                         ../images/favicon.ico
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 46
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 180
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 180
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/doc/footer.html b/doc/footer.html
new file mode 100644 (file)
index 0000000..e9a2ed7
--- /dev/null
@@ -0,0 +1,21 @@
+<!-- HTML footer for doxygen 1.8.6-->
+<!-- start footer part -->
+<!--BEGIN GENERATE_TREEVIEW-->
+<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
+  <ul>
+    $navpath
+    <li class="footer">$generatedby
+    <a href="http://www.doxygen.org/index.html">
+    <img class="footer" src="$relpath^doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
+  </ul>
+</div>
+<!--END GENERATE_TREEVIEW-->
+<!--BEGIN !GENERATE_TREEVIEW-->
+<hr class="footer"/><address class="footer"><small>
+$generatedby &#160;<a href="http://www.doxygen.org/index.html">
+<img class="footer" src="$relpath^doxygen.png" alt="doxygen"/>
+</a> $doxygenversion
+</small></address>
+<!--END !GENERATE_TREEVIEW-->
+</body>
+</html>
diff --git a/doc/generate_documentation.sh b/doc/generate_documentation.sh
new file mode 100755 (executable)
index 0000000..e9f4721
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash -e
+
+DOC_CFG="doxygen.cfg"
+
+# Check if doxy is visible
+echo -n "Checking for doxygen... "
+EXE_PATH=$(which doxygen)
+if [ ! -x "$EXE_PATH" ] ; then
+    echo "NOT FOUND, EXITING"
+else
+    echo "FOUND"
+fi
+
+# Change pwd to script dir, to keep the paths consistent
+pushd . > /dev/null
+cd "$(dirname "${BASH_SOURCE[0]}" )"
+
+doxygen "$DOC_CFG"
+
+popd > /dev/null
diff --git a/doc/header.html b/doc/header.html
new file mode 100644 (file)
index 0000000..e6d65ac
--- /dev/null
@@ -0,0 +1,43 @@
+<!-- HTML header for doxygen 1.8.6-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
+<meta http-equiv="X-UA-Compatible" content="IE=9"/>
+<meta name="generator" content="Doxygen $doxygenversion"/>
+<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
+<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
+<link href="$relpath^favicon.ico" rel="icon" type="image/ico" />
+<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
+<script type="text/javascript" src="$relpath^jquery.js"></script>
+<script type="text/javascript" src="$relpath^dynsections.js"></script>
+$treeview
+$search
+$mathjax
+<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
+$extrastylesheet
+</head>
+<body>
+<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
+
+<!--BEGIN TITLEAREA-->
+<div id="titlearea">
+<table style="table-layout:fixed">
+    <tbody>
+        <tr style="height: 56px;">
+            <!--BEGIN PROJECT_BRIEF-->
+            <td valign="bottom" style="padding:5px;">
+                <div id="projectbrief">$projectbrief</div>
+            </td>
+            <!--END PROJECT_BRIEF-->
+
+            <!--BEGIN SEARCHENGINE-->
+            <td>$searchbox</td>
+            <!--END SEARCHENGINE-->
+            </td>
+        </tr>
+    </tbody>
+</table>
+</div>
+<!--END TITLEAREA-->
+<!-- end header part -->
diff --git a/libs/cargo-fd/CMakeLists.txt b/libs/cargo-fd/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9b39e49
--- /dev/null
@@ -0,0 +1,59 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Pawel Kubik (p.kubik@samsung.com)
+#
+
+PROJECT(cargo-fd)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+FILE(GLOB SRCS internals/*.cpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
+    SOVERSION   ${_LIB_SOVERSION_}
+    VERSION     ${_LIB_VERSION_}
+)
+
+ADD_DEPENDENCIES(${PROJECT_NAME} cargo-utils)
+
+## Link libraries ##############################################################
+INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${LIBS_FOLDER})
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(TARGETS     ${PROJECT_NAME}
+        DESTINATION ${LIB_INSTALL_DIR}
+        COMPONENT   RuntimeLibraries)
+
+INSTALL(DIRECTORY . DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}
+        FILES_MATCHING PATTERN "*.hpp"
+                       PATTERN "CMakeFiles" EXCLUDE)
+
+INSTALL(FILES       ${COMMON_FOLDER}/config.hpp
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
diff --git a/libs/cargo-fd/cargo-fd.hpp b/libs/cargo-fd/cargo-fd.hpp
new file mode 100644 (file)
index 0000000..c83549c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author   Pawel Kubik (p.kubik@samsung.com)
+ * @defgroup libcargo-fd libcargo-fd
+ * @brief    cargo file descriptor interface
+ */
+
+#ifndef CARGO_FD_CARGO_FD_HPP
+#define CARGO_FD_CARGO_FD_HPP
+
+#include "cargo-fd/internals/to-fdstore-visitor.hpp"
+#include "cargo-fd/internals/to-fdstore-internet-visitor.hpp"
+#include "cargo-fd/internals/from-fdstore-visitor.hpp"
+#include "cargo-fd/internals/from-fdstore-internet-visitor.hpp"
+
+
+namespace cargo {
+
+/*@{*/
+
+/**
+ * Load binary data from a file/socket/pipe represented by the fd
+ *
+ * @param fd        file descriptor
+ * @param visitable visitable structure to load
+ */
+template <class Cargo>
+void loadFromFD(const int fd, Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::FromFDStoreVisitor visitor(fd);
+    visitable.accept(visitor);
+}
+
+/**
+ * Save binary data to a file/socket/pipe represented by the fd
+ *
+ * @param fd        file descriptor
+ * @param visitable visitable structure to save
+ */
+template <class Cargo>
+void saveToFD(const int fd, const Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::ToFDStoreVisitor visitor(fd);
+    visitable.accept(visitor);
+}
+
+/**
+ * Load binary data from an internet socket represented by the fd
+ *
+ * @param fd        file descriptor
+ * @param visitable visitable structure to load
+ */
+template <class Cargo>
+void loadFromInternetFD(const int fd, Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::FromFDStoreInternetVisitor visitor(fd);
+    visitable.accept(visitor);
+}
+
+/**
+ * Save binary data to an internet socket represented by the fd
+ *
+ * @param fd        file descriptor
+ * @param visitable visitable structure to save
+ */
+template <class Cargo>
+void saveToInternetFD(const int fd, const Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::ToFDStoreInternetVisitor visitor(fd);
+    visitable.accept(visitor);
+}
+
+} // namespace cargo
+
+/*@}*/
+
+#endif // CARGO_FD_CARGO_FD_HPP
diff --git a/libs/cargo-fd/internals/fdstore.cpp b/libs/cargo-fd/internals/fdstore.cpp
new file mode 100644 (file)
index 0000000..c5d901e
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Definition of a class for writing and reading data from a file descriptor
+ */
+
+#include "config.hpp"
+
+#include "cargo-fd/internals/fdstore.hpp"
+#include "cargo/exception.hpp"
+
+#include <cstring>
+#include <cerrno>
+#include <unistd.h>
+#include <chrono>
+#include <poll.h>
+#include <sys/socket.h>
+
+namespace cargo {
+
+namespace internals {
+
+namespace {
+
+const int ERROR_MESSAGE_BUFFER_CAPACITY = 256;
+
+std::string getSystemErrorMessage()
+{
+    char buf[ERROR_MESSAGE_BUFFER_CAPACITY];
+    return strerror_r(errno, buf, sizeof(buf));
+}
+
+
+void waitForEvent(int fd,
+                  short event,
+                  const std::chrono::high_resolution_clock::time_point deadline)
+{
+    // Wait for the rest of the data
+    struct pollfd fds[1];
+    fds[0].fd = fd;
+    fds[0].events = event | POLLHUP;
+
+    for (;;) {
+        std::chrono::milliseconds timeoutMS =
+            std::chrono::duration_cast<std::chrono::milliseconds>(deadline - std::chrono::high_resolution_clock::now());
+        if (timeoutMS.count() < 0) {
+            throw CargoException("Timeout");
+        }
+
+        int ret = ::poll(fds, 1 /*fds size*/, timeoutMS.count());
+
+        if (ret == -1) {
+            if (errno == EINTR) {
+                continue;
+            }
+            throw CargoException("Error in poll: " + getSystemErrorMessage());
+        }
+
+        if (ret == 0) {
+            throw CargoException("Timeout");
+        }
+
+        if (fds[0].revents & POLLHUP) {
+            throw CargoException("Peer disconnected");
+        }
+
+        // Here Comes the Sun
+        break;
+    }
+}
+
+} // namespace
+
+FDStore::FDStore(int fd)
+    : mFD(fd)
+{
+}
+
+FDStore::FDStore(const FDStore& store)
+    : mFD(store.mFD)
+{
+}
+
+FDStore::~FDStore()
+{
+}
+
+void FDStore::write(const void* bufferPtr, const size_t size, const unsigned int timeoutMS)
+{
+    std::chrono::high_resolution_clock::time_point deadline =
+        std::chrono::high_resolution_clock::now() +
+        std::chrono::milliseconds(timeoutMS);
+
+    size_t nTotal = 0;
+    for (;;) {
+        ssize_t n  = ::write(mFD,
+                             reinterpret_cast<const char*>(bufferPtr) + nTotal,
+                             size - nTotal);
+        if (n < 0) {
+            // Handle errors
+            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+                // Neglected errors
+            } else {
+                throw CargoException("Error during writing: " + getSystemErrorMessage());
+            }
+        } else {
+            nTotal += n;
+            if (nTotal == size) {
+                // All data is read, break loop
+                break;
+            }
+        }
+
+        waitForEvent(mFD, POLLOUT, deadline);
+    }
+}
+
+void FDStore::read(void* bufferPtr, const size_t size, const unsigned int timeoutMS)
+{
+    std::chrono::high_resolution_clock::time_point deadline =
+        std::chrono::high_resolution_clock::now() +
+        std::chrono::milliseconds(timeoutMS);
+
+    size_t nTotal = 0;
+    for (;;) {
+        ssize_t n  = ::read(mFD,
+                            reinterpret_cast<char*>(bufferPtr) + nTotal,
+                            size - nTotal);
+        if (n < 0) {
+            // Handle errors
+            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+                // Neglected errors
+            } else {
+                throw CargoException("Error during reading: " + getSystemErrorMessage());
+            }
+        } else {
+            nTotal += n;
+            if (nTotal == size) {
+                // All data is read, break loop
+                break;
+            }
+            if (n == 0) {
+                throw CargoException("Peer disconnected");
+            }
+        }
+
+        waitForEvent(mFD, POLLIN, deadline);
+    }
+}
+
+
+void FDStore::sendFD(int fd, const unsigned int timeoutMS)
+{
+    std::chrono::high_resolution_clock::time_point deadline =
+        std::chrono::high_resolution_clock::now() +
+        std::chrono::milliseconds(timeoutMS);
+
+    // Space for the file descriptor
+    union {
+        struct cmsghdr cmh;
+        char   control[CMSG_SPACE(sizeof(int))];
+    } controlUnion;
+
+    // Ensure at least 1 byte is transmited via the socket
+    struct iovec iov;
+    char buf = '!';
+    iov.iov_base = &buf;
+    iov.iov_len = sizeof(char);
+
+    // Fill the message to send:
+    // The socket has to be connected, so we don't need to specify the name
+    struct msghdr msgh;
+    ::memset(&msgh, 0, sizeof(msgh));
+
+    // Only iovec to transmit one element
+    msgh.msg_iov = &iov;
+    msgh.msg_iovlen = 1;
+
+    // Ancillary data buffer
+    msgh.msg_control = controlUnion.control;
+    msgh.msg_controllen = sizeof(controlUnion.control);
+
+    // Describe the data that we want to send
+    struct cmsghdr *cmhp;
+    cmhp = CMSG_FIRSTHDR(&msgh);
+    cmhp->cmsg_len = CMSG_LEN(sizeof(int));
+    cmhp->cmsg_level = SOL_SOCKET;
+    cmhp->cmsg_type = SCM_RIGHTS;
+    *(reinterpret_cast<int*>(CMSG_DATA(cmhp))) = fd;
+
+    // Send
+    for(;;) {
+        ssize_t ret = ::sendmsg(mFD, &msgh, MSG_NOSIGNAL);
+        if (ret < 0) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+                // Neglected errors, retry
+            } else {
+                throw CargoException("Error during sendmsg: " + getSystemErrorMessage());
+            }
+        } else if (ret == 0) {
+            // Retry the sending
+        } else {
+            // We send only 1 byte of data. No need to repeat
+            break;
+        }
+
+        waitForEvent(mFD, POLLOUT, deadline);
+    }
+}
+
+
+int FDStore::receiveFD(const unsigned int timeoutMS)
+{
+    std::chrono::high_resolution_clock::time_point deadline =
+        std::chrono::high_resolution_clock::now() +
+        std::chrono::milliseconds(timeoutMS);
+
+    // Space for the file descriptor
+    union {
+        struct cmsghdr cmh;
+        char   control[CMSG_SPACE(sizeof(int))];
+    } controlUnion;
+
+    // Describe the data that we want to recive
+    controlUnion.cmh.cmsg_len = CMSG_LEN(sizeof(int));
+    controlUnion.cmh.cmsg_level = SOL_SOCKET;
+    controlUnion.cmh.cmsg_type = SCM_RIGHTS;
+
+    // Setup the input buffer
+    // Ensure at least 1 byte is transmited via the socket
+    char buf;
+    struct iovec iov;
+    iov.iov_base = &buf;
+    iov.iov_len = sizeof(char);
+
+    // Set the ancillary data buffer
+    // The socket has to be connected, so we don't need to specify the name
+    struct msghdr msgh;
+    ::memset(&msgh, 0, sizeof(msgh));
+
+    msgh.msg_iov = &iov;
+    msgh.msg_iovlen = 1;
+
+    msgh.msg_control = controlUnion.control;
+    msgh.msg_controllen = sizeof(controlUnion.control);
+
+    // Receive
+    for(;;) {
+        ssize_t ret = ::recvmsg(mFD, &msgh, MSG_WAITALL | MSG_CMSG_CLOEXEC);
+        if (ret < 0) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+                // Neglected errors, retry
+            } else {
+                throw CargoException("Error during recvmsg: " + getSystemErrorMessage());
+            }
+        } else if (ret == 0) {
+            throw CargoException("Peer disconnected");
+        } else {
+            // We receive only 1 byte of data. No need to repeat
+            break;
+        }
+
+        waitForEvent(mFD, POLLIN, deadline);
+    }
+
+    struct cmsghdr *cmhp;
+    cmhp = CMSG_FIRSTHDR(&msgh);
+    if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(int))) {
+        throw CargoException("Bad cmsg length");
+    } else if (cmhp->cmsg_level != SOL_SOCKET) {
+        throw CargoException("cmsg_level != SOL_SOCKET");
+    } else if (cmhp->cmsg_type != SCM_RIGHTS) {
+        throw CargoException("cmsg_type != SCM_RIGHTS");
+    }
+
+    return *(reinterpret_cast<int*>(CMSG_DATA(cmhp)));
+}
+
+} // namespace internals
+
+} // namespace cargo
diff --git a/libs/cargo-fd/internals/fdstore.hpp b/libs/cargo-fd/internals/fdstore.hpp
new file mode 100644 (file)
index 0000000..6580310
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Declaration of a class for writing and reading data from a file descriptor
+ */
+
+#ifndef CARGO_FD_INTERNALS_FDSTORE_HPP
+#define CARGO_FD_INTERNALS_FDSTORE_HPP
+
+#include <cstddef>
+
+namespace {
+const unsigned int maxTimeout = 5000;
+} // namespace
+
+namespace cargo {
+
+namespace internals {
+
+class FDStore {
+
+public:
+    /**
+     * Constructor. One can pass any kind of file descriptor.
+     * Serialization is NOT written for network purposes,
+     * rather local communication.
+     *
+     * @param fd file descriptor
+     */
+    FDStore(int fd = -1);
+    FDStore(const FDStore& store);
+    ~FDStore();
+
+    /**
+     * Write data using the file descriptor
+     *
+     * @param bufferPtr buffer with the data
+     * @param size size of the buffer
+     * @param timeoutMS timeout in milliseconds
+     */
+    void write(const void* bufferPtr, const size_t size, const unsigned int timeoutMS = maxTimeout);
+
+    /**
+     * Reads a value of the given type.
+     *
+     * @param bufferPtr buffer with the data
+     * @param size size of the buffer
+     * @param timeoutMS timeout in milliseconds
+     */
+    void read(void* bufferPtr, const size_t size, const unsigned int timeoutMS = maxTimeout);
+
+    void sendFD(int fd, const unsigned int timeoutMS = maxTimeout);
+
+    int receiveFD(const unsigned int timeoutMS = maxTimeout);
+
+private:
+    int mFD;
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_FD_INTERNALS_FDSTORE_HPP
+
+
diff --git a/libs/cargo-fd/internals/from-fdstore-internet-visitor.hpp b/libs/cargo-fd/internals/from-fdstore-internet-visitor.hpp
new file mode 100644 (file)
index 0000000..2ad1475
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Visitor for reading from an internet socket file descriptor
+ */
+
+#ifndef CARGO_FD_INTERNALS_FROM_FDSTORE_INTERNET_VISITOR_HPP
+#define CARGO_FD_INTERNALS_FROM_FDSTORE_INTERNET_VISITOR_HPP
+
+#include "cargo-fd/internals/from-fdstore-visitor-base.hpp"
+
+#include <endian.h>
+#include <cstring>
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Internet file descriptor reading visitor.
+ */
+class FromFDStoreInternetVisitor : public FromFDStoreVisitorBase<FromFDStoreInternetVisitor> {
+public:
+    explicit FromFDStoreInternetVisitor(int fd)
+        : FromFDStoreVisitorBase(fd)
+    {
+    }
+
+    FromFDStoreInternetVisitor(FromFDStoreVisitorBase<FromFDStoreInternetVisitor>& visitor)
+        : FromFDStoreVisitorBase<FromFDStoreInternetVisitor>(visitor)
+    {
+    }
+
+    template<typename T>
+    void visitImpl(T& value)
+    {
+        readInternal(value);
+    }
+
+private:
+    template<typename T,
+             typename std::enable_if<std::is_arithmetic<T>::value
+                                     && sizeof(T) == 2, int>::type = 0>
+    void readInternal(T& value)
+    {
+        uint16_t rawValue;
+        mStore.read(&rawValue, sizeof(T));
+        rawValue = be16toh(rawValue);
+        ::memcpy(&value, &rawValue, sizeof(value));
+    }
+
+    template<typename T,
+             typename std::enable_if<std::is_arithmetic<T>::value
+                                     && sizeof(T) == 4, int>::type = 0>
+    void readInternal(T& value)
+    {
+        uint32_t rawValue;
+        mStore.read(&rawValue, sizeof(T));
+        rawValue = be32toh(rawValue);
+        ::memcpy(&value, &rawValue, sizeof(value));
+    }
+
+    template<typename T,
+             typename std::enable_if<std::is_arithmetic<T>::value
+                                     && sizeof(T) == 8, int>::type = 0>
+    void readInternal(T& value)
+    {
+        uint64_t rawValue;
+        mStore.read(&rawValue, sizeof(T));
+        rawValue = be64toh(rawValue);
+        ::memcpy(&value, &rawValue, sizeof(value));;
+    }
+
+    template<typename T,
+             typename std::enable_if<!std::is_arithmetic<T>::value
+                                     || sizeof(T) == 1, int>::type = 0>
+    void readInternal(T& v)
+    {
+        FromFDStoreVisitorBase<FromFDStoreInternetVisitor>::visitImpl(v);
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_FD_INTERNALS_FROM_FDSTORE_INTERNET_VISITOR_HPP
diff --git a/libs/cargo-fd/internals/from-fdstore-visitor-base.hpp b/libs/cargo-fd/internals/from-fdstore-visitor-base.hpp
new file mode 100644 (file)
index 0000000..a2be5f6
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *  Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Base of visitors for reading from a file descriptor
+ */
+
+#ifndef CARGO_FD_INTERNALS_FROM_FDSTORE_VISITOR_BASE_HPP
+#define CARGO_FD_INTERNALS_FROM_FDSTORE_VISITOR_BASE_HPP
+
+#include "cargo-fd/internals/fdstore.hpp"
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/internals/visit-fields.hpp"
+
+#include <array>
+#include <string>
+#include <utility>
+#include <vector>
+#include <map>
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Base class for file descriptor reading visitors.
+ *
+ * A curiously recurring template pattern example. This
+ * base-class provides a set of recursively called function templates. A child class can
+ * modify the base class behaviour in certain cases by defining:
+ *  - a set of functions that match those cases
+ *  - a template function that match any other case and calls the base class function
+ */
+template<typename RecursiveVisitor>
+class FromFDStoreVisitorBase {
+public:
+    template<typename T>
+    void visit(const std::string&, T& value)
+    {
+        static_cast<RecursiveVisitor*>(this)->visitImpl(value);
+    }
+
+protected:
+    FDStore mStore;
+
+    explicit FromFDStoreVisitorBase(int fd)
+        : mStore(fd)
+    {
+    }
+
+    FromFDStoreVisitorBase(const FromFDStoreVisitorBase&) = default;
+
+    template<typename T>
+    void visit(T& value)
+    {
+        static_cast<RecursiveVisitor*>(this)->visitImpl(value);
+    }
+
+    template<typename T>
+    void visitImpl(T& value)
+    {
+        readInternal(value);
+    }
+
+private:
+    void readInternal(std::string& value)
+    {
+        size_t size;
+        visit(size);
+        value.resize(size);
+        mStore.read(&value.front(), size);
+    }
+
+    void readInternal(char* &value)
+    {
+        size_t size;
+        visit(size);
+
+        value = new char[size + 1];
+        mStore.read(value, size);
+        value[size] = '\0';
+    }
+
+    template<typename T, typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
+    void readInternal(T& value)
+    {
+        mStore.read(&value, sizeof(T));
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    void readInternal(T& value)
+    {
+        RecursiveVisitor visitor(*this);
+        value.accept(visitor);
+    }
+
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    void readInternal(T& value)
+    {
+        visit(*reinterpret_cast<typename std::underlying_type<T>::type*>(&value));
+    }
+
+    template<typename T>
+    void readInternal(std::vector<T>& values)
+    {
+        size_t vectorSize;
+        visit(vectorSize);
+        values.resize(vectorSize);
+
+        for (T& value : values) {
+            visit(value);
+        }
+    }
+
+    template<typename T, std::size_t N>
+    void readInternal(std::array<T, N>& values)
+    {
+        for (T& value : values) {
+            visit(value);
+        }
+    }
+
+    template<typename V>
+    void readInternal(std::map<std::string, V>& values)
+    {
+        size_t mapSize;
+        visit(mapSize);
+
+        for (size_t i = 0; i < mapSize; ++i) {
+            std::pair<std::string, V> val;
+            visit(val);
+            values.insert(std::move(val));
+        }
+    }
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    void readInternal(T& values)
+    {
+        visitFields(values, this, std::string());
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_FD_INTERNALS_FROM_FDSTORE_VISITOR_BASE_HPP
diff --git a/libs/cargo-fd/internals/from-fdstore-visitor.hpp b/libs/cargo-fd/internals/from-fdstore-visitor.hpp
new file mode 100644 (file)
index 0000000..50cc234
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Default visitor for reading from a file descriptor
+ */
+
+#ifndef CARGO_FD_INTERNALS_FROM_FDSTORE_VISITOR_HPP
+#define CARGO_FD_INTERNALS_FROM_FDSTORE_VISITOR_HPP
+
+#include "cargo-fd/internals/from-fdstore-visitor-base.hpp"
+#include "cargo/types.hpp"
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Default file descriptor reading visitor.
+ */
+class FromFDStoreVisitor : public FromFDStoreVisitorBase<FromFDStoreVisitor> {
+public:
+    explicit FromFDStoreVisitor(int fd)
+        : FromFDStoreVisitorBase(fd)
+    {
+    }
+
+    FromFDStoreVisitor(FromFDStoreVisitorBase<FromFDStoreVisitor>& visitor)
+        : FromFDStoreVisitorBase<FromFDStoreVisitor>(visitor)
+    {
+    }
+
+    template<typename T>
+    void visitImpl(T& value)
+    {
+        readInternal(value);
+    }
+
+private:
+    void readInternal(cargo::FileDescriptor& fd)
+    {
+        fd = mStore.receiveFD();
+    }
+
+    template<typename T>
+    void readInternal(T& v)
+    {
+        FromFDStoreVisitorBase<FromFDStoreVisitor>::visitImpl(v);
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_FD_INTERNALS_FROM_FDSTORE_VISITOR_HPP
diff --git a/libs/cargo-fd/internals/to-fdstore-internet-visitor.hpp b/libs/cargo-fd/internals/to-fdstore-internet-visitor.hpp
new file mode 100644 (file)
index 0000000..bb86893
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Visitor for writing to an internet socket file descriptor
+ */
+
+#ifndef CARGO_FD_INTERNALS_TO_FDSTORE_INTERNET_VISITOR_HPP
+#define CARGO_FD_INTERNALS_TO_FDSTORE_INTERNET_VISITOR_HPP
+
+#include "to-fdstore-visitor-base.hpp"
+#include "cargo/types.hpp"
+
+#include <endian.h>
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Internet file descriptor writing visitor.
+ */
+class ToFDStoreInternetVisitor : public ToFDStoreVisitorBase<ToFDStoreInternetVisitor> {
+public:
+    explicit ToFDStoreInternetVisitor(int fd)
+        : ToFDStoreVisitorBase(fd)
+    {
+    }
+
+    ToFDStoreInternetVisitor(ToFDStoreVisitorBase<ToFDStoreInternetVisitor>& visitor)
+        : ToFDStoreVisitorBase<ToFDStoreInternetVisitor>(visitor)
+    {
+    }
+
+    template<typename T>
+    void visitImpl(T& value)
+    {
+        writeInternal(value);
+    }
+
+private:
+    template<typename T,
+             typename std::enable_if<std::is_arithmetic<T>::value
+                                     && sizeof(T) == 2, int>::type = 0>
+    void writeInternal(T& value)
+    {
+        const uint16_t leValue = htobe16(*reinterpret_cast<const uint16_t*>(&value));
+        mStore.write(&leValue, sizeof(T));
+    }
+
+    template<typename T,
+             typename std::enable_if<std::is_arithmetic<T>::value
+                                     && sizeof(T) == 4, int>::type = 0>
+    void writeInternal(T& value)
+    {
+        const uint32_t leValue = htobe32(*reinterpret_cast<const uint32_t*>(&value));
+        mStore.write(&leValue, sizeof(T));
+    }
+
+    template<typename T,
+             typename std::enable_if<std::is_arithmetic<T>::value
+                                     && sizeof(T) == 8, int>::type = 0>
+    void writeInternal(T& value)
+    {
+        const uint64_t leValue = htobe64(*reinterpret_cast<const uint64_t*>(&value));
+        mStore.write(&leValue, sizeof(T));
+    }
+
+    template<typename T,
+             typename std::enable_if<!std::is_arithmetic<T>::value
+                                     || sizeof(T) == 1, int>::type = 0>
+    void writeInternal(T& v)
+    {
+        ToFDStoreVisitorBase<ToFDStoreInternetVisitor>::visitImpl(v);
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_FD_INTERNALS_TO_FDSTORE_INTERNET_VISITOR_HPP
diff --git a/libs/cargo-fd/internals/to-fdstore-visitor-base.hpp b/libs/cargo-fd/internals/to-fdstore-visitor-base.hpp
new file mode 100644 (file)
index 0000000..8588176
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *  Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Base of visitors for writing to a file descriptor
+ */
+
+#ifndef CARGO_FD_INTERNALS_TO_FDSTORE_VISITOR_BASE_HPP
+#define CARGO_FD_INTERNALS_TO_FDSTORE_VISITOR_BASE_HPP
+
+#include "cargo-fd/internals/fdstore.hpp"
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/internals/visit-fields.hpp"
+
+#include <array>
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Base class for file descriptor writing visitors.
+ *
+ * A curiously recurring template pattern example. This
+ * base-class provides a set of recursively called function templates. A child class can
+ * modify the base class behaviour in certain cases by defining:
+ *  - a set of functions that match those cases
+ *  - a template function that match any other case and calls the base class function
+ */
+template<typename RecursiveVisitor>
+class ToFDStoreVisitorBase {
+
+public:
+    template<typename T>
+    void visit(const std::string&, T& value)
+    {
+        static_cast<RecursiveVisitor*>(this)->visitImpl(value);
+    }
+
+protected:
+    FDStore mStore;
+
+    explicit ToFDStoreVisitorBase(int fd)
+        : mStore(fd)
+    {
+    }
+
+    explicit ToFDStoreVisitorBase(const ToFDStoreVisitorBase&) = default;
+
+    template<typename T>
+    void visit(const T& value)
+    {
+        static_cast<RecursiveVisitor*>(this)->visitImpl(value);
+    }
+
+    template<typename T>
+    void visitImpl(T& value)
+    {
+        writeInternal(value);
+    }
+
+private:
+    void writeInternal(const std::string& value)
+    {
+        visit(value.size());
+        mStore.write(value.c_str(), value.size());
+    }
+
+    void writeInternal(const char* &value)
+    {
+        size_t size = std::strlen(value);
+        visit(size);
+        mStore.write(value, size);
+    }
+
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    void writeInternal(const T& value)
+    {
+        visit(static_cast<const typename std::underlying_type<T>::type>(value));
+    }
+
+    template<typename T, typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
+    void writeInternal(const T& value)
+    {
+        mStore.write(&value, sizeof(T));
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    void writeInternal(const T& value)
+    {
+        RecursiveVisitor visitor(*this);
+        value.accept(visitor);
+    }
+
+    template<typename T>
+    void writeInternal(const std::vector<T>& values)
+    {
+        visit(values.size());
+        for (const T& value: values) {
+            visit(value);
+        }
+    }
+
+    template<typename T, std::size_t N>
+    void writeInternal(const std::array<T, N>& values)
+    {
+        for (const T& value: values) {
+            visit(value);
+        }
+    }
+
+    template<typename V>
+    void writeInternal(const std::map<std::string, V>& values)
+    {
+        visit(values.size());
+        for (const auto& value: values) {
+            visit(value);
+        }
+    }
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    void writeInternal(const T& values)
+    {
+        visitFields(values, this, std::string());
+    }
+
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_FD_INTERNALS_TO_FDSTORE_VISITOR_BASE_HPP
diff --git a/libs/cargo-fd/internals/to-fdstore-visitor.hpp b/libs/cargo-fd/internals/to-fdstore-visitor.hpp
new file mode 100644 (file)
index 0000000..b764159
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Default visitor for writing to a file descriptor
+ */
+
+#ifndef CARGO_FD_INTERNALS_TO_FDSTORE_VISITOR_HPP
+#define CARGO_FD_INTERNALS_TO_FDSTORE_VISITOR_HPP
+
+#include "to-fdstore-visitor-base.hpp"
+
+#include "cargo/types.hpp"
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Default file descriptor writing visitor.
+ */
+class ToFDStoreVisitor : public ToFDStoreVisitorBase<ToFDStoreVisitor> {
+public:
+    explicit ToFDStoreVisitor(int fd)
+        : ToFDStoreVisitorBase(fd)
+    {
+    }
+
+    ToFDStoreVisitor(ToFDStoreVisitorBase<ToFDStoreVisitor>& visitor)
+        : ToFDStoreVisitorBase<ToFDStoreVisitor>(visitor)
+    {
+    }
+
+    template<typename T>
+    void visitImpl(T& value)
+    {
+        writeInternal(value);
+    }
+
+private:
+    void writeInternal(const cargo::FileDescriptor& fd)
+    {
+        mStore.sendFD(fd.value);
+    }
+
+    template<typename T>
+    void writeInternal(T& v)
+    {
+        ToFDStoreVisitorBase<ToFDStoreVisitor>::visitImpl(v);
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_FD_INTERNALS_TO_FDSTORE_VISITOR_HPP
diff --git a/libs/cargo-fd/libcargo-fd.pc.in b/libs/cargo-fd/libcargo-fd.pc.in
new file mode 100644 (file)
index 0000000..79f7e14
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: cargo file descriptor library
+Version: @_LIB_VERSION_@
+Libs: -L${libdir} -l@PROJECT_NAME@
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
diff --git a/libs/cargo-gvariant/CMakeLists.txt b/libs/cargo-gvariant/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7d9a8e1
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Pawel Kubik (p.kubik@samsung.com)
+#
+
+PROJECT(cargo-gvariant)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+FILE(GLOB HEADERS           *.hpp
+                            ${COMMON_FOLDER}/config.hpp)
+FILE(GLOB HEADERS_INTERNALS internals/*.hpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+
+## Link libraries ##############################################################
+PKG_CHECK_MODULES(CARGO_GVARIANT_DEPS REQUIRED glib-2.0)
+
+INCLUDE_DIRECTORIES(${LIBS_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${CARGO_GVARIANT_DEPS_INCLUDE_DIRS})
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(DIRECTORY . DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}
+        FILES_MATCHING PATTERN "*.hpp"
+                       PATTERN "CMakeFiles" EXCLUDE)
+
+INSTALL(FILES       ${COMMON_FOLDER}/config.hpp
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
diff --git a/libs/cargo-gvariant/cargo-gvariant.hpp b/libs/cargo-gvariant/cargo-gvariant.hpp
new file mode 100644 (file)
index 0000000..81863d5
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author   Pawel Kubik (p.kubik@samsung.com)
+ * @defgroup libcargo-gvariant libcargo-gvariant
+ * @brief    cargo GVariant interface
+ */
+
+#ifndef CARGO_GVARIANT_CARGO_GVARIANT_HPP
+#define CARGO_GVARIANT_CARGO_GVARIANT_HPP
+
+#include "cargo-gvariant/internals/to-gvariant-visitor.hpp"
+#include "cargo-gvariant/internals/from-gvariant-visitor.hpp"
+
+namespace cargo {
+
+/*@{*/
+
+/**
+ * Fills the cargo structure with data stored in the GVariant
+ *
+ * @param gvariant      data in GVariant type
+ * @param visitable     visitable structure to fill
+ */
+template <class Cargo>
+void loadFromGVariant(GVariant* gvariant, Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+    static_assert(!internals::isUnion<Cargo>::value, "Don't use CARGO_DECLARE_UNION in top level visitable");
+
+    internals::FromGVariantVisitor visitor(gvariant);
+    visitable.accept(visitor);
+}
+
+/**
+ * Saves the visitable in a GVariant
+ *
+ * @param visitable   visitable structure to convert
+ */
+template <class Cargo>
+GVariant* saveToGVariant(const Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+    static_assert(!internals::isUnion<Cargo>::value, "Don't use CARGO_DECLARE_UNION in top level visitable");
+
+    internals::ToGVariantVisitor visitor;
+    visitable.accept(visitor);
+    return visitor.toVariant();
+}
+
+} // namespace cargo
+
+/*@}*/
+
+#endif // CARGO_GVARIANT_CARGO_GVARIANT_HPP
diff --git a/libs/cargo-gvariant/internals/from-gvariant-visitor.hpp b/libs/cargo-gvariant/internals/from-gvariant-visitor.hpp
new file mode 100644 (file)
index 0000000..ba43d89
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Mateusz Malicki (m.malicki2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Mateusz Malicki (m.malicki2@samsung.com)
+ * @brief   GVariant visitor
+ */
+
+#ifndef CARGO_GVARIANT_INTERNALS_FROM_GVARIANT_VISITOR_HPP
+#define CARGO_GVARIANT_INTERNALS_FROM_GVARIANT_VISITOR_HPP
+
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/exception.hpp"
+#include "cargo/internals/is-union.hpp"
+#include "cargo/types.hpp"
+#include "cargo/internals/visit-fields.hpp"
+
+#include <string>
+#include <vector>
+#include <array>
+#include <utility>
+#include <memory>
+#include <cstring>
+#include <cassert>
+
+#include <glib.h>
+
+namespace cargo {
+
+namespace internals {
+
+class FromGVariantVisitor {
+public:
+    explicit FromGVariantVisitor(GVariant* variant)
+    {
+        //Assume that the visited object is not a union
+        checkType(variant, G_VARIANT_TYPE_TUPLE);
+        mIter = g_variant_iter_new(variant);
+    }
+
+    FromGVariantVisitor(const FromGVariantVisitor& visitor)
+        : mIter(g_variant_iter_copy(visitor.mIter))
+    {
+    }
+
+    ~FromGVariantVisitor()
+    {
+        g_variant_iter_free(mIter);
+    }
+
+    FromGVariantVisitor& operator=(const FromGVariantVisitor&) = delete;
+
+    template<typename T>
+    void visit(const std::string& name, T& value)
+    {
+        auto child = makeUnique(g_variant_iter_next_value(mIter));
+        if (!child) {
+            throw cargo::CargoException(
+                "GVariant doesn't match with Cargo. Can't set  '" + name + "'");
+        }
+        fromGVariant(child.get(), value);
+    }
+
+private:
+    GVariantIter* mIter;
+
+    static std::unique_ptr<GVariant, decltype(&g_variant_unref)> makeUnique(GVariant* variant)
+    {
+        return std::unique_ptr<GVariant, decltype(&g_variant_unref)>(variant, g_variant_unref);
+    }
+
+    static void checkType(GVariant* object, const GVariantType* type)
+    {
+        if (!g_variant_is_of_type(object, type)) {
+            throw CargoException("Invalid field type");
+        }
+    }
+
+    static void fromGVariant(GVariant* object, std::int8_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_BYTE);
+        value = static_cast<std::int8_t>(g_variant_get_byte(object));
+    }
+
+    static void fromGVariant(GVariant* object, std::int16_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_INT16);
+        value = g_variant_get_int16(object);
+    }
+
+    static void fromGVariant(GVariant* object, std::int32_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_INT32);
+        value = g_variant_get_int32(object);
+    }
+
+    static void fromGVariant(GVariant* object, std::int64_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_INT64);
+        value = g_variant_get_int64(object);
+    }
+
+    static void fromGVariant(GVariant* object, std::uint8_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_BYTE);
+        value = g_variant_get_byte(object);
+    }
+
+    static void fromGVariant(GVariant* object, std::uint16_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_UINT16);
+        value = g_variant_get_uint16(object);
+    }
+
+    static void fromGVariant(GVariant* object, std::uint32_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_UINT32);
+        value = g_variant_get_uint32(object);
+    }
+
+    static void fromGVariant(GVariant* object, std::uint64_t& value)
+    {
+        checkType(object, G_VARIANT_TYPE_UINT64);
+        value = g_variant_get_uint64(object);
+    }
+
+    static void fromGVariant(GVariant* object, bool& value)
+    {
+        checkType(object, G_VARIANT_TYPE_BOOLEAN);
+        value = g_variant_get_boolean(object);
+    }
+
+    static void fromGVariant(GVariant* object, double& value)
+    {
+        checkType(object, G_VARIANT_TYPE_DOUBLE);
+        value = g_variant_get_double(object);
+    }
+
+    static void fromGVariant(GVariant* object, std::string& value)
+    {
+        checkType(object, G_VARIANT_TYPE_STRING);
+        value = g_variant_get_string(object, NULL);
+    }
+
+    static void fromGVariant(GVariant* object, char* &value)
+    {
+        checkType(object, G_VARIANT_TYPE_STRING);
+
+        const char* source = g_variant_get_string(object, NULL);
+        size_t len = std::strlen(source);
+
+        value = new char[len + 1];
+        std::strncpy(value, source, len);
+        value[len] = '\0';
+    }
+
+    static void fromGVariant(GVariant* object, cargo::FileDescriptor& value)
+    {
+        checkType(object, G_VARIANT_TYPE_HANDLE);
+        value.value = g_variant_get_handle(object);
+    }
+
+    template<typename T>
+    static void fromGVariant(GVariant* object, std::vector<T>& value)
+    {
+        checkType(object, G_VARIANT_TYPE_ARRAY);
+        GVariantIter iter;
+        g_variant_iter_init(&iter, object);
+        int length = g_variant_iter_n_children(&iter);
+        value.resize(static_cast<size_t>(length));
+        for (int i = 0; i < length; ++i) {
+            auto child = makeUnique(g_variant_iter_next_value(&iter));
+            assert(child);
+            fromGVariant(child.get(), value[static_cast<size_t>(i)]);
+        }
+    }
+
+    template<typename T, std::size_t N>
+    static void fromGVariant(GVariant* object, std::array<T, N>& values)
+    {
+        checkType(object, G_VARIANT_TYPE_ARRAY);
+
+        GVariantIter iter;
+        g_variant_iter_init(&iter, object);
+
+        for (T& value: values) {
+            auto child = makeUnique(g_variant_iter_next_value(&iter));
+            fromGVariant(child.get(), value);
+        }
+    }
+
+    template<typename V>
+    static void fromGVariant(GVariant* object, std::map<std::string, V>& values)
+    {
+        checkType(object, G_VARIANT_TYPE_TUPLE);
+        GVariantIter iter;
+        g_variant_iter_init(&iter, object);
+        const int length = g_variant_iter_n_children(&iter);
+        for (int i = 0; i < length; ++i) {
+            auto child = makeUnique(g_variant_iter_next_value(&iter));
+            assert(child);
+            std::pair<std::string, V> val;
+            fromGVariant(child.get(), val);
+            values.insert(std::move(val));
+        }
+    }
+
+    struct HelperVisitor
+    {
+        template<typename T>
+        static void visit(GVariantIter* iter, T&& value)
+        {
+            auto child = makeUnique(g_variant_iter_next_value(iter));
+            fromGVariant(child.get(), value);
+        }
+    };
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    static void fromGVariant(GVariant* object, T& values)
+    {
+        checkType(object, G_VARIANT_TYPE_TUPLE);
+
+        GVariantIter iter;
+        g_variant_iter_init(&iter, object);
+
+        HelperVisitor visitor;
+        visitFields(values, &visitor, &iter);
+    }
+
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    static void fromGVariant(GVariant* object, T& value)
+    {
+        fromGVariant(object,
+                     *reinterpret_cast<typename std::underlying_type<T>::type*>(&value));
+    }
+
+    template<typename T>
+    static typename std::enable_if<isUnion<T>::value>::type
+    fromGVariant(GVariant* object, T& value)
+    {
+        checkType(object, G_VARIANT_TYPE_VARIANT);
+        auto inner = makeUnique(g_variant_get_variant(object));
+
+        FromGVariantVisitor visitor(inner.get());
+        value.accept(visitor);
+    }
+
+    template<typename T>
+    static typename std::enable_if<isVisitable<T>::value && !isUnion<T>::value>::type
+    fromGVariant(GVariant* object, T& value)
+    {
+        FromGVariantVisitor visitor(object);
+        value.accept(visitor);
+    }
+
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_GVARIANT_INTERNALS_FROM_GVARIANT_VISITOR_HPP
diff --git a/libs/cargo-gvariant/internals/to-gvariant-visitor.hpp b/libs/cargo-gvariant/internals/to-gvariant-visitor.hpp
new file mode 100644 (file)
index 0000000..1ed4181
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Mateusz Malicki (m.malicki2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Mateusz Malicki (m.malicki2@samsung.com)
+ * @brief   GVariant visitor
+ */
+
+#ifndef CARGO_GVARIANT_INTERNALS_TO_GVARIANT_VISITOR_HPP
+#define CARGO_GVARIANT_INTERNALS_TO_GVARIANT_VISITOR_HPP
+
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-union.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/types.hpp"
+#include "cargo/internals/visit-fields.hpp"
+
+#include <array>
+#include <string>
+#include <vector>
+#include <map>
+#include <glib.h>
+#include <utility>
+
+namespace cargo {
+
+namespace internals {
+
+class ToGVariantVisitor {
+
+public:
+    ToGVariantVisitor()
+        : mBuilder(g_variant_builder_new(G_VARIANT_TYPE_TUPLE))
+    {
+    }
+
+    ToGVariantVisitor(const ToGVariantVisitor& visitor)
+        : mBuilder(visitor.mBuilder ? g_variant_builder_ref(visitor.mBuilder) : nullptr)
+    {
+    }
+
+    ~ToGVariantVisitor()
+    {
+        if (mBuilder) {
+            g_variant_builder_unref(mBuilder);
+        }
+    }
+
+    ToGVariantVisitor& operator=(const ToGVariantVisitor&) = delete;
+
+    GVariant* toVariant()
+    {
+        if (mBuilder) {
+            GVariant* ret = g_variant_builder_end(mBuilder);
+            g_variant_builder_unref(mBuilder);
+            mBuilder = nullptr;
+            return ret;
+        }
+        return nullptr;
+    }
+
+    template<typename T>
+    void visit(const std::string& /* name */, const T& value)
+    {
+        writeInternal(value);
+    }
+private:
+    GVariantBuilder* mBuilder;
+
+    void writeInternal(std::int8_t value) {
+        add("y", value);
+    };
+    void writeInternal(std::int16_t value) {
+        add("n", value);
+    };
+    void writeInternal(std::int32_t value) {
+        add("i", value);
+    };
+    void writeInternal(std::int64_t value) {
+        add("x", value);
+    };
+    void writeInternal(std::uint8_t value) {
+        add("y", value);
+    };
+    void writeInternal(std::uint16_t value) {
+        add("q", value);
+    };
+    void writeInternal(std::uint32_t value) {
+        add("u", value);
+    };
+    void writeInternal(std::uint64_t value) {
+        add("t", value);
+    };
+    void writeInternal(bool value) {
+        add("b", value);
+    };
+    void writeInternal(double value) {
+        add("d", value);
+    };
+    void writeInternal(const std::string& value) {
+        add("s", value.c_str());
+    };
+    void writeInternal(const char* value) {
+        add("s", value);
+    };
+    void writeInternal(const cargo::FileDescriptor& value) {
+        add("h", value.value);
+    };
+
+    template<typename T>
+    void writeInternal(const std::vector<T>& value)
+    {
+        if (!value.empty()) {
+            g_variant_builder_open(mBuilder, G_VARIANT_TYPE_ARRAY);
+            for (const T& v : value) {
+                writeInternal(v);
+            }
+            g_variant_builder_close(mBuilder);
+        } else {
+            g_variant_builder_add(mBuilder, "as", NULL);
+        }
+    }
+
+    template<typename T, std::size_t N>
+    void writeInternal(const std::array<T, N>& values)
+    {
+        if (!values.empty()) {
+            g_variant_builder_open(mBuilder, G_VARIANT_TYPE_ARRAY);
+            for (const T& v : values) {
+                writeInternal(v);
+            }
+            g_variant_builder_close(mBuilder);
+        } else {
+            g_variant_builder_add(mBuilder, "as", NULL);
+        }
+    }
+
+    template<typename V>
+    void writeInternal(const std::map<std::string, V>& values)
+    {
+        if (!values.empty()) {
+            g_variant_builder_open(mBuilder, G_VARIANT_TYPE_TUPLE);
+            for (const auto& v : values) {
+                writeInternal(v);
+            }
+            g_variant_builder_close(mBuilder);
+        } else {
+            g_variant_builder_add(mBuilder, "as", NULL);
+        }
+    }
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    void writeInternal(const T& values)
+    {
+        g_variant_builder_open(mBuilder, G_VARIANT_TYPE_TUPLE);
+        visitFields(values, this, std::string());
+        g_variant_builder_close(mBuilder);
+    }
+
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    void writeInternal(const T& value)
+    {
+        writeInternal(static_cast<const typename std::underlying_type<T>::type>(value));
+    }
+
+    template<typename T>
+    typename std::enable_if<isVisitable<T>::value && !isUnion<T>::value>::type
+    writeInternal(const T& value)
+    {
+        ToGVariantVisitor visitor;
+        value.accept(visitor);
+        g_variant_builder_add_value(mBuilder, visitor.toVariant());
+    }
+
+    template<typename T>
+    typename std::enable_if<isVisitable<T>::value && isUnion<T>::value>::type
+    writeInternal(const T& value)
+    {
+        ToGVariantVisitor visitor;
+        value.accept(visitor);
+        add("v", visitor.toVariant());
+    }
+
+    template<typename Value>
+    void add(const char* type, Value value) {
+        g_variant_builder_add(mBuilder, type, value);
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_GVARIANT_INTERNALS_TO_GVARIANT_VISITOR_HPP
diff --git a/libs/cargo-gvariant/libcargo-gvariant.pc.in b/libs/cargo-gvariant/libcargo-gvariant.pc.in
new file mode 100644 (file)
index 0000000..e820114
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: cargo GVariant library
+Version: @_LIB_VERSION_@
+Libs:
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
diff --git a/libs/cargo-ipc/CMakeLists.txt b/libs/cargo-ipc/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5b11970
--- /dev/null
@@ -0,0 +1,81 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Mateusz Malicki (m.malicki2@samsung.com)
+#
+
+PROJECT(cargo-ipc)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+FILE(GLOB HEADERS           *.hpp
+                            ${COMMON_FOLDER}/config.hpp)
+FILE(GLOB HEADERS_INTERNALS internals/*.hpp)
+FILE(GLOB HEADERS_EPOLL     epoll/*.hpp)
+FILE(GLOB HEADERS_UTILS     ${COMMON_FOLDER}/utils/eventfd.hpp
+                            ${COMMON_FOLDER}/utils/callback-guard.hpp)
+
+FILE(GLOB SRCS              *.cpp)
+FILE(GLOB SRCS_INTERNALS    internals/*.cpp)
+FILE(GLOB SRCS_EPOLL        epoll/*.cpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_INTERNALS} ${SRCS_EPOLL} ${SRCS_UTILS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
+    SOVERSION   ${_LIB_SOVERSION_}
+    VERSION     ${_LIB_VERSION_}
+    )
+
+ADD_DEPENDENCIES(${PROJECT_NAME} cargo-utils Logger cargo-fd)
+
+## Link libraries ##############################################################
+IF(WITHOUT_SYSTEMD)
+PKG_CHECK_MODULES(CARGO_IPC_DEPS REQUIRED uuid gio-2.0)
+ELSE()
+PKG_CHECK_MODULES(CARGO_IPC_DEPS REQUIRED uuid gio-2.0 libsystemd-daemon)
+ENDIF(WITHOUT_SYSTEMD)
+
+INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${CARGO_IPC_DEPS_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} cargo-utils ${CARGO_IPC_DEPS_LIBRARIES} Logger cargo-fd)
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(TARGETS     ${PROJECT_NAME}
+        DESTINATION ${LIB_INSTALL_DIR}
+        COMPONENT   RuntimeLibraries)
+
+INSTALL(FILES       ${HEADERS}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
+
+INSTALL(FILES       ${HEADERS_INTERNALS}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/internals)
+
+INSTALL(FILES       ${HEADERS_EPOLL}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/epoll)
+
+INSTALL(FILES       ${HEADERS_UTILS}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/utils)
diff --git a/libs/cargo-ipc/client.cpp b/libs/cargo-ipc/client.cpp
new file mode 100644 (file)
index 0000000..aa275cb
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Handling client connections
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/client.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+#include "cargo-ipc/exception.hpp"
+
+namespace cargo {
+namespace ipc {
+
+using namespace internals;
+
+Client::Client(epoll::EventPoll& eventPoll, const std::string& socketPath)
+    : mEventPoll(eventPoll),
+      mServiceID(),
+      mProcessor(eventPoll, "[CLIENT]  "),
+      mSocketPath(socketPath)
+{
+    LOGS("Client Constructor");
+    setNewPeerCallback(nullptr);
+    setRemovedPeerCallback(nullptr);
+}
+
+Client::~Client()
+{
+    LOGS("Client Destructor");
+    try {
+        stop();
+    } catch (std::exception& e) {
+        LOGE("Error in Client's destructor: " << e.what());
+    }
+}
+
+void Client::start()
+{
+    if (mProcessor.isStarted()) {
+        return;
+    }
+    LOGS("Client start");
+    LOGD("Connecting to " + mSocketPath);
+    auto socketPtr = std::make_shared<Socket>(Socket::connectUNIX(mSocketPath));
+
+    mProcessor.start();
+
+    mServiceID = mProcessor.addPeer(socketPtr);
+}
+
+bool Client::isStarted()
+{
+    return mProcessor.isStarted();
+}
+
+void Client::stop(bool wait)
+{
+    if (!mProcessor.isStarted()) {
+        return;
+    }
+    LOGS("Client stop");
+    mProcessor.stop(wait);
+}
+
+void Client::handle(const FileDescriptor fd, const epoll::Events pollEvents)
+{
+    LOGS("Client handle");
+
+    if (!isStarted()) {
+        LOGW("Client stopped");
+        return;
+    }
+
+    if (pollEvents & EPOLLIN) {
+        mProcessor.handleInput(fd);
+        return; // because handleInput will handle RDHUP
+    }
+
+    if ((pollEvents & EPOLLHUP) || (pollEvents & EPOLLRDHUP)) {
+        mProcessor.handleLostConnection(fd);
+    }
+}
+
+void Client::setNewPeerCallback(const PeerCallback& newPeerCallback)
+{
+    LOGS("Client setNewPeerCallback");
+    auto callback = [newPeerCallback, this](PeerID peerID, FileDescriptor fd) {
+        auto handleFd = [&](FileDescriptor fd, epoll::Events events) {
+            handle(fd, events);
+        };
+        mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, handleFd);
+        if (newPeerCallback) {
+            newPeerCallback(peerID, fd);
+        }
+    };
+    mProcessor.setNewPeerCallback(callback);
+}
+
+void Client::setRemovedPeerCallback(const PeerCallback& removedPeerCallback)
+{
+    LOGS("Client setRemovedPeerCallback");
+    auto callback = [removedPeerCallback, this](PeerID peerID, FileDescriptor fd) {
+        mEventPoll.removeFD(fd);
+        if (removedPeerCallback) {
+            removedPeerCallback(peerID, fd);
+        }
+    };
+    mProcessor.setRemovedPeerCallback(callback);
+}
+
+void Client::removeMethod(const MethodID methodID)
+{
+    LOGS("Client removeMethod methodID: " << methodID);
+    mProcessor.removeMethod(methodID);
+}
+
+bool Client::isHandled(const MethodID methodID)
+{
+    return mProcessor.isHandled(methodID);
+}
+
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/client.hpp b/libs/cargo-ipc/client.hpp
new file mode 100644 (file)
index 0000000..51cad1c
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @defgroup libcargo-ipc libcargo-ipc
+ * @brief   Handling client connections
+ */
+
+#ifndef CARGO_IPC_CLIENT_HPP
+#define CARGO_IPC_CLIENT_HPP
+
+#include "cargo-ipc/internals/processor.hpp"
+#include "cargo-ipc/types.hpp"
+#include "cargo-ipc/result.hpp"
+#include "epoll/event-poll.hpp"
+#include "logger/logger.hpp"
+
+#include <string>
+
+namespace cargo {
+namespace ipc {
+
+/**
+ * @brief This class wraps communication via UX sockets for client applications.
+ * It uses serialization mechanism from Cargo.
+ *
+ * @code
+ * // eventPoll - epoll wrapper class
+ * // address - server socket address
+ * cargo::ipc::epoll::EventPoll examplePoll;
+ * cargo::ipc::Client myClient(examplePoll, address);
+ * myClient.start(); // connect to the service
+ * // call method synchronously
+ * const auto result = mClient.callSync<api::Void, api::ZoneIds>(
+ *                            api::cargo::ipc::METHOD_GET_ZONE_ID_LIST,
+ *                            std::make_shared<api::Void>());
+ * // call method asynchronously
+ * // first: declare lambda function to call on completion
+ * auto asyncResult = [result](cargo::ipc::Result<api::Void>&& out) {
+ *      if (out.isValid()) {
+ *          // got successful response!
+ *      } else {
+ *          // throw expection - one that came back or default
+ *          out.rethrow()
+ *      }
+ * };
+ * std::string id = "example_zone_id";
+ * mClient.callAsync<api::ZoneId, api::Void>(api::cargo::ipc::METHOD_DESTROY_ZONE,
+ *                            std::make_shared<api::ZoneId>(api::ZoneId{id}),
+ *                            asyncResult);
+ * @endcode
+ *
+ * @see libCargo
+ * @see cargo::ipc::Processor
+ * @see cargo::ipc::epoll::EventPoll
+ *
+ * @ingroup libcargo-ipc
+ */
+class Client {
+public:
+    /**
+     * Constructs the Client, but doesn't start it.
+     * Once set-up, call start() to connect client to the server.
+     *
+     * @param eventPoll     event poll
+     * @param serverPath    path to the server's socket
+     */
+    Client(epoll::EventPoll& eventPoll, const std::string& serverPath);
+    ~Client();
+
+    /**
+     * Copying Client class is prohibited.
+     */
+    Client(const Client&) = delete;
+    /**
+     * Copying Client class is prohibited.
+     */
+    Client& operator=(const Client&) = delete;
+
+    /**
+     * Starts processing
+     * @note if the Client is already running, it quits immediately (no exception thrown)
+     */
+    void start();
+
+    /**
+    * Is the communication thread running?
+    *
+    * @return is the communication thread running
+    */
+    bool isStarted();
+
+    /**
+     * Stops processing
+     *
+     * @param wait      should the call block while waiting for all internals to stop? By default true - do block.
+     */
+    void stop(bool wait = true);
+
+    /**
+     * Set the callback called for each new connection to a peer
+     *
+     * @param newPeerCallback    the callback to call on new connection event
+     * @note if callback is already set, it will be overridden
+     */
+    void setNewPeerCallback(const PeerCallback& newPeerCallback);
+
+    /**
+     * Set the callback called when connection to a peer is lost
+     *
+     * @param removedPeerCallback   the callback to call on peer disconnected event
+     * @note if callback is already set, it will be overridden
+     */
+    void setRemovedPeerCallback(const PeerCallback& removedPeerCallback);
+
+    /**
+     * Saves the callback connected to the method id.
+     * When a message with the given method id is received
+     * the data will be parsed and passed to this callback.
+     *
+     * @param methodID          API dependent id of the method
+     * @param method            method handling implementation
+     * @tparam SentDataType     data type to send
+     * @tparam ReceivedDataType data type to receive
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    void setMethodHandler(const MethodID methodID,
+                          const typename MethodHandler<SentDataType, ReceivedDataType>::type& method);
+
+    /**
+     * Saves the callback connected to the method id.
+     * When a message with the given method id is received
+     * the data will be parsed and passed to this callback.
+     *
+     * @param methodID          API dependent id of the method
+     * @param signal            data processing callback
+     * @tparam ReceivedDataType data type to receive
+     */
+    template<typename ReceivedDataType>
+    void setSignalHandler(const MethodID methodID,
+                          const typename SignalHandler<ReceivedDataType>::type& signal);
+
+    /**
+     * Removes the callback associated with specific method id.
+     *
+     * @param methodID          API dependent id of the method
+     * @see setMethodHandler()
+     * @see setSignalHandler()
+     */
+    void removeMethod(const MethodID methodID);
+
+    /**
+     * @param methodID MethodID defined in the user's API
+     * @return is methodID handled by a signal or method
+     */
+    bool isHandled(const MethodID methodID);
+
+    /**
+     * Synchronous method call.
+     *
+     * @param methodID              API dependent id of the method
+     * @param data                  data to send
+     * @param timeoutMS             optional, how long to wait for the return value before throw (milliseconds, default: 5000)
+     * @tparam SentDataType         data type to send
+     * @tparam ReceivedDataType     data type to receive
+     * @return pointer to the call result data
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    std::shared_ptr<ReceivedDataType> callSync(const MethodID methodID,
+                                               const std::shared_ptr<SentDataType>& data,
+                                               unsigned int timeoutMS = 5000);
+
+    /**
+     * Asynchronous method call. The return callback will be called on
+     * return data arrival. It will be run in the PROCESSOR thread.
+     *
+     * @param methodID               API dependent id of the method
+     * @param data                   data to send
+     * @param resultCallback         callback processing the return data
+     * @tparam SentDataType          data type to send
+     * @tparam ReceivedDataType      data type to receive
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    void callAsync(const MethodID methodID,
+                   const std::shared_ptr<SentDataType>& data,
+                   const typename ResultHandler<ReceivedDataType>::type& resultCallback = nullptr);
+
+
+    template<typename SentDataType, typename ReceivedDataType>
+    void callAsyncFromCallback(const MethodID methodID,
+                               const std::shared_ptr<SentDataType>& data,
+                               const typename ResultHandler<ReceivedDataType>::type& resultCallback = nullptr);
+
+    /**
+    * Send a signal to the peer.
+    * There is no return value from the peer
+    * Sends any data only if a peer registered this a signal
+    *
+    * @param methodID           API dependent id of the method
+    * @param data               data to send
+    * @tparam SentDataType      data type to send
+    */
+    template<typename SentDataType>
+    void signal(const MethodID methodID,
+                const std::shared_ptr<SentDataType>& data);
+
+private:
+    epoll::EventPoll& mEventPoll;
+    PeerID mServiceID;
+    internals::Processor mProcessor;
+    std::string mSocketPath;
+
+    void handle(const FileDescriptor fd, const epoll::Events pollEvents);
+
+};
+
+template<typename SentDataType, typename ReceivedDataType>
+void Client::setMethodHandler(const MethodID methodID,
+                              const typename MethodHandler<SentDataType, ReceivedDataType>::type& method)
+{
+    LOGS("Client setMethodHandler, methodID: " << methodID);
+    mProcessor.setMethodHandler<SentDataType, ReceivedDataType>(methodID, method);
+}
+
+template<typename ReceivedDataType>
+void Client::setSignalHandler(const MethodID methodID,
+                              const typename SignalHandler<ReceivedDataType>::type& handler)
+{
+    LOGS("Client setSignalHandler, methodID: " << methodID);
+    mProcessor.setSignalHandler<ReceivedDataType>(methodID, handler);
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+std::shared_ptr<ReceivedDataType> Client::callSync(const MethodID methodID,
+                                                   const std::shared_ptr<SentDataType>& data,
+                                                   unsigned int timeoutMS)
+{
+    LOGS("Client callSync, methodID: " << methodID << ", timeoutMS: " << timeoutMS);
+    return mProcessor.callSync<SentDataType, ReceivedDataType>(methodID, mServiceID, data, timeoutMS);
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+void Client::callAsync(const MethodID methodID,
+                       const std::shared_ptr<SentDataType>& data,
+                       const typename ResultHandler<ReceivedDataType>::type& resultCallback)
+{
+    LOGS("Client callAsync, methodID: " << methodID);
+    mProcessor.callAsync<SentDataType,
+                         ReceivedDataType>(methodID,
+                                           mServiceID,
+                                           data,
+                                           resultCallback);
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+void Client::callAsyncFromCallback(const MethodID methodID,
+                                   const std::shared_ptr<SentDataType>& data,
+                                   const typename ResultHandler<ReceivedDataType>::type& resultCallback)
+{
+    LOGS("Client callAsyncFromCallback, methodID: " << methodID);
+    mProcessor.callAsyncNonBlock<SentDataType,
+                                 ReceivedDataType>(methodID,
+                                                   mServiceID,
+                                                   data,
+                                                   resultCallback);
+}
+
+template<typename SentDataType>
+void Client::signal(const MethodID methodID,
+                    const std::shared_ptr<SentDataType>& data)
+{
+    LOGS("Client signal, methodID: " << methodID);
+    mProcessor.signal<SentDataType>(methodID, data);
+}
+
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_CLIENT_HPP
diff --git a/libs/cargo-ipc/epoll/event-poll.cpp b/libs/cargo-ipc/epoll/event-poll.cpp
new file mode 100644 (file)
index 0000000..565a558
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   C++ epoll wrapper
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+#include "logger/logger.hpp"
+
+#include <sys/epoll.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+using namespace utils;
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+EventPoll::EventPoll()
+    : mPollFD(::epoll_create1(EPOLL_CLOEXEC))
+{
+    if (mPollFD == -1) {
+        const std::string msg = "Failed to create epoll: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+EventPoll::~EventPoll()
+{
+    if (!mCallbacks.empty()) {
+        LOGW("Not removed callbacks: " << mCallbacks.size());
+        for (__attribute__((unused)) const auto& item : mCallbacks) {
+            LOGT("Not removed fd: " << item.first);
+        }
+        assert(0 && "Not removed callbacks left");
+    }
+    utils::close(mPollFD);
+}
+
+int EventPoll::getPollFD() const
+{
+    return mPollFD;
+}
+
+void EventPoll::addFD(const int fd, const Events events, Callback&& callback)
+{
+    std::lock_guard<Mutex> lock(mMutex);
+
+    if (mCallbacks.find(fd) != mCallbacks.end()) {
+        const std::string msg = "fd " + std::to_string(fd) + " already added";
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    if (!addFDInternal(fd, events)) {
+        const std::string msg = "Could not add fd";
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+
+    mCallbacks.insert({fd, std::make_shared<Callback>(std::move(callback))});
+    LOGT("Callback added for fd: " << fd);
+}
+
+void EventPoll::modifyFD(const int fd, const Events events)
+{
+    // No need to lock as modify doesn't touch the mCallbacks map
+    if (!modifyFDInternal(fd, events)) {
+        const std::string msg = "Could not modify fd: " + std::to_string(fd);
+        LOGE(msg);
+        throw UtilsException(msg);
+    }
+}
+
+void EventPoll::removeFD(const int fd)
+{
+    std::lock_guard<Mutex> lock(mMutex);
+
+    auto iter = mCallbacks.find(fd);
+    if (iter == mCallbacks.end()) {
+        LOGT("Callback not found, probably already removed fd: " << fd);
+        return;
+    }
+    mCallbacks.erase(iter);
+    removeFDInternal(fd);
+    LOGT("Callback removed for fd: " << fd);
+}
+
+bool EventPoll::dispatchIteration(const int timeoutMs)
+{
+    for (;;) {
+        epoll_event event;
+        int num = epoll_wait(mPollFD, &event, 1, timeoutMs);
+        if (num == 0) {
+            return false; // timeout
+        }
+        if (num < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+            const std::string msg = "Failed to wait on epoll: " + getSystemErrorMessage();
+            LOGE(msg);
+            throw UtilsException(msg);
+        }
+
+        // callback could be removed in the meantime, so be careful, find it inside lock
+        std::lock_guard<Mutex> lock(mMutex);
+        auto iter = mCallbacks.find(event.data.fd);
+        if (iter == mCallbacks.end()) {
+            continue;
+        }
+
+        // add ref because removeFD(self) can be called inside callback
+        std::shared_ptr<Callback> callback(iter->second);
+        try {
+            LOGT("Dispatch fd: " << event.data.fd << ", events: " << eventsToString(event.events));
+            (*callback)(event.data.fd, event.events);
+            return true;
+        } catch (std::exception& e) {
+            LOGE("Got unexpected exception: " << e.what());
+            assert(0 && "Callback should not throw any exceptions");
+            return true;
+        }
+    }
+}
+
+bool EventPoll::addFDInternal(const int fd, const Events events)
+{
+    epoll_event event;
+    memset(&event, 0, sizeof(event));
+    event.events = events;
+    event.data.fd = fd;
+
+    if (epoll_ctl(mPollFD, EPOLL_CTL_ADD, fd, &event) == -1) {
+        LOGE("Failed to add fd to poll: " << getSystemErrorMessage());
+        return false;
+    }
+    return true;
+}
+
+bool EventPoll::modifyFDInternal(const int fd, const Events events)
+{
+    epoll_event event;
+    memset(&event, 0, sizeof(event));
+    event.events = events;
+    event.data.fd = fd;
+
+    if (epoll_ctl(mPollFD, EPOLL_CTL_MOD, fd, &event) == -1) {
+        LOGE("Failed to modify fd in poll: " << getSystemErrorMessage());
+        return false;
+    }
+    return true;
+}
+
+void EventPoll::removeFDInternal(const int fd)
+{
+    if (epoll_ctl(mPollFD, EPOLL_CTL_DEL, fd, NULL) == -1) {
+        assert(errno != EBADF); // always removeFD before closing fd locally!
+                                // this is important because linux will reuse this fd number
+        LOGE("Failed to remove fd from poll: " << getSystemErrorMessage());
+    }
+}
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/epoll/event-poll.hpp b/libs/cargo-ipc/epoll/event-poll.hpp
new file mode 100644 (file)
index 0000000..2bea932
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   C++ epoll wrapper
+ */
+
+#ifndef CARGO_IPC_EPOLL_EVENT_POLL_HPP
+#define CARGO_IPC_EPOLL_EVENT_POLL_HPP
+
+#include "cargo-ipc/epoll/events.hpp"
+
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+#include <memory>
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+/**
+ * @brief This class waits on registered file descriptor for events.
+ * It uses epoll mechanism.
+ *
+ * @see cargo::ipc::epoll::Events
+ *
+ * @ingroup Types
+ */
+class EventPoll {
+public:
+    /**
+     * Generic function type used as callback for epoll events.
+     *
+     * @param   fd      descriptor that triggered the event
+     * @param   events  event mask that occured
+     * @see cargo::ipc::epoll::Events
+     */
+    typedef std::function<void(int fd, Events events)> Callback;
+
+    /**
+     * Constructs the EventPoll and initializes the underlaying epoll mechanism.
+     * @throw UtilsException thrown if epoll initialization failed
+     */
+    EventPoll();
+    ~EventPoll();
+
+    /**
+     * Returns epoll handle.
+     * @return handle or -1 if not initialized
+     */
+    int getPollFD() const;
+
+    /**
+     * Add descriptor and it's watched events.
+     *
+     * @param fd                descriptor to watch
+     * @param events            events to associate with the descriptor
+     * @param callback          callback to call once the event occurs
+     * @throw UtilsException    thrown if descriptor already registered or add fail
+     * @see cargo::ipc::epoll::Events
+     */
+    void addFD(const int fd, const Events events, Callback&& callback);
+
+    /**
+     * Modify watched events for descriptor.
+     * @param fd                watched descriptor, already registered
+     * @param events            events to associate with the descriptor
+     * @throw UtilsException    if descriptor not found or epoll modify fail
+     * @see cargo::ipc::epoll::Events
+     */
+    void modifyFD(const int fd, const Events events);
+
+    /**
+     * Remove descriptor from the watch list.
+     * @param fd                watched descriptor
+     */
+    void removeFD(const int fd);
+
+    /**
+     * Wait for events on descriptor on the watch list.
+     * Dispatch at most one signaled FD.
+     *
+     * @param timeoutMs       how long should wait in case of no pending events
+     *                        (0 - return immediately, -1 - wait forever)
+     * @throw UtilsException  if epoll_wait fails
+     * @return false on timeout
+     */
+    bool dispatchIteration(const int timeoutMs);
+
+private:
+    typedef std::recursive_mutex Mutex;
+
+    const int mPollFD;
+    Mutex mMutex;
+    std::unordered_map<int, std::shared_ptr<Callback>> mCallbacks;
+
+    bool addFDInternal(const int fd, const Events events);
+    bool modifyFDInternal(const int fd, const Events events);
+    void removeFDInternal(const int fd);
+};
+
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_EPOLL_EVENT_POLL_HPP
diff --git a/libs/cargo-ipc/epoll/events.cpp b/libs/cargo-ipc/epoll/events.cpp
new file mode 100644 (file)
index 0000000..605a2bf
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Epoll events
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/epoll/events.hpp"
+
+#include <sstream>
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+namespace {
+
+std::string eventToString(Events event)
+{
+    switch (event) {
+    case EPOLLIN: return "IN";
+    case EPOLLOUT: return "OUT";
+    case EPOLLERR: return "ERR";
+    case EPOLLHUP: return "HUP";
+    case EPOLLRDHUP: return "RDHUP";
+    default:
+        std::ostringstream ss;
+        ss << "0x" << std::hex << event;
+        return ss.str();
+    }
+}
+
+} // namespace
+
+std::string eventsToString(Events events)
+{
+    if (events == 0) {
+        return "<NONE>";
+    }
+    std::string ret;
+    for (unsigned int i = 0; i<32; ++i) {
+        Events event = 1u << i;
+        if (events & event) {
+            if (!ret.empty()) {
+                ret.append(", ");
+            }
+            ret.append(eventToString(event));
+        }
+    }
+    return ret;
+}
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/epoll/events.hpp b/libs/cargo-ipc/epoll/events.hpp
new file mode 100644 (file)
index 0000000..ad4b70d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Epoll events
+ */
+
+#ifndef CARGO_IPC_EPOLL_EVENTS_HPP
+#define CARGO_IPC_EPOLL_EVENTS_HPP
+
+#include <string>
+#include <sys/epoll.h> // for EPOLL* constatnts
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+/**
+ * @brief bitmask of EPOLL* constants
+ * @ingroup Types
+ */
+typedef unsigned int Events;
+
+/**
+ * Convert event mask into readable string.
+ * Values will be comma separated.
+ * @param events    event type mask to convert
+ * @return string describing the mask
+ */
+std::string eventsToString(Events events);
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_EPOLL_EVENTS_HPP
diff --git a/libs/cargo-ipc/epoll/glib-dispatcher.cpp b/libs/cargo-ipc/epoll/glib-dispatcher.cpp
new file mode 100644 (file)
index 0000000..cd54302
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   glib epoll dispatcher
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/epoll/glib-dispatcher.hpp"
+#include "utils/callback-wrapper.hpp"
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+GlibDispatcher::GlibDispatcher()
+{
+    mChannel = g_io_channel_unix_new(mPoll.getPollFD());
+
+    auto dispatchCallback = [this]() {
+        mPoll.dispatchIteration(0);
+    };
+
+    auto cCallback = [](GIOChannel*, GIOCondition, gpointer data) -> gboolean {
+        utils::getCallbackFromPointer<decltype(dispatchCallback)>(data)();
+        return TRUE;
+    };
+
+    mWatchId = g_io_add_watch_full(mChannel,
+                                   G_PRIORITY_DEFAULT,
+                                   G_IO_IN,
+                                   cCallback,
+                                   utils::createCallbackWrapper(dispatchCallback, mGuard.spawn()),
+                                   &utils::deleteCallbackWrapper<decltype(dispatchCallback)>);
+}
+
+GlibDispatcher::~GlibDispatcher()
+{
+    g_source_remove(mWatchId);
+    g_io_channel_unref(mChannel);
+    // mGuard destructor will wait for full unregister of dispatchCallback
+}
+
+EventPoll& GlibDispatcher::getPoll()
+{
+    return mPoll;
+}
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/epoll/glib-dispatcher.hpp b/libs/cargo-ipc/epoll/glib-dispatcher.hpp
new file mode 100644 (file)
index 0000000..5a96215
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   glib epoll dispatcher
+ */
+
+#ifndef CARGO_IPC_EPOLL_GLIB_DISPATCHER_HPP
+#define CARGO_IPC_EPOLL_GLIB_DISPATCHER_HPP
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include "utils/callback-guard.hpp"
+
+#include <gio/gio.h>
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+/**
+ * Will dispatch poll events in glib thread
+ */
+class GlibDispatcher {
+public:
+    GlibDispatcher();
+    ~GlibDispatcher();
+
+    EventPoll& getPoll();
+private:
+    EventPoll mPoll; // before mGuard!
+    utils::CallbackGuard mGuard;
+    GIOChannel* mChannel;
+    guint mWatchId;
+};
+
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_EPOLL_GLIB_DISPATCHER_HPP
diff --git a/libs/cargo-ipc/epoll/thread-dispatcher.cpp b/libs/cargo-ipc/epoll/thread-dispatcher.cpp
new file mode 100644 (file)
index 0000000..c3d3312
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Thread epoll dispatcher
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/epoll/thread-dispatcher.hpp"
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+ThreadDispatcher::ThreadDispatcher()
+    : mStopped(false)
+{
+    auto controlCallback = [this](int, Events) {
+        mStopEvent.receive();
+        mStopped.store(true, std::memory_order_release);
+    };
+
+    mPoll.addFD(mStopEvent.getFD(), EPOLLIN, std::move(controlCallback));
+    mThread = std::thread([this] {
+        while (!mStopped.load(std::memory_order_acquire)) {
+            mPoll.dispatchIteration(-1);
+        }
+    });
+}
+
+ThreadDispatcher::~ThreadDispatcher()
+{
+    mStopEvent.send();
+    mThread.join();
+    mPoll.removeFD(mStopEvent.getFD());
+}
+
+EventPoll& ThreadDispatcher::getPoll()
+{
+    return mPoll;
+}
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/epoll/thread-dispatcher.hpp b/libs/cargo-ipc/epoll/thread-dispatcher.hpp
new file mode 100644 (file)
index 0000000..3669226
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Thread epoll dispatcher
+ */
+
+#ifndef CARGO_IPC_EPOLL_THREAD_DISPATCHER_HPP
+#define CARGO_IPC_EPOLL_THREAD_DISPATCHER_HPP
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include "utils/eventfd.hpp"
+
+#include <thread>
+#include <atomic>
+
+namespace cargo {
+namespace ipc {
+namespace epoll {
+
+/**
+ * Will dispatch poll events in a newly created thread
+ */
+class ThreadDispatcher {
+public:
+    ThreadDispatcher();
+    ~ThreadDispatcher();
+
+    EventPoll& getPoll();
+private:
+    EventPoll mPoll;
+    utils::EventFD mStopEvent;
+    std::atomic_bool mStopped;
+    std::thread mThread;
+};
+
+} // namespace epoll
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_EPOLL_THREAD_DISPATCHER_HPP
diff --git a/libs/cargo-ipc/exception.hpp b/libs/cargo-ipc/exception.hpp
new file mode 100644 (file)
index 0000000..538ed0d
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Exceptions for the IPC
+ */
+
+
+#ifndef CARGO_IPC_EXCEPTION_HPP
+#define CARGO_IPC_EXCEPTION_HPP
+
+#include <stdexcept>
+
+namespace cargo {
+namespace ipc {
+
+/**
+ * @brief Base class for all exceptions in libcargo-ipc.
+ * @ingroup Types
+ * @defgroup IPCException   IPCException
+ */
+struct IPCException: public std::runtime_error {
+    explicit IPCException(const std::string& message)
+        : std::runtime_error(message) {}
+};
+
+/**
+ * Exception to indicate error while reading/parsing data from the socket.
+ * @ingroup IPCException
+ */
+struct IPCParsingException: public IPCException {
+    explicit IPCParsingException(const std::string& message = "Exception during reading/parsing data from the socket")
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate error while writing/serializing data to the socket.
+ * @ingroup IPCException
+ */
+struct IPCSerializationException: public IPCException {
+    explicit IPCSerializationException(const std::string& message = "Exception during writing/serializing data to the socket")
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate that requested peer is not available, i.e. might got disconnected.
+ * @ingroup IPCException
+ */
+struct IPCPeerDisconnectedException: public IPCException {
+    explicit IPCPeerDisconnectedException(const std::string& message = "No such peer. Might got disconnected.")
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate that peer performed a forbidden action.
+ * @ingroup IPCException
+ */
+struct IPCNaughtyPeerException: public IPCException {
+    explicit IPCNaughtyPeerException(const std::string& message = "Peer performed a forbidden action.")
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate error while removing peer
+ * @ingroup IPCException
+ */
+struct IPCRemovedPeerException: public IPCException {
+    explicit IPCRemovedPeerException(const std::string& message = "Removing peer")
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate error while closing IPC channel
+ * @ingroup IPCException
+ */
+struct IPCClosingException: public IPCException {
+    explicit IPCClosingException(const std::string& message = "Closing IPC")
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate timeout event error
+ * @ingroup IPCException
+ */
+struct IPCTimeoutException: public IPCException {
+    explicit IPCTimeoutException(const std::string& message)
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate invalid result error
+ * @ingroup IPCException
+ */
+struct IPCInvalidResultException: public IPCException {
+    explicit IPCInvalidResultException(const std::string& message)
+        : IPCException(message) {}
+};
+
+/**
+ * Exception to indicate socket error
+ * @ingroup IPCException
+ */
+struct IPCSocketException: public IPCException {
+    IPCSocketException(const int code, const std::string& message)
+        : IPCException(message),
+          mCode(code)
+    {}
+
+    int getCode() const
+    {
+        return mCode;
+    }
+
+private:
+    int mCode;
+};
+
+/**
+ * Exception to indicate user error
+ * @ingroup IPCException
+ */
+struct IPCUserException: public IPCException {
+    IPCUserException(const int code, const std::string& message)
+        : IPCException(message),
+          mCode(code)
+    {}
+
+    int getCode() const
+    {
+        return mCode;
+    }
+
+private:
+    int mCode;
+};
+
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_EXCEPTION_HPP
diff --git a/libs/cargo-ipc/internals/acceptor.cpp b/libs/cargo-ipc/internals/acceptor.cpp
new file mode 100644 (file)
index 0000000..5530aed
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Class for accepting new connections
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/internals/acceptor.hpp"
+#include "logger/logger.hpp"
+
+#include <functional>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+Acceptor::Acceptor(epoll::EventPoll& eventPoll,
+                   const std::string& socketPath,
+                   const NewConnectionCallback& newConnectionCallback)
+    : mEventPoll(eventPoll),
+      mNewConnectionCallback(newConnectionCallback),
+      mSocket(Socket::createUNIX(socketPath))
+{
+    LOGT("Creating Acceptor for socket " << socketPath);
+    mEventPoll.addFD(mSocket.getFD(), EPOLLIN, std::bind(&Acceptor::handleConnection, this));
+}
+
+Acceptor::~Acceptor()
+{
+    LOGT("Destroyed Acceptor");
+    mEventPoll.removeFD(mSocket.getFD());
+}
+
+void Acceptor::handleConnection()
+{
+    std::shared_ptr<Socket> tmpSocket = mSocket.accept();
+    mNewConnectionCallback(tmpSocket);
+}
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/internals/acceptor.hpp b/libs/cargo-ipc/internals/acceptor.hpp
new file mode 100644 (file)
index 0000000..4f7bb24
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Class for accepting new connections
+ */
+
+#ifndef CARGO_IPC_INTERNALS_ACCEPTOR_HPP
+#define CARGO_IPC_INTERNALS_ACCEPTOR_HPP
+
+#include "cargo-ipc/internals/socket.hpp"
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include "cargo-ipc/types.hpp"
+
+#include <string>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+/**
+ * Accepts new connections and passes the new socket to a callback.
+ */
+class Acceptor {
+public:
+
+    typedef std::function<void(std::shared_ptr<Socket>& socketPtr)> NewConnectionCallback;
+
+    /**
+     * Class for accepting new connections.
+     *
+     * @param eventPoll dispatcher
+     * @param socketPath path to the socket
+     * @param newConnectionCallback called on new connections
+     */
+    Acceptor(epoll::EventPoll& eventPoll,
+             const std::string& socketPath,
+             const NewConnectionCallback& newConnectionCallback);
+    ~Acceptor();
+
+    Acceptor(const Acceptor& acceptor) = delete;
+    Acceptor& operator=(const Acceptor&) = delete;
+
+private:
+    epoll::EventPoll& mEventPoll;
+    NewConnectionCallback mNewConnectionCallback;
+    Socket mSocket;
+
+    /**
+     * Handle one incoming connection.
+     * Used with external polling
+     */
+    void handleConnection();
+};
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_ACCEPTOR_HPP
diff --git a/libs/cargo-ipc/internals/add-peer-request.hpp b/libs/cargo-ipc/internals/add-peer-request.hpp
new file mode 100644 (file)
index 0000000..6ffacd3
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Processor's request to add a peer
+ */
+
+#ifndef CARGO_IPC_INTERNALS_ADD_PEER_REQUEST_HPP
+#define CARGO_IPC_INTERNALS_ADD_PEER_REQUEST_HPP
+
+#include "cargo-ipc/types.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class AddPeerRequest {
+public:
+    AddPeerRequest(const AddPeerRequest&) = delete;
+    AddPeerRequest& operator=(const AddPeerRequest&) = delete;
+
+    AddPeerRequest(const std::shared_ptr<Socket>& socketPtr)
+        : socketPtr(socketPtr),
+          peerID(getNextPeerID())
+    {
+    }
+
+    std::shared_ptr<Socket> socketPtr;
+    PeerID peerID;
+};
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_ADD_PEER_REQUEST_HPP
diff --git a/libs/cargo-ipc/internals/event-queue.hpp b/libs/cargo-ipc/internals/event-queue.hpp
new file mode 100644 (file)
index 0000000..7841a1a
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Class for passing events using eventfd mechanism
+ */
+
+#ifndef CARGO_IPC_INTERNALS_EVENT_QUEUE_HPP
+#define CARGO_IPC_INTERNALS_EVENT_QUEUE_HPP
+
+#include "cargo-ipc/exception.hpp"
+#include "utils/eventfd.hpp"
+#include "logger/logger.hpp"
+
+#include <string>
+#include <mutex>
+#include <queue>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+
+/**
+ * This class implements a simple FIFO queue of events.
+ * One can listen for the event with select/poll/epoll.
+ *
+ * @tparam MessageType type to pass as event's value
+ */
+template<typename MessageType>
+class EventQueue {
+public:
+    EventQueue() = default;
+    ~EventQueue() = default;
+    EventQueue(const EventQueue& eventQueue) = delete;
+    EventQueue& operator=(const EventQueue&) = delete;
+
+    /**
+     * @return reference to the event's file descriptor
+     */
+    int getFD() const;
+
+    /**
+     * Send an event
+     *
+     * @param message mesage to send
+     */
+    void send(const MessageType& message);
+
+    /**
+     * Receives the event.
+     * Blocks if there is no event.
+     *
+     * @return event's value
+     */
+    MessageType receive();
+
+    /**
+     * @return is the queue empty
+     */
+    bool isEmpty();
+
+private:
+    typedef std::lock_guard<std::mutex> Lock;
+
+    std::mutex mCommunicationMutex;
+    std::queue<MessageType> mMessages;
+
+    utils::EventFD mEventFD;
+};
+
+template<typename MessageType>
+int EventQueue<MessageType>::getFD() const
+{
+    return mEventFD.getFD();
+}
+
+template<typename MessageType>
+void EventQueue<MessageType>::send(const MessageType& mess)
+{
+    Lock lock(mCommunicationMutex);
+    LOGT("Sending event");
+    mMessages.push(mess);
+    mEventFD.send();
+}
+
+template<typename MessageType>
+MessageType EventQueue<MessageType>::receive()
+{
+    Lock lock(mCommunicationMutex);
+    mEventFD.receive();
+    LOGT("Received event");
+    MessageType mess = mMessages.front();
+    mMessages.pop();
+    return mess;
+}
+
+template<typename MessageType>
+bool EventQueue<MessageType>::isEmpty()
+{
+    Lock lock(mCommunicationMutex);
+    return mMessages.empty();
+}
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_EVENT_QUEUE_HPP
diff --git a/libs/cargo-ipc/internals/finish-request.hpp b/libs/cargo-ipc/internals/finish-request.hpp
new file mode 100644 (file)
index 0000000..fecdc34
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Managing the queue with requests
+ */
+
+#ifndef CARGO_IPC_INTERNALS_FINISH_REQUEST_HPP
+#define CARGO_IPC_INTERNALS_FINISH_REQUEST_HPP
+
+#include <condition_variable>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class FinishRequest {
+public:
+    FinishRequest(const FinishRequest&) = delete;
+    FinishRequest& operator=(const FinishRequest&) = delete;
+
+    FinishRequest(const std::shared_ptr<std::condition_variable>& conditionPtr)
+        : conditionPtr(conditionPtr)
+    {}
+
+    std::shared_ptr<std::condition_variable> conditionPtr;
+};
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_FINISH_REQUEST_HPP
diff --git a/libs/cargo-ipc/internals/method-request.hpp b/libs/cargo-ipc/internals/method-request.hpp
new file mode 100644 (file)
index 0000000..9becf07
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Processor's request to call a method
+ */
+
+#ifndef CARGO_IPC_INTERNALS_METHOD_REQUEST_HPP
+#define CARGO_IPC_INTERNALS_METHOD_REQUEST_HPP
+
+#include "cargo-ipc/internals/result-builder.hpp"
+#include "cargo-ipc/types.hpp"
+#include "cargo-ipc/result.hpp"
+#include "logger/logger-scope.hpp"
+#include "cargo-fd/cargo-fd.hpp"
+#include <utility>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class MethodRequest {
+public:
+    MethodRequest(const MethodRequest&) = delete;
+    MethodRequest& operator=(const MethodRequest&) = delete;
+
+    template<typename SentDataType, typename ReceivedDataType>
+    static std::shared_ptr<MethodRequest> create(const MethodID methodID,
+                                                 const PeerID& peerID,
+                                                 const std::shared_ptr<SentDataType>& data,
+                                                 const typename ResultHandler<ReceivedDataType>::type& process);
+
+    MethodID methodID;
+    PeerID peerID;
+    MessageID messageID;
+    std::shared_ptr<void> data;
+    SerializeCallback serialize;
+    ParseCallback parse;
+    ResultBuilderHandler process;
+
+private:
+    MethodRequest(const MethodID methodID, const PeerID& peerID)
+        : methodID(methodID),
+          peerID(peerID),
+          messageID(getNextMessageID())
+    {}
+};
+
+
+template<typename SentDataType, typename ReceivedDataType>
+std::shared_ptr<MethodRequest> MethodRequest::create(const MethodID methodID,
+                                                     const PeerID& peerID,
+                                                     const std::shared_ptr<SentDataType>& data,
+                                                     const typename ResultHandler<ReceivedDataType>::type& process)
+{
+    std::shared_ptr<MethodRequest> request(new MethodRequest(methodID, peerID));
+
+    request->data = data;
+
+    request->serialize = [](const int fd, std::shared_ptr<void>& data)->void {
+        LOGS("Method serialize, peerID: " << fd);
+        cargo::saveToFD<SentDataType>(fd, *std::static_pointer_cast<SentDataType>(data));
+    };
+
+    request->parse = [](const int fd)->std::shared_ptr<void> {
+        LOGS("Method parse, peerID: " << fd);
+        std::shared_ptr<ReceivedDataType> data(new ReceivedDataType());
+        cargo::loadFromFD<ReceivedDataType>(fd, *data);
+        return data;
+    };
+
+    if(process == nullptr){
+        request->process = [](ResultBuilder & ) {
+            LOGT("No method to process result");
+        };
+    } else {
+        request->process = [process](ResultBuilder & resultBuilder) {
+            LOGS("Method process");
+            process(resultBuilder.build<ReceivedDataType>());
+        };
+    }
+
+    return request;
+}
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_METHOD_REQUEST_HPP
diff --git a/libs/cargo-ipc/internals/processor.cpp b/libs/cargo-ipc/internals/processor.cpp
new file mode 100644 (file)
index 0000000..890c0ce
--- /dev/null
@@ -0,0 +1,787 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Data and event processing thread
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/exception.hpp"
+#include "cargo-ipc/internals/processor.hpp"
+#include "cargo-fd/cargo-fd.hpp"
+#include "cargo/exception.hpp"
+
+#include <cerrno>
+#include <cstring>
+#include <csignal>
+#include <stdexcept>
+#include <cassert>
+
+#include <sys/socket.h>
+#include <limits>
+
+using namespace utils;
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+#define IGNORE_EXCEPTIONS(expr)                        \
+    try                                                \
+    {                                                  \
+        expr;                                          \
+    }                                                  \
+    catch (const std::exception& e){                   \
+        LOGE(mLogPrefix + "Callback threw an error: " << e.what()); \
+    }
+
+const MethodID Processor::RETURN_METHOD_ID = std::numeric_limits<MethodID>::max();
+const MethodID Processor::REGISTER_SIGNAL_METHOD_ID = std::numeric_limits<MethodID>::max() - 1;
+const MethodID Processor::ERROR_METHOD_ID = std::numeric_limits<MethodID>::max() - 2;
+
+Processor::Processor(epoll::EventPoll& eventPoll,
+                     const std::string& logName,
+                     const PeerCallback& newPeerCallback,
+                     const PeerCallback& removedPeerCallback,
+                     const unsigned int maxNumberOfPeers)
+    : mEventPoll(eventPoll),
+      mLogPrefix(logName),
+      mIsRunning(false),
+      mNewPeerCallback(newPeerCallback),
+      mRemovedPeerCallback(removedPeerCallback),
+      mMaxNumberOfPeers(maxNumberOfPeers)
+{
+    LOGS(mLogPrefix + "Processor Constructor");
+
+    using namespace std::placeholders;
+    setSignalHandlerInternal<RegisterSignalsProtocolMessage>(REGISTER_SIGNAL_METHOD_ID,
+                                                             std::bind(&Processor::onNewSignals, this, _1, _2));
+
+    setSignalHandlerInternal<ErrorProtocolMessage>(ERROR_METHOD_ID, std::bind(&Processor::onErrorSignal, this, _1, _2));
+}
+
+Processor::~Processor()
+{
+    LOGS(mLogPrefix + "Processor Destructor");
+    try {
+        stop(false);
+    } catch (std::exception& e) {
+        LOGE(mLogPrefix + "Error in Processor's destructor: " << e.what());
+    }
+}
+
+Processor::Peers::iterator Processor::getPeerInfoIterator(const FileDescriptor fd)
+{
+    return std::find_if(mPeerInfo.begin(), mPeerInfo.end(), [fd](const PeerInfo & peerInfo) {
+        return fd == peerInfo.socketPtr->getFD();
+    });
+}
+
+Processor::Peers::iterator Processor::getPeerInfoIterator(const PeerID & peerID)
+{
+    return std::find_if(mPeerInfo.begin(), mPeerInfo.end(), [peerID](const PeerInfo & peerInfo) {
+        return peerID == peerInfo.peerID;
+    });
+}
+
+bool Processor::isStarted()
+{
+    Lock lock(mStateMutex);
+    return mIsRunning;
+}
+
+void Processor::start()
+{
+    LOGS(mLogPrefix + "Processor start");
+
+    Lock lock(mStateMutex);
+    if (!mIsRunning) {
+        LOGI(mLogPrefix + "Processor start");
+        mIsRunning = true;
+
+        mEventPoll.addFD(mRequestQueue.getFD(), EPOLLIN, std::bind(&Processor::handleEvent, this));
+    }
+}
+
+void Processor::stop(bool wait)
+{
+    LOGS(mLogPrefix + "Processor stop");
+
+    if (isStarted()) {
+        auto conditionPtr = std::make_shared<std::condition_variable>();
+        {
+            Lock lock(mStateMutex);
+            auto request = std::make_shared<FinishRequest>(conditionPtr);
+            mRequestQueue.pushBack(Event::FINISH, request);
+        }
+
+        if (wait) {
+            LOGD(mLogPrefix + "Waiting for the Processor to stop");
+
+            // Wait till the FINISH request is served
+            Lock lock(mStateMutex);
+            conditionPtr->wait(lock, [this]() {
+                return !mIsRunning;
+            });
+            assert(mPeerInfo.empty());
+        }
+    }
+}
+
+void Processor::setNewPeerCallback(const PeerCallback& newPeerCallback)
+{
+    Lock lock(mStateMutex);
+    mNewPeerCallback = newPeerCallback;
+}
+
+void Processor::setRemovedPeerCallback(const PeerCallback& removedPeerCallback)
+{
+    Lock lock(mStateMutex);
+    mRemovedPeerCallback = removedPeerCallback;
+}
+
+FileDescriptor Processor::getEventFD()
+{
+    Lock lock(mStateMutex);
+    return mRequestQueue.getFD();
+}
+
+void Processor::sendResult(const MethodID methodID,
+                           const PeerID& peerID,
+                           const MessageID& messageID,
+                           const std::shared_ptr<void>& data)
+{
+    auto requestPtr = std::make_shared<SendResultRequest>(methodID, peerID, messageID, data);
+    mRequestQueue.pushFront(Event::SEND_RESULT, requestPtr);
+}
+
+void Processor::sendError(const PeerID& peerID,
+                          const MessageID& messageID,
+                          const int errorCode,
+                          const std::string& message)
+{
+    auto data = std::make_shared<ErrorProtocolMessage>(messageID, errorCode, message);
+    signalInternal<ErrorProtocolMessage>(ERROR_METHOD_ID, peerID , data);
+}
+
+void Processor::sendVoid(const MethodID methodID,
+                         const PeerID& peerID,
+                         const MessageID& messageID)
+{
+    auto data = std::make_shared<EmptyData>();
+    auto requestPtr = std::make_shared<SendResultRequest>(methodID, peerID, messageID, data);
+    mRequestQueue.pushFront(Event::SEND_RESULT, requestPtr);
+}
+
+void Processor::removeMethod(const MethodID methodID)
+{
+    Lock lock(mStateMutex);
+    mMethodsCallbacks.erase(methodID);
+}
+
+bool Processor::isHandled(const MethodID methodID)
+{
+    Lock lock(mStateMutex);
+    return mMethodsCallbacks.count(methodID) > 0 ||
+           mSignalsCallbacks.count(methodID) > 0 ;
+}
+
+PeerID Processor::addPeer(const std::shared_ptr<Socket>& socketPtr)
+{
+    LOGS(mLogPrefix + "Processor addPeer");
+    Lock lock(mStateMutex);
+
+    auto requestPtr = std::make_shared<AddPeerRequest>(socketPtr);
+    mRequestQueue.pushBack(Event::ADD_PEER, requestPtr);
+
+    LOGI(mLogPrefix + "Add Peer Request. Id: " << shortenPeerID(requestPtr->peerID)
+                                   << ", fd: " << socketPtr->getFD());
+
+    return requestPtr->peerID;
+}
+
+void Processor::removePeerSyncInternal(const PeerID& peerID, Lock& lock)
+{
+    LOGS(mLogPrefix + "Processor removePeer peerID: " << shortenPeerID(peerID));
+
+    auto isPeerDeleted = [&peerID, this]()->bool {
+        return getPeerInfoIterator(peerID) == mPeerInfo.end();
+    };
+
+    mRequestQueue.removeIf([peerID](Request & request) {
+        return request.requestID == Event::ADD_PEER &&
+               request.get<AddPeerRequest>()->peerID == peerID;
+    });
+
+    // Remove peer and wait till he's gone
+    std::shared_ptr<std::condition_variable> conditionPtr(new std::condition_variable());
+
+    auto request = std::make_shared<RemovePeerRequest>(peerID, conditionPtr);
+    mRequestQueue.pushBack(Event::REMOVE_PEER, request);
+
+    conditionPtr->wait(lock, isPeerDeleted);
+}
+
+void Processor::removePeerInternal(Peers::iterator peerIt, const std::exception_ptr& exceptionPtr)
+{
+    if (peerIt == mPeerInfo.end()) {
+        LOGW("Peer already removed");
+        return;
+    }
+
+    LOGS(mLogPrefix + "Processor removePeerInternal peerID: " << shortenPeerID(peerIt->peerID));
+    LOGI(mLogPrefix + "Removing peer. peerID: " << shortenPeerID(peerIt->peerID));
+
+    // Remove from signal addressees
+    for (auto it = mSignalsPeers.begin(); it != mSignalsPeers.end();) {
+        it->second.remove(peerIt->peerID);
+        if (it->second.empty()) {
+            it = mSignalsPeers.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    // Erase associated return value callbacks
+    for (auto it = mReturnCallbacks.begin(); it != mReturnCallbacks.end();) {
+        if (it->second.peerID == peerIt->peerID) {
+            ResultBuilder resultBuilder(exceptionPtr);
+            IGNORE_EXCEPTIONS(it->second.process(resultBuilder));
+            it = mReturnCallbacks.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    if (mRemovedPeerCallback) {
+        // Notify about the deletion
+        mRemovedPeerCallback(peerIt->peerID, peerIt->socketPtr->getFD());
+    }
+
+    mPeerInfo.erase(peerIt);
+}
+
+void Processor::handleLostConnection(const FileDescriptor fd)
+{
+    Lock lock(mStateMutex);
+    auto peerIt = getPeerInfoIterator(fd);
+    removePeerInternal(peerIt,
+                       std::make_exception_ptr(IPCPeerDisconnectedException()));
+}
+
+void Processor::handleInput(const FileDescriptor fd)
+{
+    LOGS(mLogPrefix + "Processor handleInput fd: " << fd);
+
+    Lock lock(mStateMutex);
+
+    auto peerIt = getPeerInfoIterator(fd);
+
+    if (peerIt == mPeerInfo.end()) {
+        LOGE(mLogPrefix + "No peer for fd: " << fd);
+        return;
+    }
+
+    MessageHeader hdr;
+    {
+        try {
+            // Read information about the incoming data
+            Socket& socket = *peerIt->socketPtr;
+            cargo::loadFromFD<MessageHeader>(socket.getFD(), hdr);
+        } catch (const cargo::CargoException& e) {
+            LOGE(mLogPrefix + "Error during reading the socket");
+            removePeerInternal(peerIt,
+                               std::make_exception_ptr(IPCNaughtyPeerException()));
+            return;
+        }
+
+        if (hdr.methodID == RETURN_METHOD_ID) {
+            onReturnValue(peerIt, hdr.messageID);
+            return;
+        } else {
+            if (mMethodsCallbacks.count(hdr.methodID)) {
+                // Method
+                std::shared_ptr<MethodHandlers> methodCallbacks = mMethodsCallbacks.at(hdr.methodID);
+                onRemoteMethod(peerIt, hdr.methodID, hdr.messageID, methodCallbacks);
+            } else if (mSignalsCallbacks.count(hdr.methodID)) {
+                // Signal
+                std::shared_ptr<SignalHandlers> signalCallbacks = mSignalsCallbacks.at(hdr.methodID);
+                onRemoteSignal(peerIt, hdr.methodID, hdr.messageID, signalCallbacks);
+
+            } else {
+                LOGW(mLogPrefix + "No method or signal callback for methodID: " << hdr.methodID);
+                removePeerInternal(peerIt,
+                                   std::make_exception_ptr(IPCNaughtyPeerException()));
+            }
+        }
+    }
+}
+
+ipc::HandlerExitCode Processor::onNewSignals(const PeerID& peerID, std::shared_ptr<RegisterSignalsProtocolMessage>& data)
+{
+    LOGS(mLogPrefix + "Processor onNewSignals peerID: " << shortenPeerID(peerID));
+
+    for (const MethodID methodID : data->ids) {
+        mSignalsPeers[methodID].push_back(peerID);
+    }
+
+    return ipc::HandlerExitCode::SUCCESS;
+}
+
+ipc::HandlerExitCode Processor::onErrorSignal(const PeerID&, std::shared_ptr<ErrorProtocolMessage>& data)
+{
+    LOGS(mLogPrefix + "Processor onErrorSignal messageID: " << shortenMessageID(data->messageID));
+
+    // If there is no return callback an out_of_range error will be thrown and peer will be removed
+    ReturnCallbacks returnCallbacks = std::move(mReturnCallbacks.at(data->messageID));
+    mReturnCallbacks.erase(data->messageID);
+
+    ResultBuilder resultBuilder(std::make_exception_ptr(IPCUserException(data->code, data->message)));
+    IGNORE_EXCEPTIONS(returnCallbacks.process(resultBuilder));
+
+    return ipc::HandlerExitCode::SUCCESS;
+}
+
+void Processor::onReturnValue(Peers::iterator& peerIt,
+                              const MessageID& messageID)
+{
+    LOGS(mLogPrefix + "Processor onReturnValue messageID: " << shortenMessageID(messageID));
+
+    ReturnCallbacks returnCallbacks;
+    try {
+        LOGT(mLogPrefix + "Getting the return callback");
+        returnCallbacks = std::move(mReturnCallbacks.at(messageID));
+        mReturnCallbacks.erase(messageID);
+    } catch (const std::out_of_range&) {
+        LOGW(mLogPrefix + "No return callback for messageID: " << shortenMessageID(messageID));
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCNaughtyPeerException()));
+        return;
+    }
+
+    std::shared_ptr<void> data;
+    try {
+        LOGT(mLogPrefix + "Parsing incoming return data");
+        data = returnCallbacks.parse(peerIt->socketPtr->getFD());
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Exception during parsing: " << e.what());
+        ResultBuilder resultBuilder(std::make_exception_ptr(IPCParsingException()));
+        IGNORE_EXCEPTIONS(returnCallbacks.process(resultBuilder));
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCParsingException()));
+        return;
+    }
+
+    ResultBuilder resultBuilder(data);
+    IGNORE_EXCEPTIONS(returnCallbacks.process(resultBuilder));
+}
+
+void Processor::onRemoteSignal(Peers::iterator& peerIt,
+                               __attribute__((unused)) const MethodID methodID,
+                               __attribute__((unused)) const MessageID& messageID,
+                               std::shared_ptr<SignalHandlers> signalCallbacks)
+{
+    LOGS(mLogPrefix + "Processor onRemoteSignal; methodID: " << methodID
+                                           << " messageID: " << shortenMessageID(messageID));
+
+    std::shared_ptr<void> data;
+    try {
+        LOGT(mLogPrefix + "Parsing incoming data");
+        data = signalCallbacks->parse(peerIt->socketPtr->getFD());
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Exception during parsing: " << e.what());
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCParsingException()));
+        return;
+    }
+
+    try {
+        auto leaveHandler = signalCallbacks->signal(peerIt->peerID, data);
+
+        if(leaveHandler == HandlerExitCode::REMOVE_HANDLER) {
+            LOGI("Signal handler requested deletion (returned REMOVE_HANDLER): " << methodID);
+            mSignalsCallbacks.erase(methodID);
+        }
+    } catch (const IPCUserException& e) {
+        LOGW("Discarded user's exception");
+        return;
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Exception in method handler: " << e.what());
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCNaughtyPeerException()));
+        return;
+    }
+}
+
+void Processor::onRemoteMethod(Peers::iterator& peerIt,
+                               const MethodID methodID,
+                               const MessageID& messageID,
+                               std::shared_ptr<MethodHandlers> methodCallbacks)
+{
+    LOGS(mLogPrefix + "Processor onRemoteMethod; methodID: " << methodID
+                                           << " messageID: " << shortenMessageID(messageID));
+
+    std::shared_ptr<void> data;
+    try {
+        LOGT(mLogPrefix + "Parsing incoming data");
+        data = methodCallbacks->parse(peerIt->socketPtr->getFD());
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Exception during parsing: " << e.what());
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCParsingException()));
+        return;
+    }
+
+    LOGT(mLogPrefix + "Process callback for methodID: " << methodID
+                                     << "; messageID: " << shortenMessageID(messageID));
+    try {
+        auto methodResultPtr = std::make_shared<MethodResult>(*this, methodID, messageID, peerIt->peerID);
+        auto leaveHandler = methodCallbacks->method(peerIt->peerID, data, methodResultPtr);
+
+        if(leaveHandler == HandlerExitCode::SUCCESS) {
+            // Leave the handler
+            return;
+        }
+
+        LOGI("Method handler requested deletion (returned REMOVE_HANDLER): " << methodID);
+        if(!methodResultPtr.unique()) {
+            LOGE("There is a MethodResult object referencing this method, can't delete: " << methodID);
+        }
+
+        // We can't just remove the callback here,
+        // MethodResult called sendResult/sendError/sendVoid and the request can still be in the queue.
+        // TODO: There can be another request for this method
+        auto requestPtr = std::make_shared<RemoveMethodRequest>(methodID);
+        mRequestQueue.pushBack(Event::REMOVE_METHOD, requestPtr);
+    } catch (const IPCUserException& e) {
+        LOGW("User's exception");
+        sendError(peerIt->peerID, messageID, e.getCode(), e.what());
+        return;
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Exception in method handler: " << e.what());
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCNaughtyPeerException()));
+        return;
+    }
+}
+
+void Processor::handleEvent()
+{
+    LOGS(mLogPrefix + "Processor handleEvent");
+
+    Lock lock(mStateMutex);
+
+    auto request = mRequestQueue.pop();
+    LOGD(mLogPrefix + "Got: " << request.requestID);
+
+    switch (request.requestID) {
+    case Event::METHOD:
+        onMethodRequest(*request.get<MethodRequest>());
+        break;
+    case Event::SIGNAL:
+        onSignalRequest(*request.get<SignalRequest>());
+        break;
+    case Event::ADD_PEER:
+        onAddPeerRequest(*request.get<AddPeerRequest>());
+        break;
+    case Event::REMOVE_PEER:
+        onRemovePeerRequest(*request.get<RemovePeerRequest>());
+        break;
+    case Event::SEND_RESULT:
+        onSendResultRequest(*request.get<SendResultRequest>());
+        break;
+    case Event::REMOVE_METHOD:
+        onRemoveMethodRequest(*request.get<RemoveMethodRequest>());
+        break;
+    case Event::FINISH:
+        onFinishRequest(*request.get<FinishRequest>());
+        break;
+    }
+}
+
+void Processor::onMethodRequest(MethodRequest& request)
+{
+    LOGS(mLogPrefix + "Processor onMethodRequest");
+
+    auto peerIt = getPeerInfoIterator(request.peerID);
+
+    if (peerIt == mPeerInfo.end()) {
+        LOGE(mLogPrefix + "Peer disconnected. No user with a peerID: "
+             << shortenPeerID(request.peerID));
+
+        // Pass the error to the processing callback
+        ResultBuilder resultBuilder(std::make_exception_ptr(IPCPeerDisconnectedException()));
+        IGNORE_EXCEPTIONS(request.process(resultBuilder));
+
+        return;
+    }
+
+    if (mReturnCallbacks.count(request.messageID) != 0) {
+        LOGE(mLogPrefix + "There already was a return callback for messageID: "
+             << shortenMessageID(request.messageID));
+    }
+    mReturnCallbacks[request.messageID] = ReturnCallbacks(peerIt->peerID,
+                                                          std::move(request.parse),
+                                                          std::move(request.process));
+
+    MessageHeader hdr;
+    try {
+        // Send the call with the socket
+        Socket& socket = *peerIt->socketPtr;
+        hdr.methodID = request.methodID;
+        hdr.messageID = request.messageID;
+        cargo::saveToFD<MessageHeader>(socket.getFD(), hdr);
+        LOGT(mLogPrefix + "Serializing the message");
+        request.serialize(socket.getFD(), request.data);
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Error during sending a method: " << e.what());
+
+        // Inform about the error
+        ResultBuilder resultBuilder(std::make_exception_ptr(IPCSerializationException()));
+        IGNORE_EXCEPTIONS(mReturnCallbacks[request.messageID].process(resultBuilder));
+
+
+        mReturnCallbacks.erase(request.messageID);
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCSerializationException()));
+    }
+}
+
+void Processor::onSignalRequest(SignalRequest& request)
+{
+    LOGS(mLogPrefix + "Processor onSignalRequest");
+
+    auto peerIt = getPeerInfoIterator(request.peerID);
+
+    if (peerIt == mPeerInfo.end()) {
+        LOGE(mLogPrefix + "Peer disconnected. No user for peerID: "
+             << shortenPeerID(request.peerID));
+        return;
+    }
+
+    MessageHeader hdr;
+    try {
+        // Send the call with the socket
+        Socket& socket = *peerIt->socketPtr;
+        hdr.methodID = request.methodID;
+        hdr.messageID = request.messageID;
+        cargo::saveToFD<MessageHeader>(socket.getFD(), hdr);
+        request.serialize(socket.getFD(), request.data);
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Error during sending a signal: " << e.what());
+
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCSerializationException()));
+    }
+}
+
+void Processor::onAddPeerRequest(AddPeerRequest& request)
+{
+    LOGS(mLogPrefix + "Processor onAddPeerRequest");
+
+    if (mPeerInfo.size() > mMaxNumberOfPeers) {
+        LOGE(mLogPrefix + "There are too many peers. I don't accept the connection with "
+             << shortenPeerID(request.peerID));
+        return;
+    }
+
+    if (getPeerInfoIterator(request.peerID) != mPeerInfo.end()) {
+        LOGE(mLogPrefix + "There already was a socket for peerID: "
+             << shortenPeerID(request.peerID));
+        return;
+    }
+
+    PeerInfo peerInfo(request.peerID, request.socketPtr);
+    mPeerInfo.push_back(std::move(peerInfo));
+
+
+    // Sending handled signals
+    std::vector<MethodID> ids;
+    for (const auto kv : mSignalsCallbacks) {
+        ids.push_back(kv.first);
+    }
+    auto data = std::make_shared<RegisterSignalsProtocolMessage>(ids);
+    signalInternal<RegisterSignalsProtocolMessage>(REGISTER_SIGNAL_METHOD_ID,
+                                                   request.peerID,
+                                                   data);
+
+    if (mNewPeerCallback) {
+        // Notify about the new user.
+        LOGT(mLogPrefix + "Calling NewPeerCallback");
+        mNewPeerCallback(request.peerID, request.socketPtr->getFD());
+    }
+
+    LOGI(mLogPrefix + "New peerID: " << shortenPeerID(request.peerID));
+}
+
+void Processor::onRemovePeerRequest(RemovePeerRequest& request)
+{
+    LOGS(mLogPrefix + "Processor onRemovePeer");
+
+    removePeerInternal(getPeerInfoIterator(request.peerID),
+                       std::make_exception_ptr(IPCRemovedPeerException()));
+
+    request.conditionPtr->notify_all();
+}
+
+void Processor::onSendResultRequest(SendResultRequest& request)
+{
+    LOGS(mLogPrefix + "Processor onMethodRequest");
+
+    auto peerIt = getPeerInfoIterator(request.peerID);
+
+    if (peerIt == mPeerInfo.end()) {
+        LOGE(mLogPrefix + "Peer disconnected, no result is sent. No user with a peerID: "
+             << shortenPeerID(request.peerID));
+        return;
+    }
+
+    std::shared_ptr<MethodHandlers> methodCallbacks;
+    try {
+        methodCallbacks = mMethodsCallbacks.at(request.methodID);
+    } catch (const std::out_of_range&) {
+        LOGW(mLogPrefix + "No method, might have been deleted. methodID: " << request.methodID);
+        return;
+    }
+
+    MessageHeader hdr;
+    try {
+        // Send the call with the socket
+        Socket& socket = *peerIt->socketPtr;
+        hdr.methodID = RETURN_METHOD_ID;
+        hdr.messageID = request.messageID;
+        cargo::saveToFD<MessageHeader>(socket.getFD(), hdr);
+        LOGT(mLogPrefix + "Serializing the message");
+        methodCallbacks->serialize(socket.getFD(), request.data);
+    } catch (const std::exception& e) {
+        LOGE(mLogPrefix + "Error during sending a method: " << e.what());
+
+        // Inform about the error
+        ResultBuilder resultBuilder(std::make_exception_ptr(IPCSerializationException()));
+        IGNORE_EXCEPTIONS(mReturnCallbacks[request.messageID].process(resultBuilder));
+
+
+        mReturnCallbacks.erase(request.messageID);
+        removePeerInternal(peerIt,
+                           std::make_exception_ptr(IPCSerializationException()));
+    }
+}
+
+void Processor::onRemoveMethodRequest(RemoveMethodRequest& request)
+{
+    mMethodsCallbacks.erase(request.methodID);
+}
+
+void Processor::onFinishRequest(FinishRequest& requestFinisher)
+{
+    LOGS(mLogPrefix + "Processor onFinishRequest");
+
+    // Clean the mRequestQueue
+    std::vector<std::shared_ptr<FinishRequest>> remainingFinishRequests;
+    while (!mRequestQueue.isEmpty()) {
+        auto request = mRequestQueue.pop();
+        LOGE(mLogPrefix + "Got: " << request.requestID << " after FINISH");
+
+        switch (request.requestID) {
+        case Event::METHOD: {
+            auto requestPtr = request.get<MethodRequest>();
+            ResultBuilder resultBuilder(std::make_exception_ptr(IPCClosingException()));
+            IGNORE_EXCEPTIONS(requestPtr->process(resultBuilder));
+            break;
+        }
+        case Event::REMOVE_PEER: {
+            onRemovePeerRequest(*request.get<RemovePeerRequest>());
+            break;
+        }
+        case Event::SEND_RESULT: {
+            onSendResultRequest(*request.get<SendResultRequest>());
+            break;
+        }
+        case Event::FINISH:
+            remainingFinishRequests.push_back(request.get<FinishRequest>());
+            break;
+        case Event::SIGNAL:
+        case Event::ADD_PEER:
+        case Event::REMOVE_METHOD:
+            break;
+        }
+    }
+
+    // Close peers
+    while (!mPeerInfo.empty()) {
+        removePeerInternal(--mPeerInfo.end(),
+                           std::make_exception_ptr(IPCClosingException()));
+    }
+
+    mEventPoll.removeFD(mRequestQueue.getFD());
+    mIsRunning = false;
+    requestFinisher.conditionPtr->notify_all();
+    for(auto & c : remainingFinishRequests) {
+        c->conditionPtr->notify_all();
+    }
+}
+
+std::ostream& operator<<(std::ostream& os, const Processor::Event& event)
+{
+    switch (event) {
+
+    case Processor::Event::FINISH: {
+        os << "Event::FINISH";
+        break;
+    }
+
+    case Processor::Event::METHOD: {
+        os << "Event::METHOD";
+        break;
+    }
+
+    case Processor::Event::SIGNAL: {
+        os << "Event::SIGNAL";
+        break;
+    }
+
+    case Processor::Event::ADD_PEER: {
+        os << "Event::ADD_PEER";
+        break;
+    }
+
+    case Processor::Event::REMOVE_PEER: {
+        os << "Event::REMOVE_PEER";
+        break;
+    }
+
+    case Processor::Event::REMOVE_METHOD: {
+        os << "Event::REMOVE_METHOD";
+        break;
+    }
+
+    case Processor::Event::SEND_RESULT: {
+        os << "Event::SEND_RESULT";
+        break;
+    }
+    }
+
+    return os;
+}
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/internals/processor.hpp b/libs/cargo-ipc/internals/processor.hpp
new file mode 100644 (file)
index 0000000..1fb68f2
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Data and event processing thread
+ */
+
+#ifndef CARGO_IPC_INTERNALS_PROCESSOR_HPP
+#define CARGO_IPC_INTERNALS_PROCESSOR_HPP
+
+#include "cargo-ipc/internals/result-builder.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+#include "cargo-ipc/internals/request-queue.hpp"
+#include "cargo-ipc/internals/method-request.hpp"
+#include "cargo-ipc/internals/signal-request.hpp"
+#include "cargo-ipc/internals/add-peer-request.hpp"
+#include "cargo-ipc/internals/remove-peer-request.hpp"
+#include "cargo-ipc/internals/send-result-request.hpp"
+#include "cargo-ipc/internals/remove-method-request.hpp"
+#include "cargo-ipc/internals/finish-request.hpp"
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include "cargo-ipc/exception.hpp"
+#include "cargo-ipc/method-result.hpp"
+#include "cargo-ipc/types.hpp"
+#include "cargo-fd/cargo-fd.hpp"
+#include "cargo/fields.hpp"
+#include "logger/logger.hpp"
+#include "logger/logger-scope.hpp"
+
+#include <ostream>
+#include <condition_variable>
+#include <mutex>
+#include <chrono>
+#include <vector>
+#include <thread>
+#include <string>
+#include <list>
+#include <functional>
+#include <unordered_map>
+#include <utility>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+const unsigned int DEFAULT_MAX_NUMBER_OF_PEERS = 500;
+/**
+* This class wraps communication via UX sockets
+*
+* It's intended to be used both in Client and Service classes.
+* It uses a serialization mechanism from Config.
+* Library user will only have to pass the types that each call will send and receive
+*
+* Message format:
+* - MethodID  - probably casted enum.
+*               MethodID == std::numeric_limits<MethodID>::max() is reserved for return messages
+* - MessageID - unique id of a message exchange sent by this object instance. Used to identify reply messages.
+* - Rest: The data written in a callback. One type per method.ReturnCallbacks
+*
+* TODO: API for removing signals
+* TODO: Implement HandlerStore class for storing/handling handlers. This will simplify Processor.
+* TODO: Implement CallbackStore class for storing/handling ReturnCallbacks. This will simplify Processor.
+* TODO: Adding requests after stop() should fail
+*
+*/
+class Processor {
+private:
+    enum class Event {
+        FINISH,      // Shutdown request
+        METHOD,      // New method call in the queue
+        SIGNAL,      // New signal call in the queue
+        ADD_PEER,    // New peer in the queue
+        REMOVE_PEER, // Remove peer
+        SEND_RESULT,  // Send the result of a method's call
+        REMOVE_METHOD  // Remove method handler
+    };
+
+public:
+    friend std::ostream& operator<<(std::ostream& os, const Processor::Event& event);
+
+    /**
+     * Used to indicate a message with the return value.
+     */
+    static const MethodID RETURN_METHOD_ID;
+
+    /**
+     * Indicates an Processor's internal request/broadcast to register a Signal
+     */
+    static const MethodID REGISTER_SIGNAL_METHOD_ID;
+
+    /**
+    * Error return message
+    */
+    static const MethodID ERROR_METHOD_ID;
+
+    /**
+     * Constructs the Processor, but doesn't start it.
+     * The object is ready to add methods.
+     *
+     * @param eventPoll event poll
+     * @param logName log name
+     * @param newPeerCallback called when a new peer arrives
+     * @param removedPeerCallback called when the Processor stops listening for this peer
+     * @param maxNumberOfPeers maximal number of peers
+     */
+    Processor(epoll::EventPoll& eventPoll,
+              const std::string& logName = "",
+              const PeerCallback& newPeerCallback = nullptr,
+              const PeerCallback& removedPeerCallback = nullptr,
+              const unsigned int maxNumberOfPeers = DEFAULT_MAX_NUMBER_OF_PEERS);
+    ~Processor();
+
+    Processor(const Processor&) = delete;
+    Processor(Processor&&) = delete;
+    Processor& operator=(const Processor&) = delete;
+
+
+    /**
+     * Start processing.
+     */
+    void start();
+
+    /**
+     * @return is processor running
+     */
+    bool isStarted();
+
+    /**
+     * Stops the processing thread.
+     * No incoming data will be handled after.
+     *
+     * @param wait does it block waiting for all internals to stop
+     */
+    void stop(bool wait);
+
+    /**
+     * Set the callback called for each new connection to a peer
+     *
+     * @param newPeerCallback the callback
+     */
+    void setNewPeerCallback(const PeerCallback& newPeerCallback);
+
+    /**
+     * Set the callback called when connection to a peer is lost
+     *
+     * @param removedPeerCallback the callback
+     */
+    void setRemovedPeerCallback(const PeerCallback& removedPeerCallback);
+
+    /**
+     * From now on socket is owned by the Processor object.
+     * Calls the newPeerCallback.
+     *
+     * @param socketPtr pointer to the new socket
+     * @return peerID of the new user
+     */
+    PeerID addPeer(const std::shared_ptr<Socket>& socketPtr);
+
+    /**
+     * Saves the callbacks connected to the method id.
+     * When a message with the given method id is received,
+     * the data will be passed to the serialization callback through file descriptor.
+     *
+     * Then the process callback will be called with the parsed data.
+     *
+     * @param methodID API dependent id of the method
+     * @param process data processing callback
+     * @tparam SentDataType data type to send
+     * @tparam ReceivedDataType data type to receive
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    void setMethodHandler(const MethodID methodID,
+                          const typename MethodHandler<SentDataType, ReceivedDataType>::type& process);
+
+    /**
+     * Saves the callbacks connected to the method id.
+     * When a message with the given method id is received,
+     * the data will be passed to the serialization callback through file descriptor.
+     *
+     * Then the process callback will be called with the parsed data.
+     * There is no return data to send back.
+     *
+     * Adding signal sends a registering message to all peers
+     *
+     * @param methodID API dependent id of the method
+     * @param process data processing callback
+     * @tparam ReceivedDataType data type to receive
+     */
+    template<typename ReceivedDataType>
+    void setSignalHandler(const MethodID methodID,
+                          const typename SignalHandler<ReceivedDataType>::type& process);
+
+    /**
+     * Send result of the method.
+     * Used for asynchronous communication, only internally.
+     *
+     * @param methodID API dependent id of the method
+     * @param peerID id of the peer
+     * @param messageID id of the message to which it replies
+     * @param data data to send
+     */
+    void sendResult(const MethodID methodID,
+                    const PeerID& peerID,
+                    const MessageID& messageID,
+                    const std::shared_ptr<void>& data);
+
+    /**
+     * Send error result of the method
+     *
+     * @param peerID id of the peer
+     * @param messageID id of the message to which it replies
+     * @param errorCode code of the error
+     * @param message description of the error
+     */
+    void sendError(const PeerID& peerID,
+                   const MessageID& messageID,
+                   const int errorCode,
+                   const std::string& message);
+
+    /**
+     * Indicate that the method handler finished
+     *
+     * @param methodID API dependent id of the method
+     * @param peerID id of the peer
+     * @param messageID id of the message to which it replies
+     */
+    void sendVoid(const MethodID methodID,
+                  const PeerID& peerID,
+                  const MessageID& messageID);
+
+    /**
+     * Removes the callback associated with specific method id.
+     *
+     * @param methodID              API dependent id of the method
+     * @see setMethodHandler()
+     * @see setSignalHandler()
+     */
+    void removeMethod(const MethodID methodID);
+
+    /**
+     * @param methodID MethodID defined in the user's API
+     * @return is methodID handled by a signal or method
+     */
+    bool isHandled(const MethodID methodID);
+
+    /**
+     * Synchronous method call.
+     *
+     * @param methodID          API dependent id of the method
+     * @param peerID            id of the peer
+     * @param data              data to send
+     * @param timeoutMS         optional, how long to wait for the return value before throw (milliseconds, default: 5000)
+     * @tparam SentDataType     data type to send
+     * @tparam ReceivedDataType data type to receive
+     * @return call result data
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    std::shared_ptr<ReceivedDataType> callSync(const MethodID methodID,
+                                               const PeerID& peerID,
+                                               const std::shared_ptr<SentDataType>& data,
+                                               unsigned int timeoutMS = 5000);
+
+    /**
+     * Asynchronous method call
+     *
+     * @param methodID API dependent id of the method
+     * @param peerID id of the peer
+     * @param data data to sent
+     * @param process callback processing the return data
+     * @tparam SentDataType data type to send
+     * @tparam ReceivedDataType data type to receive
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    MessageID callAsync(const MethodID methodID,
+                        const PeerID& peerID,
+                        const std::shared_ptr<SentDataType>& data,
+                        const typename ResultHandler<ReceivedDataType>::type& process);
+
+    /**
+     * The same as callAsync, but not blocking on the state mutex.
+     *
+     * @param methodID API dependent id of the method
+     * @param peerID id of the peer
+     * @param data data to sent
+     * @param process callback processing the return data
+     * @tparam SentDataType data type to send
+     * @tparam ReceivedDataType data type to receive
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    MessageID callAsyncNonBlock(const MethodID methodID,
+                                const PeerID& peerID,
+                                const std::shared_ptr<SentDataType>& data,
+                                const typename ResultHandler<ReceivedDataType>::type& process);
+    /**
+     * Send a signal to the peer.
+     * There is no return value from the peer
+     * Sends any data only if a peer registered this a signal
+     *
+     * @param methodID API dependent id of the method
+     * @param data data to sent
+     * @tparam SentDataType data type to send
+     */
+    template<typename SentDataType>
+    void signal(const MethodID methodID,
+                const std::shared_ptr<SentDataType>& data);
+
+    /**
+     * Removes one peer.
+     * Handler used in external polling.
+     *
+     * @param fd file description identifying the peer
+     */
+    void handleLostConnection(const FileDescriptor fd);
+
+    /**
+     * Handles input from one peer.
+     * Handler used in external polling.
+     *
+     * @param fd file description identifying the peer
+     */
+    void handleInput(const FileDescriptor fd);
+
+    /**
+     * Handle one event from the internal event's queue
+     */
+    void handleEvent();
+
+    /**
+     * @return file descriptor for the internal event's queue
+     */
+    FileDescriptor getEventFD();
+
+private:
+    typedef std::unique_lock<std::mutex> Lock;
+    typedef RequestQueue<Event>::Request Request;
+
+    struct EmptyData {
+        CARGO_REGISTER_EMPTY
+    };
+
+    struct MessageHeader {
+        MethodID methodID;
+        MessageID messageID;
+
+        CARGO_REGISTER
+        (
+            methodID,
+            messageID
+        )
+    };
+
+    struct RegisterSignalsProtocolMessage {
+        RegisterSignalsProtocolMessage() = default;
+        explicit RegisterSignalsProtocolMessage(const std::vector<MethodID>& ids)
+            : ids(ids) {}
+
+        std::vector<MethodID> ids;
+
+        CARGO_REGISTER
+        (
+            ids
+        )
+    };
+
+    struct ErrorProtocolMessage {
+        ErrorProtocolMessage() = default;
+        ErrorProtocolMessage(const MessageID& messageID, const int code, const std::string& message)
+            : messageID(messageID), code(code), message(message) {}
+
+        MessageID messageID;
+        int code;
+        std::string message;
+
+        CARGO_REGISTER
+        (
+            messageID,
+            code,
+            message
+        )
+    };
+
+    struct MethodHandlers {
+        MethodHandlers(const MethodHandlers& other) = delete;
+        MethodHandlers& operator=(const MethodHandlers&) = delete;
+        MethodHandlers() = default;
+        MethodHandlers(MethodHandlers&&) = default;
+        MethodHandlers& operator=(MethodHandlers &&) = default;
+
+        SerializeCallback serialize;
+        ParseCallback parse;
+        MethodHandler<void, void>::type method;
+    };
+
+    struct SignalHandlers {
+        SignalHandlers(const SignalHandlers& other) = delete;
+        SignalHandlers& operator=(const SignalHandlers&) = delete;
+        SignalHandlers() = default;
+        SignalHandlers(SignalHandlers&&) = default;
+        SignalHandlers& operator=(SignalHandlers &&) = default;
+
+        ParseCallback parse;
+        SignalHandler<void>::type signal;
+    };
+
+    struct ReturnCallbacks {
+        ReturnCallbacks(const ReturnCallbacks& other) = delete;
+        ReturnCallbacks& operator=(const ReturnCallbacks&) = delete;
+        ReturnCallbacks() = default;
+        ReturnCallbacks(ReturnCallbacks&&) = default;
+        ReturnCallbacks& operator=(ReturnCallbacks &&) = default;
+
+        ReturnCallbacks(PeerID peerID, const ParseCallback& parse, const ResultBuilderHandler& process)
+            : peerID(peerID), parse(parse), process(process) {}
+
+        PeerID peerID;
+        ParseCallback parse;
+        ResultBuilderHandler process;
+    };
+
+    struct PeerInfo {
+        PeerInfo(const PeerInfo& other) = delete;
+        PeerInfo& operator=(const PeerInfo&) = delete;
+        PeerInfo() = delete;
+
+        PeerInfo(PeerInfo&&) = default;
+        PeerInfo& operator=(PeerInfo &&) = default;
+
+        PeerInfo(PeerID peerID, const std::shared_ptr<Socket>& socketPtr)
+            : peerID(peerID), socketPtr(socketPtr) {}
+
+        PeerID peerID;
+        std::shared_ptr<Socket> socketPtr;
+    };
+
+    epoll::EventPoll& mEventPoll;
+
+    typedef std::vector<PeerInfo> Peers;
+
+    std::string mLogPrefix;
+
+    RequestQueue<Event> mRequestQueue;
+
+    bool mIsRunning;
+
+    std::unordered_map<MethodID, std::shared_ptr<MethodHandlers>> mMethodsCallbacks;
+    std::unordered_map<MethodID, std::shared_ptr<SignalHandlers>> mSignalsCallbacks;
+    std::unordered_map<MethodID, std::list<PeerID>> mSignalsPeers;
+
+    Peers mPeerInfo;
+
+    std::unordered_map<MessageID, ReturnCallbacks> mReturnCallbacks;
+
+    // Mutex for modifying any internal data
+    std::mutex mStateMutex;
+
+    PeerCallback mNewPeerCallback;
+    PeerCallback mRemovedPeerCallback;
+
+    unsigned int mMaxNumberOfPeers;
+
+    template<typename SentDataType, typename ReceivedDataType>
+    void setMethodHandlerInternal(const MethodID methodID,
+                                  const typename MethodHandler<SentDataType, ReceivedDataType>::type& process);
+
+    template<typename ReceivedDataType>
+    void setSignalHandlerInternal(const MethodID methodID,
+                                  const typename SignalHandler<ReceivedDataType>::type& handler);
+
+    template<typename SentDataType>
+    void signalInternal(const MethodID methodID,
+                        const PeerID& peerID,
+                        const std::shared_ptr<SentDataType>& data);
+
+    // Request handlers
+    void onMethodRequest(MethodRequest& request);
+    void onSignalRequest(SignalRequest& request);
+    void onAddPeerRequest(AddPeerRequest& request);
+    void onRemovePeerRequest(RemovePeerRequest& request);
+    void onSendResultRequest(SendResultRequest& request);
+    void onRemoveMethodRequest(RemoveMethodRequest& request);
+    void onFinishRequest(FinishRequest& request);
+
+    void onReturnValue(Peers::iterator& peerIt,
+                       const MessageID& messageID);
+    void onRemoteMethod(Peers::iterator& peerIt,
+                        const MethodID methodID,
+                        const MessageID& messageID,
+                        std::shared_ptr<MethodHandlers> methodCallbacks);
+    void onRemoteSignal(Peers::iterator& peerIt,
+                        const MethodID methodID,
+                        const MessageID& messageID,
+                        std::shared_ptr<SignalHandlers> signalCallbacks);
+
+    void removePeerInternal(Peers::iterator peerIt,
+                            const std::exception_ptr& exceptionPtr);
+    void removePeerSyncInternal(const PeerID& peerID, Lock& lock);
+
+    HandlerExitCode onNewSignals(const PeerID& peerID,
+                                 std::shared_ptr<RegisterSignalsProtocolMessage>& data);
+
+    HandlerExitCode onErrorSignal(const PeerID& peerID,
+                                  std::shared_ptr<ErrorProtocolMessage>& data);
+
+    Peers::iterator getPeerInfoIterator(const FileDescriptor fd);
+    Peers::iterator getPeerInfoIterator(const PeerID& peerID);
+
+};
+
+template<typename SentDataType, typename ReceivedDataType>
+void Processor::setMethodHandlerInternal(const MethodID methodID,
+                                         const typename MethodHandler<SentDataType, ReceivedDataType>::type& method)
+{
+    MethodHandlers methodCall;
+
+    methodCall.parse = [](const int fd)->std::shared_ptr<void> {
+        std::shared_ptr<ReceivedDataType> data(new ReceivedDataType());
+        cargo::loadFromFD<ReceivedDataType>(fd, *data);
+        return data;
+    };
+
+    methodCall.serialize = [](const int fd, std::shared_ptr<void>& data)->void {
+        cargo::saveToFD<SentDataType>(fd, *std::static_pointer_cast<SentDataType>(data));
+    };
+
+    methodCall.method = [method](const PeerID peerID, std::shared_ptr<void>& data, MethodResult::Pointer && methodResult) {
+        std::shared_ptr<ReceivedDataType> tmpData = std::static_pointer_cast<ReceivedDataType>(data);
+        return method(peerID, tmpData, std::forward<MethodResult::Pointer>(methodResult));
+    };
+
+    mMethodsCallbacks[methodID] = std::make_shared<MethodHandlers>(std::move(methodCall));
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+void Processor::setMethodHandler(const MethodID methodID,
+                                 const typename MethodHandler<SentDataType, ReceivedDataType>::type& method)
+{
+    if (methodID == RETURN_METHOD_ID || methodID == REGISTER_SIGNAL_METHOD_ID) {
+        LOGE(mLogPrefix + "Forbidden methodID: " << methodID);
+        throw IPCException("Forbidden methodID: " + std::to_string(methodID));
+    }
+
+    {
+        Lock lock(mStateMutex);
+
+        if (mSignalsCallbacks.count(methodID)) {
+            LOGE(mLogPrefix + "MethodID used by a signal: " << methodID);
+            throw IPCException("MethodID used by a signal: " + std::to_string(methodID));
+        }
+
+        setMethodHandlerInternal<SentDataType, ReceivedDataType>(methodID, method);
+    }
+
+}
+
+template<typename ReceivedDataType>
+void Processor::setSignalHandlerInternal(const MethodID methodID,
+                                         const typename SignalHandler<ReceivedDataType>::type& handler)
+{
+    SignalHandlers signalCall;
+
+    signalCall.parse = [](const int fd)->std::shared_ptr<void> {
+        std::shared_ptr<ReceivedDataType> dataToFill(new ReceivedDataType());
+        cargo::loadFromFD<ReceivedDataType>(fd, *dataToFill);
+        return dataToFill;
+    };
+
+    signalCall.signal = [handler](const PeerID peerID, std::shared_ptr<void>& dataReceived) {
+        std::shared_ptr<ReceivedDataType> tmpData = std::static_pointer_cast<ReceivedDataType>(dataReceived);
+        return handler(peerID, tmpData);
+    };
+
+    mSignalsCallbacks[methodID] = std::make_shared<SignalHandlers>(std::move(signalCall));
+}
+
+template<typename ReceivedDataType>
+void Processor::setSignalHandler(const MethodID methodID,
+                                 const typename SignalHandler<ReceivedDataType>::type& handler)
+{
+    if (methodID == RETURN_METHOD_ID || methodID == REGISTER_SIGNAL_METHOD_ID) {
+        LOGE(mLogPrefix + "Forbidden methodID: " << methodID);
+        throw IPCException("Forbidden methodID: " + std::to_string(methodID));
+    }
+
+    std::shared_ptr<RegisterSignalsProtocolMessage> data;
+
+    {
+        Lock lock(mStateMutex);
+
+        // Andd the signal handler:
+        if (mMethodsCallbacks.count(methodID)) {
+            LOGE(mLogPrefix + "MethodID used by a method: " << methodID);
+            throw IPCException("MethodID used by a method: " + std::to_string(methodID));
+        }
+
+        setSignalHandlerInternal<ReceivedDataType>(methodID, handler);
+
+        // Broadcast the new signal:
+        std::vector<MethodID> ids {methodID};
+        data = std::make_shared<RegisterSignalsProtocolMessage>(ids);
+
+        for (const PeerInfo& peerInfo : mPeerInfo) {
+            signalInternal<RegisterSignalsProtocolMessage>(REGISTER_SIGNAL_METHOD_ID,
+                                                           peerInfo.peerID,
+                                                           data);
+        }
+    }
+}
+
+
+template<typename SentDataType, typename ReceivedDataType>
+MessageID Processor::callAsync(const MethodID methodID,
+                               const PeerID& peerID,
+                               const std::shared_ptr<SentDataType>& data,
+                               const typename ResultHandler<ReceivedDataType>::type& process)
+{
+    Lock lock(mStateMutex);
+    return callAsyncNonBlock<SentDataType, ReceivedDataType>(methodID, peerID, data, process);
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+MessageID Processor::callAsyncNonBlock(const MethodID methodID,
+                                       const PeerID& peerID,
+                                       const std::shared_ptr<SentDataType>& data,
+                                       const typename ResultHandler<ReceivedDataType>::type& process)
+{
+    auto request = MethodRequest::create<SentDataType, ReceivedDataType>(methodID, peerID, data, process);
+    mRequestQueue.pushBack(Event::METHOD, request);
+    return request->messageID;
+}
+
+
+template<typename SentDataType, typename ReceivedDataType>
+std::shared_ptr<ReceivedDataType> Processor::callSync(const MethodID methodID,
+                                                      const PeerID& peerID,
+                                                      const std::shared_ptr<SentDataType>& data,
+                                                      unsigned int timeoutMS)
+{
+    Result<ReceivedDataType> result;
+    std::condition_variable cv;
+
+    auto process = [&result, &cv](const Result<ReceivedDataType> && r) {
+        // This is called under lock(mStateMutex)
+        result = std::move(r);
+        cv.notify_all();
+    };
+
+    Lock lock(mStateMutex);
+    MessageID messageID = callAsyncNonBlock<SentDataType, ReceivedDataType>(methodID,
+                                                                            peerID,
+                                                                            data,
+                                                                            process);
+
+    auto isResultInitialized = [&result]() {
+        return result.isSet();
+    };
+
+    LOGT(mLogPrefix + "Waiting for the response...");
+    //In the case of too large sending time response can be received far after timeoutMS but
+    //before this thread wakes up and before predicate check (there will by no timeout exception)
+    if (!cv.wait_for(lock, std::chrono::milliseconds(timeoutMS), isResultInitialized)) {
+        LOGW(mLogPrefix + "Probably a timeout in callSync. Checking...");
+
+        // Call isn't sent or call is sent but there is no reply
+        bool isTimeout = mRequestQueue.removeIf([messageID](Request & request) {
+            return request.requestID == Event::METHOD &&
+                   request.get<MethodRequest>()->messageID == messageID;
+        })
+        || 1 == mReturnCallbacks.erase(messageID);
+
+        if (isTimeout) {
+            LOGE(mLogPrefix + "Function call timeout; methodID: " << methodID);
+            removePeerSyncInternal(peerID, lock);
+            throw IPCTimeoutException("Function call timeout; methodID: " + std::to_string(methodID));
+        } else {
+            LOGW(mLogPrefix + "Timeout started during the return value processing, so wait for it to finish");
+            if (!cv.wait_for(lock, std::chrono::milliseconds(timeoutMS), isResultInitialized)) {
+                LOGE(mLogPrefix + "Function call timeout; methodID: " << methodID);
+                throw IPCTimeoutException("Function call timeout; methodID: " + std::to_string(methodID));
+            }
+        }
+    }
+
+    return result.get();
+}
+
+template<typename SentDataType>
+void Processor::signalInternal(const MethodID methodID,
+                               const PeerID& peerID,
+                               const std::shared_ptr<SentDataType>& data)
+{
+    auto requestPtr = SignalRequest::create<SentDataType>(methodID, peerID, data);
+    mRequestQueue.pushFront(Event::SIGNAL, requestPtr);
+}
+
+template<typename SentDataType>
+void Processor::signal(const MethodID methodID,
+                       const std::shared_ptr<SentDataType>& data)
+{
+    Lock lock(mStateMutex);
+    const auto it = mSignalsPeers.find(methodID);
+    if (it == mSignalsPeers.end()) {
+        LOGW(mLogPrefix + "No peer is handling signal with methodID: " << methodID);
+        return;
+    }
+    for (const PeerID peerID : it->second) {
+        auto requestPtr =  SignalRequest::create<SentDataType>(methodID, peerID, data);
+        mRequestQueue.pushBack(Event::SIGNAL, requestPtr);
+    }
+}
+
+
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_PROCESSOR_HPP
diff --git a/libs/cargo-ipc/internals/remove-method-request.hpp b/libs/cargo-ipc/internals/remove-method-request.hpp
new file mode 100644 (file)
index 0000000..c95f966
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Processor's request to remove the specified method handler
+ */
+
+#ifndef CARGO_IPC_INTERNALS_REMOVE_METHOD_REQUEST_HPP
+#define CARGO_IPC_INTERNALS_REMOVE_METHOD_REQUEST_HPP
+
+#include "cargo-ipc/types.hpp"
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class RemoveMethodRequest {
+public:
+    RemoveMethodRequest(const RemoveMethodRequest&) = delete;
+    RemoveMethodRequest& operator=(const RemoveMethodRequest&) = delete;
+
+    RemoveMethodRequest(const MethodID methodID)
+        : methodID(methodID)
+    {}
+
+    MethodID methodID;
+};
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_REMOVE_METHOD_REQUEST_HPP
diff --git a/libs/cargo-ipc/internals/remove-peer-request.hpp b/libs/cargo-ipc/internals/remove-peer-request.hpp
new file mode 100644 (file)
index 0000000..366a7d8
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Processor's request to remove a peer
+ */
+
+#ifndef CARGO_IPC_INTERNALS_REMOVE_PEER_REQUEST_HPP
+#define CARGO_IPC_INTERNALS_REMOVE_PEER_REQUEST_HPP
+
+#include "cargo-ipc/types.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+#include <condition_variable>
+
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class RemovePeerRequest {
+public:
+    RemovePeerRequest(const RemovePeerRequest&) = delete;
+    RemovePeerRequest& operator=(const RemovePeerRequest&) = delete;
+
+    RemovePeerRequest(const PeerID& peerID,
+                      const std::shared_ptr<std::condition_variable>& conditionPtr)
+        : peerID(peerID),
+          conditionPtr(conditionPtr)
+    {
+    }
+
+    PeerID peerID;
+    std::shared_ptr<std::condition_variable> conditionPtr;
+};
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_REMOVE_PEER_REQUEST_HPP
diff --git a/libs/cargo-ipc/internals/request-queue.hpp b/libs/cargo-ipc/internals/request-queue.hpp
new file mode 100644 (file)
index 0000000..6b7e775
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Managing the queue of messages carrying any kind of data
+ */
+
+#ifndef CARGO_IPC_INTERNALS_REQUEST_QUEUE_HPP
+#define CARGO_IPC_INTERNALS_REQUEST_QUEUE_HPP
+
+#include "cargo-ipc/exception.hpp"
+#include "utils/eventfd.hpp"
+#include "logger/logger.hpp"
+
+#include <list>
+#include <memory>
+#include <mutex>
+#include <algorithm>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+/**
+* Class for managing a queue of Requests carrying any data
+*/
+template<typename RequestIdType>
+class RequestQueue {
+public:
+    RequestQueue() = default;
+
+    RequestQueue(const RequestQueue&) = delete;
+    RequestQueue& operator=(const RequestQueue&) = delete;
+
+    struct Request {
+        Request(const Request& other) = delete;
+        Request& operator=(const Request&) = delete;
+
+        Request(Request&&) = default;
+        Request(const RequestIdType requestID, const std::shared_ptr<void>& data)
+            : requestID(requestID),
+              data(data)
+        {}
+
+        template<typename DataType>
+        std::shared_ptr<DataType> get()
+        {
+            return std::static_pointer_cast<DataType>(data);
+        }
+
+        RequestIdType requestID;
+        std::shared_ptr<void> data;
+    };
+
+    /**
+     * @return event's file descriptor
+     */
+    int getFD();
+
+    /**
+     * @return is the queue empty
+     */
+    bool isEmpty();
+
+    /**
+     * Push data to back of the queue
+     *
+     * @param requestID request type
+     * @param data data corresponding to the request
+     */
+    void pushBack(const RequestIdType requestID,
+                  const std::shared_ptr<void>& data = nullptr);
+
+    /**
+     * Push data to back of the queue
+     *
+     * @param requestID request type
+     * @param data data corresponding to the request
+     */
+    void pushFront(const RequestIdType requestID,
+                   const std::shared_ptr<void>& data = nullptr);
+
+    /**
+     * @return get the data from the next request
+     */
+    Request pop();
+
+    /**
+     * Remove elements from the queue when the predicate returns true
+     *
+     * @param predicate condition
+     * @return was anything removed
+     */
+    template<typename Predicate>
+    bool removeIf(Predicate predicate);
+
+private:
+    typedef std::unique_lock<std::mutex> Lock;
+
+    std::list<Request> mRequests;
+    std::mutex mStateMutex;
+    utils::EventFD mEventFD;
+};
+
+template<typename RequestIdType>
+int RequestQueue<RequestIdType>::getFD()
+{
+    Lock lock(mStateMutex);
+    return mEventFD.getFD();
+}
+
+template<typename RequestIdType>
+bool RequestQueue<RequestIdType>::isEmpty()
+{
+    Lock lock(mStateMutex);
+    return mRequests.empty();
+}
+
+template<typename RequestIdType>
+void RequestQueue<RequestIdType>::pushBack(const RequestIdType requestID,
+                                           const std::shared_ptr<void>& data)
+{
+    Lock lock(mStateMutex);
+    Request request(requestID, data);
+    mRequests.push_back(std::move(request));
+    mEventFD.send();
+}
+
+template<typename RequestIdType>
+void RequestQueue<RequestIdType>::pushFront(const RequestIdType requestID,
+                                            const std::shared_ptr<void>& data)
+{
+    Lock lock(mStateMutex);
+    Request request(requestID, data);
+    mRequests.push_front(std::move(request));
+    mEventFD.send();
+}
+
+template<typename RequestIdType>
+typename RequestQueue<RequestIdType>::Request RequestQueue<RequestIdType>::pop()
+{
+    Lock lock(mStateMutex);
+    mEventFD.receive();
+    if (mRequests.empty()) {
+        const std::string msg = "Request queue is empty";
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+    Request request = std::move(mRequests.front());
+    mRequests.pop_front();
+    return request;
+}
+
+template<typename RequestIdType>
+template<typename Predicate>
+bool RequestQueue<RequestIdType>::removeIf(Predicate predicate)
+{
+    Lock lock(mStateMutex);
+    auto it = std::find_if(mRequests.begin(), mRequests.end(), predicate);
+    if (it == mRequests.end()) {
+        return false;
+    }
+
+    do {
+        it = mRequests.erase(it);
+        it = std::find_if(it, mRequests.end(), predicate);
+    } while (it != mRequests.end());
+
+    return true;
+}
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_REQUEST_QUEUE_HPP
diff --git a/libs/cargo-ipc/internals/result-builder.hpp b/libs/cargo-ipc/internals/result-builder.hpp
new file mode 100644 (file)
index 0000000..15b39cf
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Class for storing result of a method - data or exception
+ */
+
+#ifndef CARGO_IPC_INTERNALS_RESULT_BUILDER_HPP
+#define CARGO_IPC_INTERNALS_RESULT_BUILDER_HPP
+
+#include "cargo-ipc/result.hpp"
+#include <functional>
+#include <exception>
+#include <memory>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class ResultBuilder {
+public:
+    ResultBuilder()
+        : mData(nullptr),
+          mExceptionPtr(nullptr)
+    {}
+
+    explicit ResultBuilder(const std::exception_ptr& exceptionPtr)
+        : mData(nullptr),
+          mExceptionPtr(exceptionPtr)
+    {}
+
+    explicit ResultBuilder(const std::shared_ptr<void>& data)
+        : mData(data),
+          mExceptionPtr(nullptr)
+
+    {}
+
+    template<typename Data>
+    Result<Data> build()
+    {
+        return Result<Data>(std::move(std::static_pointer_cast<Data>(mData)),
+                            std::move(mExceptionPtr));
+    }
+
+private:
+    std::shared_ptr<void> mData;
+    std::exception_ptr mExceptionPtr;
+};
+
+typedef std::function<void(ResultBuilder&)> ResultBuilderHandler;
+
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_RESULT_BUILDER_HPP
diff --git a/libs/cargo-ipc/internals/send-result-request.hpp b/libs/cargo-ipc/internals/send-result-request.hpp
new file mode 100644 (file)
index 0000000..93ccf37
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Processor's request to send the result of a method
+ */
+
+#ifndef CARGO_IPC_INTERNALS_SEND_RESULT_REQUEST_HPP
+#define CARGO_IPC_INTERNALS_SEND_RESULT_REQUEST_HPP
+
+#include "cargo-ipc/types.hpp"
+#include "logger/logger-scope.hpp"
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class SendResultRequest {
+public:
+    SendResultRequest(const SendResultRequest&) = delete;
+    SendResultRequest& operator=(const SendResultRequest&) = delete;
+
+    SendResultRequest(const MethodID methodID,
+                      const PeerID& peerID,
+                      const MessageID& messageID,
+                      const std::shared_ptr<void>& data)
+        : methodID(methodID),
+          peerID(peerID),
+          messageID(messageID),
+          data(data)
+    {}
+
+    MethodID methodID;
+    PeerID peerID;
+    MessageID messageID;
+    std::shared_ptr<void> data;
+};
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_SEND_RESULT_REQUEST_HPP
diff --git a/libs/cargo-ipc/internals/signal-request.hpp b/libs/cargo-ipc/internals/signal-request.hpp
new file mode 100644 (file)
index 0000000..299ec4c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Processor's request to send a signal
+ */
+
+#ifndef CARGO_IPC_INTERNALS_SIGNAL_REQUEST_HPP
+#define CARGO_IPC_INTERNALS_SIGNAL_REQUEST_HPP
+
+#include "cargo-ipc/types.hpp"
+#include "logger/logger-scope.hpp"
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+class SignalRequest {
+public:
+    SignalRequest(const SignalRequest&) = delete;
+    SignalRequest& operator=(const SignalRequest&) = delete;
+
+    template<typename SentDataType>
+    static std::shared_ptr<SignalRequest> create(const MethodID methodID,
+                                                 const PeerID& peerID,
+                                                 const std::shared_ptr<SentDataType>& data);
+
+    MethodID methodID;
+    PeerID peerID;
+    MessageID messageID;
+    std::shared_ptr<void> data;
+    SerializeCallback serialize;
+
+private:
+    SignalRequest(const MethodID methodID, const PeerID& peerID)
+        : methodID(methodID),
+          peerID(peerID),
+          messageID(getNextMessageID())
+    {}
+
+};
+
+template<typename SentDataType>
+std::shared_ptr<SignalRequest> SignalRequest::create(const MethodID methodID,
+                                                     const PeerID& peerID,
+                                                     const std::shared_ptr<SentDataType>& data)
+{
+    std::shared_ptr<SignalRequest> request(new SignalRequest(methodID, peerID));
+
+    request->data = data;
+
+    request->serialize = [](const int fd, std::shared_ptr<void>& data)->void {
+        LOGS("Signal serialize, peerID: " << fd);
+        cargo::saveToFD<SentDataType>(fd, *std::static_pointer_cast<SentDataType>(data));
+    };
+
+    return request;
+}
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_SIGNAL_REQUEST_HPP
diff --git a/libs/cargo-ipc/internals/socket.cpp b/libs/cargo-ipc/internals/socket.cpp
new file mode 100644 (file)
index 0000000..f5f3561
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Linux socket wrapper
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/exception.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+#include "logger/logger.hpp"
+
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif // HAVE_SYSTEMD
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <cerrno>
+#include <cstring>
+#include <thread>
+
+using namespace utils;
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+namespace {
+const int MAX_QUEUE_LENGTH = 1000;
+const int RETRY_CONNECT_STEP_MS = 10;
+const int UNIX_SOCKET_PROTOCOL = 0;
+
+void setFdOptions(const int fd)
+{
+    // Prevent from inheriting fd by zones
+    if (-1 == ::fcntl(fd, F_SETFD, FD_CLOEXEC)) {
+        const std::string msg = "Error in fcntl: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+}
+
+std::unique_ptr<::addrinfo, void(*)(::addrinfo*)> getAddressInfo(const std::string& host,
+                                                                 const std::string& port)
+{
+    ::addrinfo* addressInfo;
+
+    const char* chost = host.empty() ? nullptr : host.c_str();
+    const char* cport = port.empty() ? nullptr : port.c_str();
+
+    int ret = ::getaddrinfo(chost, cport, nullptr, &addressInfo);
+    if (ret != 0) {
+        const std::string msg = "Failed to get address info: " + std::string(::gai_strerror(ret));
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    return std::unique_ptr<::addrinfo, void(*)(::addrinfo*)>(addressInfo, ::freeaddrinfo);
+}
+
+void connect(const int socket,
+             const ::sockaddr* address,
+             const ::socklen_t addressLength,
+             const unsigned int timeoutMS)
+{
+    auto deadline = std::chrono::steady_clock::now() +
+                    std::chrono::milliseconds(timeoutMS);
+
+    // There's a race between connect() in one peer and listen() in the other.
+    // We'll retry connect if no one is listening.
+    do {
+        if (-1 != ::connect(socket,
+                            address,
+                            addressLength)) {
+            return;
+        }
+
+        if (errno == ECONNREFUSED || errno == EAGAIN || errno == EINTR) {
+            // No one is listening, so sleep and retry
+            LOGW("No one listening on the socket, retrying");
+            std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_CONNECT_STEP_MS));
+            continue;
+        }
+
+        // Error
+        utils::close(socket);
+        const std::string msg = "Error in connect: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCException(msg);
+
+    } while (std::chrono::steady_clock::now() < deadline);
+
+    const std::string msg = "Timeout in connect";
+    LOGE(msg);
+    throw IPCException(msg);
+}
+
+int getSocketFd(const int family, const int type, const int protocol)
+{
+    int fd = ::socket(family, type, protocol);
+    if (fd == -1) {
+        const std::string msg = "Error in socket: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCSocketException(errno, msg);
+    }
+    setFdOptions(fd);
+
+    return fd;
+}
+
+int getConnectedFd(const int family,
+                   const int type,
+                   const int protocol,
+                   const ::sockaddr* address,
+                   const ::socklen_t addressLength,
+                   const int timeoutMs)
+{
+    int fd = getSocketFd(family, type, protocol);
+
+    connect(fd, address, addressLength, timeoutMs);
+
+    // Nonblocking socket
+    int flags = ::fcntl(fd, F_GETFL, 0);
+    if (-1 == ::fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+        utils::close(fd);
+        const std::string msg = "Error in fcntl: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    return fd;
+}
+
+int getBoundFd(const int family,
+               const int type,
+               const int protocol,
+               const ::sockaddr* address,
+               const ::socklen_t addressLength)
+{
+    int fd = getSocketFd(family, type, protocol);
+
+    // Ensure address doesn't exist before bind() to avoid errors
+    ::unlink(reinterpret_cast<const ::sockaddr_un*>(address)->sun_path);
+
+    if (-1 == ::bind(fd, address, addressLength)) {
+        utils::close(fd);
+        const std::string msg = "Error in bind: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    if (-1 == ::listen(fd,
+                       MAX_QUEUE_LENGTH)) {
+        utils::close(fd);
+        const std::string msg = "Error in listen: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    return fd;
+}
+
+} // namespace
+
+Socket::Socket(int socketFD)
+    : mFD(socketFD)
+{
+}
+
+Socket::Socket(Socket&& socket) noexcept
+    : mFD(socket.mFD)
+{
+    socket.mFD = -1;
+}
+
+Socket::~Socket() noexcept
+{
+    try {
+        utils::close(mFD);
+    } catch (std::exception& e) {
+        LOGE("Error in Socket's destructor: " << e.what());
+    }
+}
+
+int Socket::getFD() const
+{
+    return mFD;
+}
+
+std::shared_ptr<Socket> Socket::accept()
+{
+    int sockfd = ::accept(mFD, nullptr, nullptr);
+    if (sockfd == -1) {
+        const std::string msg = "Error in accept: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+    setFdOptions(sockfd);
+    return std::make_shared<Socket>(sockfd);
+}
+
+Socket::Type Socket::getType() const
+{
+    int family;
+    socklen_t length = sizeof(family);
+
+    if (::getsockopt(mFD, SOL_SOCKET, SO_DOMAIN, &family, &length)) {
+        if (errno == EBADF) {
+            return Type::INVALID;
+        } else {
+            const std::string msg = "Error getting socket type: " + getSystemErrorMessage();
+            LOGE(msg);
+            throw IPCException(msg);
+        }
+    }
+
+
+    if (family == AF_UNIX || family == AF_LOCAL) {
+        return Type::UNIX;
+    }
+
+    if (family == AF_INET || family == AF_INET6) {
+        return Type::INET;
+    }
+
+    return Type::INVALID;
+}
+
+unsigned short Socket::getPort() const
+{
+    ::sockaddr_storage address = {0, 0, {0}};
+    ::socklen_t length = sizeof(address);
+    if (::getsockname(mFD, reinterpret_cast<sockaddr*>(&address), &length) != 0) {
+        const std::string msg = "Failed to get socked address: " + getSystemErrorMessage();
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    if (length == sizeof(sockaddr_in)) {
+        return ntohs(reinterpret_cast<const sockaddr_in*>(&address)->sin_port);
+    } else {
+        return ntohs(reinterpret_cast<const sockaddr_in6*>(&address)->sin6_port);
+    }
+}
+
+void Socket::write(const void* bufferPtr, const size_t size) const
+{
+    Guard guard(mCommunicationMutex);
+    utils::write(mFD, bufferPtr, size);
+}
+
+void Socket::read(void* bufferPtr, const size_t size) const
+{
+    Guard guard(mCommunicationMutex);
+    utils::read(mFD, bufferPtr, size);
+}
+
+#ifdef HAVE_SYSTEMD
+int Socket::getSystemdSocketInternal(const std::string& path)
+{
+    int n = ::sd_listen_fds(-1 /*Block further calls to sd_listen_fds*/);
+    if (n < 0) {
+        const std::string msg = "sd_listen_fds failed: " + getSystemErrorMessage(-n);
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    for (int fd = SD_LISTEN_FDS_START;
+            fd < SD_LISTEN_FDS_START + n;
+            ++fd) {
+        if (0 < ::sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0)) {
+            setFdOptions(fd);
+            return fd;
+        }
+    }
+    LOGW("No usable sockets were passed by systemd.");
+    return -1;
+}
+#endif // HAVE_SYSTEMD
+
+int Socket::createSocketInternal(const std::string& path)
+{
+    // Isn't the path too long?
+    if (path.size() >= sizeof(sockaddr_un::sun_path)) {
+        const std::string msg = "Socket's path too long";
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    ::sockaddr_un serverAddress;
+    serverAddress.sun_family = AF_UNIX;
+    ::strncpy(serverAddress.sun_path, path.c_str(), path.length() + 1);
+
+    return getBoundFd(AF_UNIX,
+                      SOCK_STREAM,
+                      UNIX_SOCKET_PROTOCOL,
+                      reinterpret_cast<struct sockaddr*>(&serverAddress),
+                      sizeof(struct sockaddr_un));
+}
+
+Socket Socket::createUNIX(const std::string& path)
+{
+    // Initialize a socket
+    int fd;
+#ifdef HAVE_SYSTEMD
+    fd = getSystemdSocketInternal(path);
+    if (fd == -1) {
+        fd = createSocketInternal(path);
+    }
+#else
+    fd = createSocketInternal(path);
+#endif // HAVE_SYSTEMD
+
+    return Socket(fd);
+}
+
+Socket Socket::createINET(const std::string& host, const std::string& service)
+{
+    auto address = getAddressInfo(host, service);
+
+    int fd = getBoundFd(address->ai_family,
+                        address->ai_socktype,
+                        address->ai_protocol,
+                        address->ai_addr,
+                        address->ai_addrlen);
+
+    return Socket(fd);
+}
+
+Socket Socket::connectUNIX(const std::string& path, const int timeoutMs)
+{
+    // Isn't the path too long?
+    if (path.size() >= sizeof(::sockaddr_un::sun_path)) {
+        const std::string msg = "Socket's path too long";
+        LOGE(msg);
+        throw IPCException(msg);
+    }
+
+    // Fill address
+    struct ::sockaddr_un serverAddress;
+    serverAddress.sun_family = AF_UNIX;
+    ::strncpy(serverAddress.sun_path, path.c_str(), sizeof(::sockaddr_un::sun_path));
+
+    int fd = getConnectedFd(AF_UNIX,
+                            SOCK_STREAM,
+                            UNIX_SOCKET_PROTOCOL,
+                            reinterpret_cast<struct sockaddr*>(&serverAddress),
+                            sizeof(struct ::sockaddr_un),
+                            timeoutMs);
+
+    return Socket(fd);
+}
+
+Socket Socket::connectINET(const std::string& host, const std::string& service, const int timeoutMs)
+{
+    auto addressInfo = getAddressInfo(host, service);
+
+    int fd = getConnectedFd(addressInfo->ai_family,
+                            addressInfo->ai_socktype,
+                            addressInfo->ai_protocol,
+                            reinterpret_cast<::sockaddr*>(addressInfo->ai_addr),
+                            addressInfo->ai_addrlen,
+                            timeoutMs);
+
+    return Socket(fd);
+}
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/internals/socket.hpp b/libs/cargo-ipc/internals/socket.hpp
new file mode 100644 (file)
index 0000000..0e0dc0b
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+*  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Linux socket wrapper
+ */
+
+#ifndef CARGO_IPC_INTERNALS_SOCKET_HPP
+#define CARGO_IPC_INTERNALS_SOCKET_HPP
+
+#include <string>
+#include <mutex>
+#include <memory>
+#include <netdb.h>
+
+namespace cargo {
+namespace ipc {
+namespace internals {
+
+/**
+ * This class wraps all operations possible to do with a socket.
+ *
+ * It has operations both for client and server application.
+ */
+class Socket {
+public:
+    enum class Type : int8_t {
+        INVALID,
+        UNIX,
+        INET
+    };
+
+    typedef std::unique_lock<std::recursive_mutex> Guard;
+
+    /**
+     * Default constructor.
+     * If socketFD is passed then it's passed by the Socket
+     *
+     * @param socketFD socket obtained outside the class.
+     */
+    explicit Socket(int socketFD = -1);
+    Socket(Socket&& socket) noexcept;
+    ~Socket() noexcept;
+
+    Socket(const Socket&) = delete;
+    Socket& operator=(const Socket&) = delete;
+    Socket& operator=(Socket&&) = delete;
+
+    /**
+     * @return reference to the socket's file descriptor
+     */
+    int getFD() const;
+
+    /**
+     * Write data using the file descriptor
+     *
+     * @param bufferPtr buffer with the data
+     * @param size size of the buffer
+     */
+    void write(const void* bufferPtr, const size_t size) const;
+
+    /**
+     * Reads a value of the given type.
+     *
+     * @param bufferPtr buffer with the data
+     * @param size size of the buffer
+     */
+    void read(void* bufferPtr, const size_t size) const;
+
+    /**
+     * Accepts connection. Used by a server application.
+     * Blocking, called by a server.
+     */
+    std::shared_ptr<Socket> accept();
+
+    /**
+     * Returns the socket type based on it's domain.
+     */
+    Type getType() const;
+
+    /**
+     * Returns a port associated with the socket.
+     */
+    unsigned short getPort() const;
+
+    /**
+     * Prepares UNIX socket for accepting connections.
+     * Called by a server.
+     *
+     * @param path path to the socket
+     * @return created socket
+     */
+    static Socket createUNIX(const std::string& path);
+
+    /**
+     * Prepares INET socket for accepting connections.
+     * Called by a server.
+     *
+     * @param host hostname or ip address
+     * @param service port number or service name
+     * @return created socket
+     */
+    static Socket createINET(const std::string& host, const std::string& service);
+
+    /**
+     * Connects to an UNIX socket. Called as a client.
+     *
+     * @param path path to the socket
+     * @return connected socket
+     */
+    static Socket connectUNIX(const std::string& path, const int timeoutMs = 1000);
+
+    /**
+     * Connects to an INET socket. Called as a client.
+     *
+     * @param host hostname or ip address
+     * @param service port number or service name
+     * @return connected socket
+     */
+    static Socket connectINET(const std::string& host,
+                              const std::string& service,
+                              const int timeoutMs = 1000);
+
+private:
+    int mFD;
+    mutable std::recursive_mutex mCommunicationMutex;
+
+    static int createSocketInternal(const std::string& path);
+    static int getSystemdSocketInternal(const std::string& path);
+};
+
+} // namespace internals
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_INTERNALS_SOCKET_HPP
diff --git a/libs/cargo-ipc/libcargo-ipc.pc.in b/libs/cargo-ipc/libcargo-ipc.pc.in
new file mode 100644 (file)
index 0000000..c1ecd89
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: IPC library
+Version: @_LIB_VERSION_@
+Libs: -L${libdir} -l@PROJECT_NAME@
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
diff --git a/libs/cargo-ipc/method-result.cpp b/libs/cargo-ipc/method-result.cpp
new file mode 100644 (file)
index 0000000..27cc6c4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Class for sending the result of a method
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/method-result.hpp"
+#include "cargo-ipc/internals/processor.hpp"
+
+namespace cargo {
+namespace ipc {
+
+using namespace internals;
+
+MethodResult::MethodResult(Processor& processor,
+                           const MethodID methodID,
+                           const MessageID& messageID,
+                           const PeerID& peerID)
+    : mProcessor(processor),
+      mMethodID(methodID),
+      mPeerID(peerID),
+      mMessageID(messageID)
+{}
+
+void MethodResult::setInternal(const std::shared_ptr<void>& data)
+{
+    mProcessor.sendResult(mMethodID, mPeerID, mMessageID, data);
+}
+
+void MethodResult::setVoid()
+{
+    mProcessor.sendVoid(mMethodID, mPeerID, mMessageID);
+}
+
+void MethodResult::setError(const int code, const std::string& message)
+{
+    mProcessor.sendError(mPeerID, mMessageID, code, message);
+}
+
+PeerID MethodResult::getPeerID() const
+{
+    return mPeerID;
+}
+
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/method-result.hpp b/libs/cargo-ipc/method-result.hpp
new file mode 100644 (file)
index 0000000..4b976bd
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Class for sending the result of a method
+ */
+
+#ifndef CARGO_IPC_METHOD_RESULT_HPP
+#define CARGO_IPC_METHOD_RESULT_HPP
+
+#include "cargo-ipc/types.hpp"
+#include "logger/logger.hpp"
+#include <memory>
+
+namespace cargo {
+namespace ipc {
+
+namespace internals {
+
+class Processor;
+
+}
+
+/**
+ * Class used to obtain method call result code.
+ * This class may be used to return value both:
+ * - synchronously in the MethodHandler
+ * - asynchronously, later outside MethodHandler
+ *
+ * @ingroup Types
+ */
+class MethodResult {
+public:
+    typedef std::shared_ptr<MethodResult> Pointer;
+
+    MethodResult(internals::Processor& processor,
+                 const MethodID methodID,
+                 const MessageID& messageID,
+                 const PeerID& peerID);
+
+
+    template<typename Data>
+    void set(const std::shared_ptr<Data>& data)
+    {
+        setInternal(data);
+    }
+
+    void setVoid();
+    void setError(const int code, const std::string& message);
+    PeerID getPeerID() const;
+
+private:
+    internals::Processor& mProcessor;
+    MethodID mMethodID;
+    PeerID mPeerID;
+    MessageID mMessageID;
+
+    void setInternal(const std::shared_ptr<void>& data);
+};
+
+template<typename SentDataType, typename ReceivedDataType>
+struct MethodHandler {
+    typedef std::function <HandlerExitCode(PeerID peerID,
+                                           std::shared_ptr<ReceivedDataType>& data,
+                                           MethodResult::Pointer methodResult) > type;
+};
+
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_METHOD_RESULT_HPP
diff --git a/libs/cargo-ipc/result.hpp b/libs/cargo-ipc/result.hpp
new file mode 100644 (file)
index 0000000..1e3f247
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Class for storing result of a method - data or exception
+ */
+
+#ifndef CARGO_IPC_RESULT_HPP
+#define CARGO_IPC_RESULT_HPP
+
+#include <functional>
+#include <exception>
+#include <memory>
+#include "cargo-ipc/exception.hpp"
+
+namespace cargo {
+namespace ipc {
+
+template<typename Data>
+class Result {
+public:
+    Result()
+        : mData(nullptr),
+          mExceptionPtr(nullptr)
+    {}
+
+    Result(std::shared_ptr<Data>&& data, std::exception_ptr&& exceptionPtr)
+        : mData(std::move(data)),
+          mExceptionPtr(std::move(exceptionPtr))
+    {}
+
+    void rethrow() const
+    {
+        if (isValid()) {
+            return;
+        }
+        if (mExceptionPtr) {
+            std::rethrow_exception(mExceptionPtr);
+        }
+        throw IPCInvalidResultException("Invalid result received. Details unknown.");
+    }
+
+    std::shared_ptr<Data> get() const
+    {
+        rethrow();
+        return mData;
+    }
+
+    bool isSet() const
+    {
+        return static_cast<bool>(mExceptionPtr) || static_cast<bool>(mData);
+    }
+
+    bool isValid() const
+    {
+        return static_cast<bool>(mData);
+    }
+
+private:
+    std::shared_ptr<Data> mData;
+    std::exception_ptr mExceptionPtr;
+};
+
+template<typename Data>
+struct ResultHandler {
+    typedef std::function < void(Result<Data>&&) > type;
+};
+
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_RESULT_HPP
diff --git a/libs/cargo-ipc/service.cpp b/libs/cargo-ipc/service.cpp
new file mode 100644 (file)
index 0000000..fd623e9
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Implementation of the cargo IPC handling class
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/service.hpp"
+#include "cargo-ipc/exception.hpp"
+#include "logger/logger.hpp"
+
+using namespace std::placeholders;
+using namespace cargo::ipc::internals;
+
+namespace cargo {
+namespace ipc {
+
+Service::Service(epoll::EventPoll& eventPoll,
+                 const std::string& socketPath,
+                 const PeerCallback& addPeerCallback,
+                 const PeerCallback& removePeerCallback)
+    : mEventPoll(eventPoll),
+      mProcessor(eventPoll, "[SERVICE] "),
+      mAcceptor(eventPoll, socketPath, std::bind(&Processor::addPeer, &mProcessor, _1))
+
+{
+    LOGS("Service Constructor");
+    setNewPeerCallback(addPeerCallback);
+    setRemovedPeerCallback(removePeerCallback);
+}
+
+Service::~Service()
+{
+    LOGS("Service Destructor");
+    try {
+        stop();
+    } catch (std::exception& e) {
+        LOGE("Error in Service's destructor: " << e.what());
+    }
+}
+
+void Service::start()
+{
+    if (mProcessor.isStarted()) {
+        return;
+    }
+    LOGS("Service start");
+
+    mProcessor.start();
+}
+
+bool Service::isStarted()
+{
+    return mProcessor.isStarted();
+}
+
+void Service::stop(bool wait)
+{
+    if (!mProcessor.isStarted()) {
+        return;
+    }
+    LOGS("Service stop");
+    mProcessor.stop(wait);
+}
+
+void Service::handle(const FileDescriptor fd, const epoll::Events pollEvents)
+{
+    LOGS("Service handle");
+
+    if (!isStarted()) {
+        LOGW("Service stopped, but got event: " << pollEvents << " on fd: " << fd);
+        return;
+    }
+
+    if ((pollEvents & EPOLLHUP) || (pollEvents & EPOLLRDHUP)) {
+        //IN, HUP, RDHUP are set when client is disconnecting but there is 0 bytes to read, so
+        //assume that if IN and HUP or RDHUP are set then input data is garbage.
+        //Assumption is harmless because handleInput processes all message data.
+        mProcessor.handleLostConnection(fd);
+        return;
+    }
+
+    if (pollEvents & EPOLLIN) {
+        //Process all message data
+        mProcessor.handleInput(fd);
+    }
+}
+
+void Service::setNewPeerCallback(const PeerCallback& newPeerCallback)
+{
+    LOGS("Service setNewPeerCallback");
+    auto callback = [newPeerCallback, this](PeerID peerID, FileDescriptor fd) {
+        auto handleFd = [&](FileDescriptor fd, epoll::Events events) {
+            handle(fd, events);
+        };
+        mEventPoll.addFD(fd, EPOLLIN | EPOLLHUP | EPOLLRDHUP, handleFd);
+        if (newPeerCallback) {
+            newPeerCallback(peerID, fd);
+        }
+    };
+    mProcessor.setNewPeerCallback(callback);
+}
+
+void Service::setRemovedPeerCallback(const PeerCallback& removedPeerCallback)
+{
+    LOGS("Service setRemovedPeerCallback");
+    auto callback = [removedPeerCallback, this](PeerID peerID, FileDescriptor fd) {
+        mEventPoll.removeFD(fd);
+        if (removedPeerCallback) {
+            removedPeerCallback(peerID, fd);
+        }
+    };
+    mProcessor.setRemovedPeerCallback(callback);
+}
+
+void Service::removeMethod(const MethodID methodID)
+{
+    LOGS("Service removeMethod methodID: " << methodID);
+    mProcessor.removeMethod(methodID);
+}
+
+bool Service::isHandled(const MethodID methodID)
+{
+    return mProcessor.isHandled(methodID);
+}
+
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/service.hpp b/libs/cargo-ipc/service.hpp
new file mode 100644 (file)
index 0000000..572ca9f
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Declaration of the cargo IPC handling class
+ */
+
+#ifndef CARGO_IPC_SERVICE_HPP
+#define CARGO_IPC_SERVICE_HPP
+
+#include "cargo-ipc/internals/processor.hpp"
+#include "cargo-ipc/internals/acceptor.hpp"
+#include "cargo-ipc/types.hpp"
+#include "cargo-ipc/result.hpp"
+#include "epoll/event-poll.hpp"
+#include "logger/logger.hpp"
+
+#include <string>
+
+namespace cargo {
+namespace ipc {
+
+
+/**
+ * @brief This class wraps communication via UX sockets.
+ * It uses serialization mechanism from Config.
+ *
+ * @code
+ * // eventPoll - epoll wrapper class
+ * // address - server socket address
+ * cargo::ipc::epoll::EventPoll examplePoll;
+ * // create callbacks for connected / disconnected events
+ * cargo::ipc::PeerCallback connectedCallback = [this](const cargo::ipc::PeerID peerID,
+ *                                              const cargo::ipc::FileDescriptor) {
+ *     // new connection came!
+ * };
+ * cargo::ipc::PeerCallback disconnectedCallback = [this](const cargo::ipc::PeerID peerID,
+ *                                                 const cargo::ipc::FileDescriptor) {
+ *     // connection disconnected!
+ * };
+ * // create the service
+ * cargo::ipc::Service myService(eventPoll, "/tmp/example_service.socket",
+ *                        connectedCallback, disconnectedCallback));
+ * // add example method handler
+ * auto exampleMethodHandler = [&](const PeerID, std::shared_ptr<RecvData>& data, MethodResult::Pointer methodResult) {
+ *     // got example method call! Incoming data in "data" argument
+ *     // respond with some data
+ *     auto returnData = std::make_shared<SendData>(data->intVal);
+ *     methodResult->set(returnData);
+ * };
+ * const MethodID exampleMethodID = 1234;
+ * myService.setMethodHandler<SendData, RecvData>(exampleMethodID, exampleMethodHandler);
+ * myService.start(); // start the service, clients may connect via /tmp/example_service.socket
+ * @endcode
+ *
+ * @see libCargo
+ * @see cargo::ipc::Processor
+ *
+ * @ingroup libcargo-ipc
+ */
+class Service {
+public:
+    /**
+     * Constructs the Service, but doesn't start it.
+     * The object is ready to add methods.
+     * Once set-up, call start() to start the service.
+     *
+     * @param eventPoll             event poll
+     * @param path                  path to the socket
+     * @param addPeerCallback       optional on new peer connection callback
+     * @param removePeerCallback    optional on peer removal callback
+     */
+    Service(epoll::EventPoll& eventPoll,
+            const std::string& path,
+            const PeerCallback& addPeerCallback = nullptr,
+            const PeerCallback& removePeerCallback = nullptr);
+    virtual ~Service();
+
+    /**
+     * Copying Service class is prohibited.
+     */
+    Service(const Service&) = delete;
+    /**
+     * Copying Service class is prohibited.
+     */
+    Service& operator=(const Service&) = delete;
+
+    /**
+     * Starts processing
+     * @note if the service is already running, it quits immediately (no exception thrown)
+     */
+    void start();
+
+    /**
+    * @return is the communication thread running
+    */
+    bool isStarted();
+
+    /**
+     * Stops all working threads
+     *
+     * @param wait      should the call block while waiting for all internals to stop? By default true - do block.
+     */
+    void stop(bool wait = true);
+
+    /**
+    * Set the callback called for each new connection to a peer
+    *
+    * @param newPeerCallback        the callback to call on new connection event
+    * @note if callback is already set, it will be overridden
+    */
+    void setNewPeerCallback(const PeerCallback& newPeerCallback);
+
+    /**
+     * Set the callback called when connection to a peer is lost
+     *
+     * @param removedPeerCallback   the callback to call on peer disconnected event
+     * @note if callback is already set, it will be overridden
+     */
+    void setRemovedPeerCallback(const PeerCallback& removedPeerCallback);
+
+    /**
+     * Saves the callbacks connected to the method id.
+     * When a message with the given method id is received,
+     * the data will be passed to the serialization callback through file descriptor.
+     *
+     * Then the process callback will be called with the parsed data.
+     *
+     * @param methodID              API dependent id of the method
+     * @param method                data processing callback
+     * @tparam SentDataType         data type to send
+     * @tparam ReceivedDataType     data type to receive
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    void setMethodHandler(const MethodID methodID,
+                          const typename MethodHandler<SentDataType, ReceivedDataType>::type& method);
+
+    /**
+     * Saves the callbacks connected to the method id.
+     * When a message with the given method id is received,
+     * the data will be passed to the serialization callback through file descriptor.
+     *
+     * Then the process callback will be called with the parsed data.
+     * There is no return data to send back.
+     *
+     * Adding signal sends a registering message to all peers
+     *
+     * @param methodID              API dependent id of the method
+     * @param handler               data processing callback
+     * @tparam ReceivedDataType     data type to receive
+     */
+    template<typename ReceivedDataType>
+    void setSignalHandler(const MethodID methodID,
+                          const typename SignalHandler<ReceivedDataType>::type& handler);
+
+    /**
+     * Removes the callback associated with specific method id.
+     *
+     * @param methodID              API dependent id of the method
+     * @see setMethodHandler()
+     * @see setSignalHandler()
+     */
+    void removeMethod(const MethodID methodID);
+
+    /**
+     * @param methodID MethodID defined in the user's API
+     * @return is methodID handled by a signal or method
+     */
+    bool isHandled(const MethodID methodID);
+
+    /**
+     * Synchronous method call.
+     *
+     * @param methodID              API dependent id of the method
+     * @param peerID                id of the peer
+     * @param data                  data to send
+     * @param timeoutMS             optional, how long to wait for the return value before throw (milliseconds, default: 5000)
+     * @tparam SentDataType         data type to send
+     * @tparam ReceivedDataType     data type to receive
+     * @return pointer to the call result data
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    std::shared_ptr<ReceivedDataType> callSync(const MethodID methodID,
+                                               const PeerID& peerID,
+                                               const std::shared_ptr<SentDataType>& data,
+                                               unsigned int timeoutMS = 5000);
+
+    /**
+     * Asynchronous method call. The return callback will be called on
+     * return data arrival. It will be run in the PROCESSOR thread.
+     *
+     * @param methodID              API dependent id of the method
+     * @param peerID                id of the peer
+     * @param data                  data to send
+     * @param resultCallback        callback processing the return data
+     * @tparam SentDataType         data type to send
+     * @tparam ReceivedDataType     data type to receive
+     */
+    template<typename SentDataType, typename ReceivedDataType>
+    void callAsync(const MethodID methodID,
+                   const PeerID& peerID,
+                   const std::shared_ptr<SentDataType>& data,
+                   const typename ResultHandler<ReceivedDataType>::type& resultCallback =  nullptr);
+
+    template<typename SentDataType, typename ReceivedDataType>
+    void callAsyncFromCallback(const MethodID methodID,
+                               const PeerID& peerID,
+                               const std::shared_ptr<SentDataType>& data,
+                               const typename ResultHandler<ReceivedDataType>::type& resultCallback  =  nullptr);
+
+    /**
+    * Send a signal to the peer.
+    * There is no return value from the peer
+    * Sends any data only if a peer registered this a signal
+    *
+    * @param methodID               API dependent id of the method
+    * @param data                   data to send
+    * @tparam SentDataType          data type to send
+    */
+    template<typename SentDataType>
+    void signal(const MethodID methodID,
+                const std::shared_ptr<SentDataType>& data);
+private:
+    epoll::EventPoll& mEventPoll;
+    internals::Processor mProcessor;
+    internals::Acceptor mAcceptor;
+
+    void handle(const FileDescriptor fd, const epoll::Events pollEvents);
+};
+
+
+template<typename SentDataType, typename ReceivedDataType>
+void Service::setMethodHandler(const MethodID methodID,
+                               const typename MethodHandler<SentDataType, ReceivedDataType>::type& method)
+{
+    LOGS("Service setMethodHandler, methodID " << methodID);
+    mProcessor.setMethodHandler<SentDataType, ReceivedDataType>(methodID, method);
+}
+
+template<typename ReceivedDataType>
+void Service::setSignalHandler(const MethodID methodID,
+                               const typename SignalHandler<ReceivedDataType>::type& handler)
+{
+    LOGS("Service setSignalHandler, methodID " << methodID);
+    mProcessor.setSignalHandler<ReceivedDataType>(methodID, handler);
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+std::shared_ptr<ReceivedDataType> Service::callSync(const MethodID methodID,
+                                                    const PeerID& peerID,
+                                                    const std::shared_ptr<SentDataType>& data,
+                                                    unsigned int timeoutMS)
+{
+    LOGS("Service callSync, methodID: " << methodID
+         << ", peerID: " << shortenPeerID(peerID)
+         << ", timeoutMS: " << timeoutMS);
+    return mProcessor.callSync<SentDataType, ReceivedDataType>(methodID, peerID, data, timeoutMS);
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+void Service::callAsync(const MethodID methodID,
+                        const PeerID& peerID,
+                        const std::shared_ptr<SentDataType>& data,
+                        const typename ResultHandler<ReceivedDataType>::type& resultCallback)
+{
+    LOGS("Service callAsync, methodID: " << methodID << ", peerID: " << shortenPeerID(peerID));
+    mProcessor.callAsync<SentDataType,
+                         ReceivedDataType>(methodID,
+                                           peerID,
+                                           data,
+                                           resultCallback);
+}
+
+template<typename SentDataType, typename ReceivedDataType>
+void Service::callAsyncFromCallback(const MethodID methodID,
+                                    const PeerID& peerID,
+                                    const std::shared_ptr<SentDataType>& data,
+                                    const typename ResultHandler<ReceivedDataType>::type& resultCallback)
+{
+    LOGS("Service callAsyncFromCallback, methodID: " << methodID
+                                     << ", peerID: " << shortenPeerID(peerID));
+    mProcessor.callAsyncNonBlock<SentDataType,
+                                 ReceivedDataType>(methodID,
+                                                   peerID,
+                                                   data,
+                                                   resultCallback);
+}
+
+
+template<typename SentDataType>
+void Service::signal(const MethodID methodID,
+                     const std::shared_ptr<SentDataType>& data)
+{
+    LOGS("Service signal, methodID: " << methodID);
+    mProcessor.signal<SentDataType>(methodID, data);
+}
+
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_SERVICE_HPP
diff --git a/libs/cargo-ipc/types.cpp b/libs/cargo-ipc/types.cpp
new file mode 100644 (file)
index 0000000..e94b6fc
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Types definitions and helper functions
+ */
+
+#include "config.hpp"
+
+#include "cargo-ipc/types.hpp"
+#include "cargo-ipc/unique-id.hpp"
+#include "logger/logger.hpp"
+
+#include <mutex>
+
+namespace cargo {
+namespace ipc {
+
+namespace {
+// complex types cannot be easily used with std::atomic - these require libatomic to be linked
+// instead, secure the ID getters with mutex
+MessageID gLastMessageID;
+PeerID gLastPeerID;
+
+std::mutex gMessageIDMutex;
+std::mutex gPeerIDMutex;
+
+const size_t ID_TRIM_LENGTH = 6;
+} // namespace
+
+MessageID getNextMessageID()
+{
+    std::unique_lock<std::mutex> lock(gMessageIDMutex);
+    UniqueID uid;
+    uid.generate();
+    gLastMessageID = uid;
+    return gLastMessageID;
+}
+
+MessageID shortenMessageID(const MessageID& id)
+{
+    return id.substr(0, ID_TRIM_LENGTH) + "..." + id.substr(id.length() - ID_TRIM_LENGTH);
+}
+
+PeerID getNextPeerID()
+{
+    std::unique_lock<std::mutex> lock(gPeerIDMutex);
+    UniqueID uid;
+    uid.generate();
+    gLastPeerID = uid;
+    return gLastPeerID;
+}
+
+PeerID shortenPeerID(const PeerID& id)
+{
+    return id.substr(0, ID_TRIM_LENGTH) + "..." + id.substr(id.length() - ID_TRIM_LENGTH);
+}
+
+
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/types.hpp b/libs/cargo-ipc/types.hpp
new file mode 100644 (file)
index 0000000..7d8d0ed
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Types definitions
+ */
+
+#ifndef CARGO_IPC_TYPES_HPP
+#define CARGO_IPC_TYPES_HPP
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace cargo {
+namespace ipc {
+
+/**
+ * @brief Generic types used in libcargo-ipc.
+ *
+ * @ingroup libcargo-ipc
+ * @defgroup Types libcargo-ipc tools
+ */
+
+typedef int FileDescriptor;
+typedef unsigned int MethodID;
+typedef std::string MessageID;
+typedef std::string PeerID;
+
+/**
+ * Generic function type used as callback for peer events.
+ *
+ * @param   peerID          peer identifier that event relates to
+ * @param   fd              event origin
+ * @ingroup Types
+ */
+typedef std::function<void(const cargo::ipc::PeerID peerID, const cargo::ipc::FileDescriptor fd)> PeerCallback;
+
+/**
+ * Generic function type used as callback for serializing and
+ * saving serialized data to the descriptor.
+ *
+ * @param   fd              descriptor to save the serialized data to
+ * @param   data            data to serialize
+ * @ingroup Types
+ */
+typedef std::function<void(cargo::ipc::FileDescriptor fd, std::shared_ptr<void>& data)> SerializeCallback;
+
+/**
+ * Generic function type used as callback for reading and parsing data.
+ *
+ * @param   fd              descriptor to read the data from
+ * @ingroup Types
+ */
+typedef std::function<std::shared_ptr<void>(cargo::ipc::FileDescriptor fd)> ParseCallback;
+
+/**
+ * Generate an unique message id.
+ *
+ * @return new, unique MessageID
+ * @ingroup Types
+ */
+MessageID getNextMessageID();
+
+/**
+ * Shorten the message ID for logging purposes.
+ *
+ * @param id ID to shorten
+ * @return shortened ID
+ *
+ * @remarks The function does not return full ID. It is only a subset of it, so resulting ID should
+ *          be used only for logging.
+ */
+MessageID shortenMessageID(const MessageID& id);
+
+/**
+ * Generate an unique peer id.
+ *
+ * @return new, unique PeerID
+ * @ingroup Types
+ */
+PeerID getNextPeerID();
+
+/**
+ * Shorten the peer ID for logging purposes.
+ *
+ * @param id ID to shorten
+ * @return shortened ID
+ *
+ * @remarks The function does not return full ID. It is only a subset of it, so resulting ID should
+ *          be used only for logging.
+ */
+PeerID shortenPeerID(const PeerID& id);
+
+/**
+ * method/signal handler return code, used to tell the processor
+ * what to do with the handler after its execution.
+ * @ingroup Types
+ */
+enum class HandlerExitCode : int {
+    SUCCESS,            ///< do nothing
+    REMOVE_HANDLER      ///< remove handler from the processor
+};
+
+/**
+ * Generic type used as a callback function for handling signals.
+ * @tparam ReceivedDataType     type of received data
+ * @ingroup Types
+ */
+template<typename ReceivedDataType>
+struct SignalHandler {
+    typedef std::function<HandlerExitCode(PeerID peerID,
+                                          std::shared_ptr<ReceivedDataType>& data)> type;
+};
+
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_TYPES_HPP
diff --git a/libs/cargo-ipc/unique-id.cpp b/libs/cargo-ipc/unique-id.cpp
new file mode 100644 (file)
index 0000000..f5cc2ee
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Unique ID type definition
+ */
+
+#include "config.hpp"
+
+#include "unique-id.hpp"
+
+namespace cargo {
+namespace ipc {
+
+UniqueID::UniqueID()
+{
+    mTime.tv_sec = 0;
+    mTime.tv_nsec = 0;
+    ::uuid_clear(mUUID);
+}
+
+void UniqueID::generate()
+{
+    ::clock_gettime(CLOCK_REALTIME, &mTime);
+    ::uuid_generate_random(mUUID);
+}
+
+bool UniqueID::operator==(const UniqueID& other) const
+{
+    return (mTime.tv_sec == other.mTime.tv_sec)
+        && (mTime.tv_nsec == other.mTime.tv_nsec)
+        && (::uuid_compare(mUUID, other.mUUID) == 0); // uuid_compare works just like strcmp
+}
+
+UniqueID::operator std::string() const
+{
+    char uuid[37]; // uuid_unparse assures that it will print 36 chars + terminating zero
+    ::uuid_unparse(mUUID, uuid);
+    return std::to_string(mTime.tv_sec) + '.' + std::to_string(mTime.tv_nsec) + ':' + uuid;
+}
+
+std::ostream& operator<<(std::ostream& str, const UniqueID& id)
+{
+    str << static_cast<std::string>(id);
+    return str;
+}
+
+} // namespace ipc
+} // namespace cargo
diff --git a/libs/cargo-ipc/unique-id.hpp b/libs/cargo-ipc/unique-id.hpp
new file mode 100644 (file)
index 0000000..702ee52
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Unique ID type declaration
+ */
+
+#ifndef CARGO_IPC_UNIQUE_ID_HPP
+#define CARGO_IPC_UNIQUE_ID_HPP
+
+#include <ostream>
+#include <chrono>
+#include <uuid/uuid.h>
+#include <time.h>
+
+namespace cargo {
+namespace ipc {
+
+class UniqueID {
+public:
+    typedef struct timespec TimestampType;
+    typedef uuid_t UUIDType;
+
+    /**
+     * Default constructor. Generates an empty ID.
+     */
+    UniqueID();
+
+    /**
+     * Generate new timestamp and UUID pair.
+     */
+    void generate();
+
+    /**
+     * Compare two IDs
+     *
+     * @param other Other ID to compare to.
+     * @return True if both timestamp and UUID are equal, false otherwise.
+     */
+    bool operator==(const UniqueID& other) const;
+
+    /**
+     * Casts current ID to string
+     */
+    operator std::string() const;
+
+    /**
+     * Overloaded << operator for debugging purposes. Used in ut-uid.cpp tests.
+     */
+    friend std::ostream& operator<<(std::ostream& str, const UniqueID& id);
+
+
+    TimestampType mTime;    ///< timestamp when generate() was called
+    UUIDType mUUID;         ///< random UUID generated with libuuid
+};
+
+template <typename T>
+class hash;
+
+template <>
+class hash<cargo::ipc::UniqueID>
+{
+public:
+    std::size_t operator()(const cargo::ipc::UniqueID& id) const
+    {
+        char uuid[37]; // 36 chars for UUID + terminating zero
+        ::uuid_unparse(id.mUUID, uuid);
+
+        // STL does not provide correct hash implementation for char *
+        // Instead, just convert it to string
+        std::string uuids(uuid);
+
+        return std::hash<time_t>()(id.mTime.tv_sec)
+             ^ std::hash<long>()(id.mTime.tv_nsec)
+             ^ std::hash<std::string>()(uuids);
+    }
+};
+
+} // namespace ipc
+} // namespace cargo
+
+#endif // CARGO_IPC_UNIQUE_ID_HPP
diff --git a/libs/cargo-json/CMakeLists.txt b/libs/cargo-json/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e79316c
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Pawel Kubik (p.kubik@samsung.com)
+#
+
+PROJECT(cargo-json)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+FILE(GLOB SRCS internals/*.cpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
+    SOVERSION   ${_LIB_SOVERSION_}
+    VERSION     ${_LIB_VERSION_}
+)
+
+ADD_DEPENDENCIES(${PROJECT_NAME} cargo-utils)
+
+## Link libraries ##############################################################
+PKG_SEARCH_MODULE(JSON_C REQUIRED json json-c)
+
+INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${LIBS_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${JSON_C_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${JSON_C_LIBRARIES})
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(TARGETS     ${PROJECT_NAME}
+        DESTINATION ${LIB_INSTALL_DIR}
+        COMPONENT   RuntimeLibraries)
+
+INSTALL(DIRECTORY . DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}
+        FILES_MATCHING PATTERN "*.hpp"
+                       PATTERN "CMakeFiles" EXCLUDE)
+
+INSTALL(FILES       ${COMMON_FOLDER}/config.hpp
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
diff --git a/libs/cargo-json/cargo-json.hpp b/libs/cargo-json/cargo-json.hpp
new file mode 100644 (file)
index 0000000..f0d69b6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author   Pawel Kubik (p.kubik@samsung.com)
+ * @defgroup libcargo-json libcargo-json
+ * @brief    cargo Json interface
+ */
+
+#ifndef CARGO_JSON_CARGO_JSON_HPP
+#define CARGO_JSON_CARGO_JSON_HPP
+
+#include "cargo-json/internals/to-json-visitor.hpp"
+#include "cargo-json/internals/from-json-visitor.hpp"
+#include "cargo-json/internals/fs-utils.hpp"
+
+namespace cargo {
+
+/*@{*/
+
+/**
+ * Fills the visitable with data stored in the json string
+ *
+ * @param jsonString    data in a json format
+ * @param visitable        visitable structure to fill
+ */
+template <class Cargo>
+void loadFromJsonString(const std::string& jsonString, Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::FromJsonVisitor visitor(jsonString);
+    visitable.accept(visitor);
+}
+
+/**
+ * Creates a string representation of the visitable in json format
+ *
+ * @param visitable   visitable structure to convert
+ */
+template <class Cargo>
+std::string saveToJsonString(const Cargo& visitable)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::ToJsonVisitor visitor;
+    visitable.accept(visitor);
+    return visitor.toString();
+}
+
+/**
+ * Loads the visitable from a json file
+ *
+ * @param filename    path to the file
+ * @param visitable   visitable structure to load
+ */
+template <class Cargo>
+void loadFromJsonFile(const std::string& filename, Cargo& visitable)
+{
+    std::string content;
+    if (!internals::fsutils::readFileContent(filename, content)) {
+        const std::string& msg = "Could not load " + filename;
+        throw CargoException(msg);
+    }
+    try {
+        loadFromJsonString(content, visitable);
+    } catch (CargoException& e) {
+        const std::string& msg = "Error in " + filename + ": " + e.what();
+        throw CargoException(msg);
+    }
+}
+
+/**
+ * Saves the visitable in a json file
+ *
+ * @param filename    path to the file
+ * @param visitable   visitable structure to save
+ */
+template <class Cargo>
+void saveToJsonFile(const std::string& filename, const Cargo& visitable)
+{
+    const std::string content = saveToJsonString(visitable);
+    if (!internals::fsutils::saveFileContent(filename, content)) {
+        throw CargoException("Could not save " + filename);
+    }
+}
+
+} // namespace cargo
+
+/*@}*/
+
+#endif // CARGO_JSON_CARGO_JSON_HPP
diff --git a/libs/cargo-json/internals/from-json-visitor.hpp b/libs/cargo-json/internals/from-json-visitor.hpp
new file mode 100644 (file)
index 0000000..77becb1
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   JSON visitor
+ */
+
+#ifndef CARGO_JSON_INTERNALS_FROM_JSON_VISITOR_HPP
+#define CARGO_JSON_INTERNALS_FROM_JSON_VISITOR_HPP
+
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/exception.hpp"
+#include "cargo/internals/visit-fields.hpp"
+
+#include <json.h>
+#include <string>
+#include <cstring>
+#include <vector>
+#include <array>
+#include <utility>
+#include <limits>
+
+namespace cargo {
+
+namespace internals {
+
+class FromJsonVisitor {
+public:
+    explicit FromJsonVisitor(const std::string& jsonString)
+        : mObject(nullptr)
+    {
+        mObject = json_tokener_parse(jsonString.c_str());
+        if (mObject == nullptr) {
+            throw CargoException("Json parsing error");
+        }
+    }
+
+    FromJsonVisitor(const FromJsonVisitor& visitor)
+        : mObject(json_object_get(visitor.mObject))
+    {
+    }
+
+    ~FromJsonVisitor()
+    {
+        json_object_put(mObject);
+    }
+
+    FromJsonVisitor& operator=(const FromJsonVisitor&) = delete;
+
+    template<typename T>
+    void visit(const std::string& name, T& value)
+    {
+        json_object* object = nullptr;
+        if (!json_object_object_get_ex(mObject, name.c_str(), &object)) {
+            throw CargoException("Missing field '" + name + "'");
+        }
+        fromJsonObject(object, value);
+    }
+
+private:
+    json_object* mObject;
+
+
+    explicit FromJsonVisitor(json_object* object)
+        : mObject(json_object_get(object))
+    {
+    }
+
+    static void checkType(json_object* object, json_type type)
+    {
+        if (type != json_object_get_type(object)) {
+            throw CargoException("Invalid field type");
+        }
+    }
+
+    template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
+    static void fromJsonObject(json_object* object, T& value)
+    {
+        checkType(object, json_type_int);
+        typedef typename std::conditional<std::is_signed<T>::value,
+                                          std::int64_t, std::uint64_t>::type BufferType;
+        BufferType value64 = json_object_get_int64(object);
+        if (value64 > std::numeric_limits<T>::max() || value64 < std::numeric_limits<T>::min()) {
+            throw CargoException("Value out of range");
+        }
+        value = static_cast<T>(value64);
+    }
+
+    static void fromJsonObject(json_object* object, bool& value)
+    {
+        checkType(object, json_type_boolean);
+        value = json_object_get_boolean(object);
+    }
+
+    static void fromJsonObject(json_object* object, double& value)
+    {
+        checkType(object, json_type_double);
+        value = json_object_get_double(object);
+    }
+
+    static void fromJsonObject(json_object* object, std::string& value)
+    {
+        checkType(object, json_type_string);
+        value = json_object_get_string(object);
+    }
+
+    static void fromJsonObject(json_object* object, char* &value)
+    {
+        checkType(object, json_type_string);
+
+        int len = json_object_get_string_len(object);
+        value = new char[len + 1];
+        std::strncpy(value, json_object_get_string(object), len);
+        value[len] = '\0';
+    }
+
+    template<typename T>
+    static void fromJsonObject(json_object* object, std::vector<T>& values)
+    {
+        checkType(object, json_type_array);
+        int length = json_object_array_length(object);
+        values.resize(static_cast<size_t>(length));
+        for (int i = 0; i < length; ++i) {
+            fromJsonObject(json_object_array_get_idx(object, i), values[static_cast<size_t>(i)]);
+        }
+    }
+
+    template<typename T, std::size_t N>
+    static void fromJsonObject(json_object* object, std::array<T, N>& values)
+    {
+        checkType(object, json_type_array);
+
+        for (std::size_t i = 0; i < N; ++i) {
+            fromJsonObject(json_object_array_get_idx(object, i), values[i]);
+        }
+    }
+
+    template<typename V>
+    static void fromJsonObject(json_object* object, std::map<std::string, V>& values)
+    {
+        checkType(object, json_type_object);
+
+        json_object_object_foreach(object, key, val) {
+            fromJsonObject(val, values[key]);
+        }
+    }
+
+    struct HelperVisitor
+    {
+        template<typename T>
+        static void visit(json_object* object, std::size_t& idx, T&& value)
+        {
+            fromJsonObject(json_object_array_get_idx(object, idx), value);
+            idx += 1;
+        }
+    };
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    static void fromJsonObject(json_object* object, T& values)
+    {
+        checkType(object, json_type_array);
+
+        std::size_t idx = 0;
+        HelperVisitor visitor;
+        visitFields(values, &visitor, object, idx);
+    }
+
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    static void fromJsonObject(json_object* object, T& value)
+    {
+        fromJsonObject(object,
+                       *reinterpret_cast<typename std::underlying_type<T>::type*>(&value));
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    static void fromJsonObject(json_object* object, T& value)
+    {
+        checkType(object, json_type_object);
+        FromJsonVisitor visitor(object);
+        value.accept(visitor);
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_JSON_INTERNALS_FROM_JSON_VISITOR_HPP
diff --git a/libs/cargo-json/internals/fs-utils.cpp b/libs/cargo-json/internals/fs-utils.cpp
new file mode 100644 (file)
index 0000000..c0ad6d1
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Filesystem helper functions
+ */
+
+#include "config.hpp"
+
+#include "cargo-json/internals/fs-utils.hpp"
+
+#include <fstream>
+#include <streambuf>
+
+
+namespace cargo {
+namespace internals {
+namespace fsutils {
+
+bool readFileContent(const std::string& path, std::string& result)
+{
+    std::ifstream file(path);
+
+    if (!file) {
+        return false;
+    }
+
+    file.seekg(0, std::ios::end);
+    std::streampos length = file.tellg();
+    if (length < 0) {
+        return false;
+    }
+    result.resize(static_cast<size_t>(length));
+    file.seekg(0, std::ios::beg);
+
+    file.read(&result[0], length);
+    if (!file) {
+        result.clear();
+        return false;
+    }
+
+    return true;
+}
+
+bool saveFileContent(const std::string& path, const std::string& content)
+{
+    std::ofstream file(path);
+    if (!file) {
+        return false;
+    }
+    file.write(content.data(), static_cast<std::streamsize>(content.size()));
+    if (!file) {
+        return false;
+    }
+    return true;
+}
+
+} // namespace fsutils
+} // namespace internals
+} // namespace cargo
diff --git a/libs/cargo-json/internals/fs-utils.hpp b/libs/cargo-json/internals/fs-utils.hpp
new file mode 100644 (file)
index 0000000..2d2ba9b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   src/cargo/manager.hpp
+ */
+
+#ifndef CARGO_JSON_INTERNALS_FS_UTILS_HPP
+#define CARGO_JSON_INTERNALS_FS_UTILS_HPP
+
+#include <string>
+
+namespace cargo {
+namespace internals {
+namespace fsutils {
+
+bool readFileContent(const std::string& path, std::string& result);
+bool saveFileContent(const std::string& path, const std::string& content);
+
+inline std::string readFileContent(const std::string& path) {
+    std::string content;
+    return readFileContent(path, content) ? content : std::string();
+}
+} // namespace fsutils
+} // namespace internals
+} // namespace cargo
+
+#endif // CARGO_JSON_INTERNALS_FS_UTILS_HPP
diff --git a/libs/cargo-json/internals/to-json-visitor.hpp b/libs/cargo-json/internals/to-json-visitor.hpp
new file mode 100644 (file)
index 0000000..7164171
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   JSON visitor
+ */
+
+#ifndef CARGO_JSON_INTERNALS_TO_JSON_VISITOR_HPP
+#define CARGO_JSON_INTERNALS_TO_JSON_VISITOR_HPP
+
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/exception.hpp"
+#include "cargo/internals/visit-fields.hpp"
+
+#include <array>
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+
+#include <json.h>
+
+namespace cargo {
+
+namespace internals {
+
+class ToJsonVisitor {
+
+public:
+    ToJsonVisitor()
+        : mObject(json_object_new_object())
+    {
+    }
+
+    ToJsonVisitor(const ToJsonVisitor& visitor)
+        : mObject(json_object_get(visitor.mObject))
+    {
+    }
+
+    ~ToJsonVisitor()
+    {
+        json_object_put(mObject);
+    }
+
+    ToJsonVisitor& operator=(const ToJsonVisitor&) = delete;
+
+    std::string toString() const
+    {
+        return json_object_to_json_string(mObject);
+    }
+
+    template<typename T>
+    void visit(const std::string& name, const T& value)
+    {
+        json_object_object_add(mObject, name.c_str(), toJsonObject(value));
+    }
+private:
+    json_object* mObject;
+
+
+    json_object* detach()
+    {
+        json_object* ret = mObject;
+        mObject = nullptr;
+        return ret;
+    }
+
+    template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
+    static json_object* toJsonObject(T value)
+    {
+        return toJsonObject(static_cast<int64_t>(value));
+    }
+
+    static json_object* toJsonObject(std::int64_t value)
+    {
+        return json_object_new_int64(value);
+    }
+
+    static json_object* toJsonObject(std::uint64_t value)
+    {
+        if (value > INT64_MAX) {
+            throw CargoException("Value out of range");
+        }
+        return json_object_new_int64(value);
+    }
+
+    static json_object* toJsonObject(bool value)
+    {
+        return json_object_new_boolean(value);
+    }
+
+    static json_object* toJsonObject(double value)
+    {
+#if JSON_C_MINOR_VERSION >= 12
+        return json_object_new_double_s(value, std::to_string(value).c_str());
+#else
+        return json_object_new_double(value);
+#endif
+    }
+
+    static json_object* toJsonObject(const std::string& value)
+    {
+        return json_object_new_string(value.c_str());
+    }
+
+    static json_object* toJsonObject(char* value)
+    {
+        return json_object_new_string(value);
+    }
+
+    template<typename T>
+    static json_object* toJsonObject(const std::vector<T>& value)
+    {
+        json_object* array = json_object_new_array();
+        for (const T& v : value) {
+            json_object_array_add(array, toJsonObject(v));
+        }
+        return array;
+    }
+
+    template<typename T, std::size_t N>
+    static json_object* toJsonObject(const std::array<T, N>& values)
+    {
+        json_object* array = json_object_new_array();
+        for (const T& v : values) {
+            json_object_array_add(array, toJsonObject(v));
+        }
+        return array;
+    }
+
+    template<typename V>
+    static json_object* toJsonObject(const std::map<std::string, V>& values)
+    {
+        json_object* newMap = json_object_new_object();
+        for (const auto& item : values) {
+            json_object_object_add(newMap, item.first.c_str(), toJsonObject(item.second));
+        }
+        return newMap;
+    }
+
+    struct HelperVisitor
+    {
+        template<typename T>
+        static void visit(json_object* array, const T& value)
+        {
+            json_object_array_add(array, toJsonObject(value));
+        }
+    };
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    static json_object* toJsonObject(const T& values)
+    {
+        json_object* array = json_object_new_array();
+
+        HelperVisitor visitor;
+        visitFields(values, &visitor, array);
+        return array;
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    static json_object* toJsonObject(const T& value)
+    {
+        ToJsonVisitor visitor;
+        value.accept(visitor);
+        return visitor.detach();
+    }
+
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    static json_object* toJsonObject(const T& value)
+    {
+        return toJsonObject(static_cast<const typename std::underlying_type<T>::type>(value));
+    }
+
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_JSON_INTERNALS_TO_JSON_VISITOR_HPP
diff --git a/libs/cargo-json/libcargo-json.pc.in b/libs/cargo-json/libcargo-json.pc.in
new file mode 100644 (file)
index 0000000..18690b5
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: cargo Json library
+Version: @_LIB_VERSION_@
+Libs: -L${libdir} -l@PROJECT_NAME@ -l@JSON_C_LIBRARIES@
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@ -I@JSON_C_INCLUDE_DIRS@
diff --git a/libs/cargo-sqlite-json/CMakeLists.txt b/libs/cargo-sqlite-json/CMakeLists.txt
new file mode 100644 (file)
index 0000000..666cf2a
--- /dev/null
@@ -0,0 +1,43 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Pawel Kubik (p.kubik@samsung.com)
+#
+
+PROJECT(cargo-sqlite-json)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+FILE(GLOB HEADERS         *.hpp ${COMMON_FOLDER}/config.hpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+
+## Link libraries ##############################################################
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(FILES       ${HEADERS}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
diff --git a/libs/cargo-sqlite-json/cargo-sqlite-json.hpp b/libs/cargo-sqlite-json/cargo-sqlite-json.hpp
new file mode 100644 (file)
index 0000000..06125b5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author   Pawel Kubik (p.kubik@samsung.com)
+ * @defgroup libcargo-sqlite-json libcargo-sqlite-json
+ * @brief    cargo SQLite with Json interface
+ */
+
+#ifndef CARGO_SQLITE_JSON_CARGO_SQLITE_JSON_HPP
+#define CARGO_SQLITE_JSON_CARGO_SQLITE_JSON_HPP
+
+#include "cargo-json/internals/to-json-visitor.hpp"
+#include "cargo-sqlite/internals/to-kvstore-visitor.hpp"
+#include "cargo-json/internals/from-json-visitor.hpp"
+#include "cargo-sqlite/internals/from-kvstore-visitor.hpp"
+#include "cargo-sqlite/internals/from-kvstore-ignoring-visitor.hpp"
+#include "cargo-json/internals/fs-utils.hpp"
+// TODO: remove dependencies to other libraries internals
+
+namespace cargo {
+
+/*@{*/
+
+/**
+ * Load the visitable from KVStore with defaults given in json
+ *
+ * @param kvfile            path to the KVStore db
+ * @param json              path to json file with defaults
+ * @param visitable         visitable structure to save
+ * @param kvVisitableName   name of the structure inside the KVStore db
+ */
+template <class Cargo>
+void loadFromKVStoreWithJson(const std::string& kvfile,
+                             const std::string& json,
+                             Cargo& visitable,
+                             const std::string& kvVisitableName)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::KVStore store(kvfile);
+    internals::KVStore::Transaction transaction(store);
+    internals::FromJsonVisitor fromJsonVisitor(json);
+    internals::FromKVStoreIgnoringVisitor fromKVStoreVisitor(store, kvVisitableName);
+    visitable.accept(fromJsonVisitor);
+    visitable.accept(fromKVStoreVisitor);
+    transaction.commit();
+}
+
+/**
+ * Load the data from KVStore with defaults given in json file
+ *
+ * @param kvfile            path to the KVStore db
+ * @param jsonfile          path to json file with defaults
+ * @param visitable         visitable structure to save
+ * @param kvVisitableName   name of the structure inside the KVStore db
+ */
+template <class Cargo>
+void loadFromKVStoreWithJsonFile(const std::string& kvfile,
+                                 const std::string& jsonfile,
+                                 Cargo& visitable,
+                                 const std::string& kvVisitableName)
+{
+    std::string content;
+    if (!internals::fsutils::readFileContent(jsonfile, content)) {
+        throw CargoException("Could not load " + jsonfile);
+    }
+    try {
+        loadFromKVStoreWithJson(kvfile, content, visitable, kvVisitableName);
+    } catch (CargoException& e) {
+        throw CargoException("Error in " + jsonfile + ": " + e.what());
+    }
+}
+
+} // namespace cargo
+
+/*@}*/
+
+#endif // CARGO_SQLITE_JSON_CARGO_SQLITE_JSON_HPP
diff --git a/libs/cargo-sqlite-json/libcargo-sqlite-json.pc.in b/libs/cargo-sqlite-json/libcargo-sqlite-json.pc.in
new file mode 100644 (file)
index 0000000..4b70b0f
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: cargo KVStore with Json extension library
+Version: @_LIB_VERSION_@
+Libs:
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
diff --git a/libs/cargo-sqlite/CMakeLists.txt b/libs/cargo-sqlite/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c4e4900
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Pawel Kubik (p.kubik@samsung.com)
+#
+
+PROJECT(cargo-sqlite)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+FILE(GLOB_RECURSE SRCS *.cpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
+    SOVERSION   ${_LIB_SOVERSION_}
+    VERSION     ${_LIB_VERSION_}
+)
+
+ADD_DEPENDENCIES(${PROJECT_NAME} cargo-utils)
+
+## Link libraries ##############################################################
+PKG_CHECK_MODULES(SQLITE3 REQUIRED sqlite3)
+
+INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${LIBS_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${SQLITE3_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${SQLITE3_LIBRARIES})
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(TARGETS     ${PROJECT_NAME}
+        DESTINATION ${LIB_INSTALL_DIR}
+        COMPONENT   RuntimeLibraries)
+
+INSTALL(DIRECTORY . DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}
+        FILES_MATCHING PATTERN "*.hpp"
+                       PATTERN "CMakeFiles" EXCLUDE)
+
+INSTALL(FILES       ${COMMON_FOLDER}/config.hpp
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
diff --git a/libs/cargo-sqlite/cargo-sqlite.hpp b/libs/cargo-sqlite/cargo-sqlite.hpp
new file mode 100644 (file)
index 0000000..35bc7e0
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author   Pawel Kubik (p.kubik@samsung.com)
+ * @defgroup libcargo-sqlite libcargo-sqlite
+ * @brief    cargo KVStore interface
+ */
+
+#ifndef CARGO_SQLITE_CARGO_SQLITE_HPP
+#define CARGO_SQLITE_CARGO_SQLITE_HPP
+
+#include "cargo-sqlite/internals/to-kvstore-visitor.hpp"
+#include "cargo-sqlite/internals/from-kvstore-visitor.hpp"
+#include "cargo-sqlite/internals/from-kvstore-ignoring-visitor.hpp"
+
+namespace cargo {
+
+/*@{*/
+
+/**
+ * Loads a visitable structure from KVStore.
+ *
+ * @param filename      path to the KVStore db
+ * @param visitable     visitable structure to load
+ * @param visitableName name of the structure inside the KVStore db
+ */
+template <class Cargo>
+void loadFromKVStore(const std::string& filename, Cargo& visitable, const std::string& visitableName)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::KVStore store(filename);
+    internals::KVStore::Transaction transaction(store);
+    internals::FromKVStoreVisitor visitor(store, visitableName);
+    visitable.accept(visitor);
+    transaction.commit();
+}
+
+/**
+ * Saves the visitable to a KVStore.
+ *
+ * @param filename      path to the KVStore db
+ * @param visitable     visitable structure to save
+ * @param visitableName name of the structure inside the KVStore db
+ */
+template <class Cargo>
+void saveToKVStore(const std::string& filename, const Cargo& visitable, const std::string& visitableName)
+{
+    static_assert(internals::isVisitable<Cargo>::value, "Use CARGO_REGISTER macro");
+
+    internals::KVStore store(filename);
+    internals::KVStore::Transaction transaction(store);
+    internals::ToKVStoreVisitor visitor(store, visitableName);
+    visitable.accept(visitor);
+    transaction.commit();
+}
+
+} // namespace cargo
+
+/*@}*/
+
+#endif // CARGO_SQLITE_CARGO_SQLITE_HPP
diff --git a/libs/cargo-sqlite/internals/from-kvstore-ignoring-visitor.hpp b/libs/cargo-sqlite/internals/from-kvstore-ignoring-visitor.hpp
new file mode 100644 (file)
index 0000000..8727b3f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Visitor for loading from KVStore that doesn't fail on missing values
+ */
+
+#ifndef CARGO_SQLITE_INTERNALS_FROM_KVSTORE_IGNORING_VISITOR_HPP
+#define CARGO_SQLITE_INTERNALS_FROM_KVSTORE_IGNORING_VISITOR_HPP
+
+#include "cargo-sqlite/internals/from-kvstore-visitor-base.hpp"
+
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * A variant of KVStoreVisitor that ignore non-existing fields.
+ *
+ * Allows to partially update visited structures with fields that exist in the KVStore.
+ */
+class FromKVStoreIgnoringVisitor : public FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor> {
+public:
+    FromKVStoreIgnoringVisitor(KVStore& store, const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>(store, prefix)
+    {
+    }
+
+    FromKVStoreIgnoringVisitor(const FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>& visitor,
+                               const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>(visitor, prefix)
+    {
+    }
+
+    FromKVStoreIgnoringVisitor& operator=(const FromKVStoreIgnoringVisitor&) = delete;
+
+protected:
+    template<typename T>
+    void visitImpl(const std::string& name, T& value)
+    {
+        try {
+            getInternal(name, value);
+        } catch (const NoKeyException& e) {
+        } catch (const ContainerSizeException& e) {
+        }
+    }
+
+private:
+    FromKVStoreIgnoringVisitor(const FromKVStoreIgnoringVisitor& visitor,
+                               const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>(visitor.mStore, prefix)
+    {
+    }
+
+    template<typename T,
+             typename std::enable_if<isUnion<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        std::string type;
+        getInternal(key(name, "type"), type);
+        if (type.empty()) {
+            return;
+        }
+
+        FromKVStoreIgnoringVisitor visitor(*this, name);
+        value.accept(visitor);
+    }
+
+    template<typename T,
+             typename std::enable_if<!isUnion<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        FromKVStoreVisitorBase<FromKVStoreIgnoringVisitor>::visitImpl(name, value);
+    }
+
+    friend class FromKVStoreVisitorBase;
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_SQLITE_INTERNALS_FROM_KVSTORE_IGNORING_VISITOR_HPP
diff --git a/libs/cargo-sqlite/internals/from-kvstore-visitor-base.hpp b/libs/cargo-sqlite/internals/from-kvstore-visitor-base.hpp
new file mode 100644 (file)
index 0000000..8f86bd8
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Base of visitors for loading from KVStore
+ */
+
+#ifndef CARGO_SQLITE_INTERNALS_FROM_KVSTORE_VISITOR_BASE_HPP
+#define CARGO_SQLITE_INTERNALS_FROM_KVSTORE_VISITOR_BASE_HPP
+
+#include "cargo-sqlite/internals/kvstore.hpp"
+#include "cargo-sqlite/internals/kvstore-visitor-utils.hpp"
+#include "cargo/exception.hpp"
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/internals/is-streamable.hpp"
+#include "cargo/internals/is-union.hpp"
+#include "cargo/internals/visit-fields.hpp"
+#include <map>
+
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Base class for KVStore visitors.
+ *
+ * A curiously recurring template pattern example. This
+ * base-class provides a set of recursively called function templates. A child class can
+ * modify the base class behaviour in certain cases by defining:
+ *  - a set of functions that match those cases
+ *  - a template function that match any other case and calls the base class function
+ */
+template<typename RecursiveVisitor>
+class FromKVStoreVisitorBase {
+public:
+    FromKVStoreVisitorBase& operator=(const FromKVStoreVisitorBase&) = delete;
+
+    template<typename T>
+    void visit(const std::string& name, T& value)
+    {
+        static_cast<RecursiveVisitor*>(this)->visitImpl(key(mKeyPrefix, name), value);
+    }
+
+protected:
+    KVStore& mStore;
+    std::string mKeyPrefix;
+
+    template<typename T>
+    void visitImpl(const std::string& name, T& value)
+    {
+        getInternal(name, value);
+    }
+
+    FromKVStoreVisitorBase(KVStore& store, const std::string& prefix)
+        : mStore(store),
+          mKeyPrefix(prefix)
+    {
+    }
+
+    FromKVStoreVisitorBase(const FromKVStoreVisitorBase& visitor,
+                           const std::string& prefix)
+        : mStore(visitor.mStore),
+          mKeyPrefix(prefix)
+    {
+    }
+
+private:
+    template<typename T, typename std::enable_if<isStreamableIn<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        value = fromString<T>(mStore.get(name));
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        RecursiveVisitor visitor(*this, name);
+        value.accept(visitor);
+    }
+
+    template<typename T,
+             typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& value)
+    {
+        auto rawValue = static_cast<typename std::underlying_type<T>::type>(value);
+        static_cast<RecursiveVisitor*>(this)->visitImpl(name, rawValue);
+        value = static_cast<T>(rawValue);
+    }
+
+    template<typename T>
+    void getInternal(const std::string& name, std::vector<T>& values)
+    {
+        size_t storedSize = 0;
+        getInternal(name, storedSize);
+
+        if (storedSize == 0) {
+            return;
+        }
+
+        values.resize(storedSize);
+        for (size_t i = 0; i < storedSize; ++i) {
+            const std::string k = key(name, std::to_string(i));
+            if (!mStore.prefixExists(k)) {
+                throw InternalIntegrityException("Corrupted list serialization.");
+            }
+            static_cast<RecursiveVisitor*>(this)->visitImpl(k, values[i]);
+        }
+    }
+
+    template<typename T, size_t N>
+    void getInternal(const std::string& name, std::array<T, N>& values)
+    {
+        size_t storedSize = 0;
+        getInternal(name, storedSize);
+
+        if (storedSize != values.size()) {
+            throw ContainerSizeException("Size of stored array doesn't match provided one.");
+        }
+
+        for (size_t i = 0; i < storedSize; ++i) {
+            const std::string k = key(name, std::to_string(i));
+            if (!mStore.prefixExists(k)) {
+                throw InternalIntegrityException("Corrupted list serialization.");
+            }
+            static_cast<RecursiveVisitor*>(this)->visitImpl(k, values[i]);
+        }
+    }
+
+    template<typename V>
+    void getInternal(const std::string& name, std::map<std::string, V>& values)
+    {
+        size_t storedSize = 0;
+        getInternal(name, storedSize);
+
+        for (size_t i = 0; i < storedSize; ++i) {
+            std::string mapKey, k = key(name, i);
+            if (!mStore.prefixExists(k)) {
+                throw InternalIntegrityException("Corrupted map serialization.");
+            }
+            static_cast<RecursiveVisitor*>(this)->visitImpl(k, mapKey);
+            static_cast<RecursiveVisitor*>(this)->visitImpl(k + ".val", values[mapKey]);
+        }
+    }
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    void getInternal(const std::string& name, T& values)
+    {
+        size_t storedSize = 0;
+        getInternal(name, storedSize);
+
+        if (storedSize != std::tuple_size<T>::value) {
+            throw ContainerSizeException("Size of stored array doesn't match provided one.");
+        }
+
+        RecursiveVisitor recursiveVisitor(*this, name);
+        GetTupleVisitor visitor(recursiveVisitor);
+        visitFields(values, &visitor);
+    }
+
+    class GetTupleVisitor
+    {
+    public:
+        GetTupleVisitor(RecursiveVisitor& visitor) : mVisitor(visitor) {}
+
+        template<typename T>
+        void visit(T& value)
+        {
+            const std::string k = key(mVisitor.mKeyPrefix, idx);
+            if (!mVisitor.mStore.prefixExists(k)) {
+                throw InternalIntegrityException("Corrupted list serialization.");
+            }
+            mVisitor.visitImpl(k, value);
+
+            ++idx;
+        }
+
+    private:
+        RecursiveVisitor& mVisitor;
+        size_t idx = 0;
+    };
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_SQLITE_INTERNALS_FROM_KVSTORE_VISITOR_BASE_HPP
diff --git a/libs/cargo-sqlite/internals/from-kvstore-visitor.hpp b/libs/cargo-sqlite/internals/from-kvstore-visitor.hpp
new file mode 100644 (file)
index 0000000..2ab41e8
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Default visitor for loading from KVStore
+ */
+
+#ifndef CARGO_SQLITE_INTERNALS_FROM_KVSTORE_VISITOR_HPP
+#define CARGO_SQLITE_INTERNALS_FROM_KVSTORE_VISITOR_HPP
+
+#include "cargo-sqlite/internals/from-kvstore-visitor-base.hpp"
+
+
+namespace cargo {
+
+namespace internals {
+
+/**
+ * Default KVStore visitor.
+ */
+class FromKVStoreVisitor : public FromKVStoreVisitorBase<FromKVStoreVisitor> {
+public:
+    FromKVStoreVisitor(KVStore& store, const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreVisitor>(store, prefix)
+    {
+    }
+
+    FromKVStoreVisitor(const FromKVStoreVisitorBase<FromKVStoreVisitor>& visitor,
+                       const std::string& prefix)
+        : FromKVStoreVisitorBase<FromKVStoreVisitor>(visitor, prefix)
+    {
+    }
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_SQLITE_INTERNALS_FROM_KVSTORE_VISITOR_HPP
diff --git a/libs/cargo-sqlite/internals/kvstore-visitor-utils.hpp b/libs/cargo-sqlite/internals/kvstore-visitor-utils.hpp
new file mode 100644 (file)
index 0000000..653c5f3
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   KVStore visitors utilities
+ */
+
+#ifndef CARGO_SQLITE_INTERNALS_KVSTORE_VISITOR_UTILS_HPP
+#define CARGO_SQLITE_INTERNALS_KVSTORE_VISITOR_UTILS_HPP
+
+#include <vector>
+#include <string>
+#include <sstream>
+
+namespace cargo {
+
+namespace internals {
+
+template<typename T>
+T fromString(const std::string& strValue)
+{
+    std::istringstream iss(strValue);
+    T value;
+    iss >> value;
+    return value;
+}
+
+template<typename T>
+std::string toString(const T& value)
+{
+    std::ostringstream oss;
+    oss << value;
+    return oss.str();
+}
+
+/**
+ * Concatenates all parameters into one std::string.
+ * Uses '.' to connect the terms.
+ * @param args components of the string
+ * @tparam delim optional delimiter
+ * @tparam Args any type implementing str
+ * @return string created from he args
+ */
+template<char delim = '.', typename Arg1, typename ... Args>
+std::string key(const Arg1& a1, const Args& ... args)
+{
+    std::string ret = toString(a1);
+    std::initializer_list<std::string> strings {toString(args)...};
+    for (const std::string& s : strings) {
+        ret += delim + s;
+    }
+
+    return ret;
+}
+
+/**
+ * Function added for key function completeness.
+ *
+ * @tparam delim = '.' parameter not used, added for consistency
+ * @return empty string
+ */
+template<char delim = '.'>
+std::string key()
+{
+    return std::string();
+}
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_SQLITE_INTERNALS_KVSTORE_VISITOR_UTILS_HPP
diff --git a/libs/cargo-sqlite/internals/kvstore.cpp b/libs/cargo-sqlite/internals/kvstore.cpp
new file mode 100644 (file)
index 0000000..b746478
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Definition of a class for key-value storage in a sqlite3 database
+ */
+
+#include "config.hpp"
+
+#include "cargo-sqlite/internals/kvstore.hpp"
+#include "cargo/exception.hpp"
+
+#include <exception>
+#include <limits>
+#include <memory>
+#include <set>
+#include <cassert>
+#include <cstring>
+
+namespace cargo {
+
+namespace internals {
+
+namespace {
+
+const int AUTO_DETERM_SIZE = -1;
+const int FIRST_COLUMN = 0;
+
+struct ScopedReset {
+    ScopedReset(std::unique_ptr<sqlite3::Statement>& stmtPtr)
+        : mStmtPtr(stmtPtr) {}
+    ~ScopedReset()
+    {
+        mStmtPtr->reset();
+    }
+private:
+    std::unique_ptr<sqlite3::Statement>& mStmtPtr;
+};
+
+/**
+ * Escape characters used by the GLOB function.
+ */
+void sqliteEscapeStr(::sqlite3_context* context, int argc, ::sqlite3_value** values)
+{
+    char* inBuff = (char*)sqlite3_value_text(values[0]);
+    if (argc != 1 || inBuff == NULL) {
+        sqlite3_result_error(context, "SQL function escapeSequence() called with invalid arguments.\n", -1);
+        return;
+    }
+
+    std::string in(inBuff);
+    static const std::set<char> toEscape({'?', '*', '[', ']'});
+
+    // Compute the out size
+    auto isEscapeChar = [&](char c) {
+        return toEscape.count(c) == 1;
+    };
+    size_t numEscape = std::count_if(in.begin(),
+                                     in.end(),
+                                     isEscapeChar);
+    if (numEscape == 0) {
+        sqlite3_result_text(context, in.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+    }
+
+    // Escape characters
+    std::string out(in.size() + 2 * numEscape, 'x');
+    for (size_t i = 0, j = 0;
+            i < in.size();
+            ++i, ++j) {
+        if (isEscapeChar(in[i])) {
+            out[j] = '[';
+            ++j;
+            out[j] = in[i];
+            ++j;
+            out[j] = ']';
+        } else {
+            out[j] = in[i];
+        }
+    }
+    sqlite3_result_text(context, out.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+}
+
+} // namespace
+
+KVStore::Transaction::Transaction(KVStore& kvStore)
+    : mLock(kvStore.mMutex)
+    , mKVStore(kvStore)
+    , mIsOuter(kvStore.mTransactionDepth == 0)
+{
+    if (mKVStore.mIsTransactionCommited) {
+        throw CargoException("Previous transaction is not closed");
+    }
+    if (mIsOuter) {
+        mKVStore.mConn.exec("BEGIN EXCLUSIVE TRANSACTION");
+    }
+    ++mKVStore.mTransactionDepth;
+}
+
+KVStore::Transaction::~Transaction()
+{
+    --mKVStore.mTransactionDepth;
+    if (mIsOuter) {
+        if (mKVStore.mIsTransactionCommited) {
+            mKVStore.mIsTransactionCommited = false;
+        } else {
+            mKVStore.mConn.exec("ROLLBACK TRANSACTION");
+        }
+    }
+}
+
+void KVStore::Transaction::commit()
+{
+    if (mKVStore.mIsTransactionCommited) {
+        throw CargoException("Transaction already commited");
+    }
+    if (mIsOuter) {
+        mKVStore.mConn.exec("COMMIT TRANSACTION");
+        mKVStore.mIsTransactionCommited = true;
+    }
+}
+
+KVStore::KVStore(const std::string& path)
+    : mTransactionDepth(0),
+      mIsTransactionCommited(false),
+      mPath(path),
+      mConn(path)
+{
+    setupDb();
+    createFunctions();
+    prepareStatements();
+}
+
+KVStore::~KVStore()
+{
+    assert(mTransactionDepth == 0);
+}
+
+void KVStore::set(const std::string& key, const std::string& value)
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mSetValueStmt);
+
+    ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
+    ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
+
+
+    if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) {
+        throw CargoException("Error during stepping: " + mConn.getErrorMessage());
+    }
+    transaction.commit();
+}
+
+std::string KVStore::get(const std::string& key)
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mGetValueStmt);
+
+    ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+
+    int ret = ::sqlite3_step(mGetValueStmt->get());
+    if (ret == SQLITE_DONE) {
+        throw NoKeyException("No value corresponding to the key: " + key + "@" + mPath);
+    }
+    if (ret != SQLITE_ROW) {
+        throw CargoException("Error during stepping: " + mConn.getErrorMessage());
+    }
+
+    std::string value = reinterpret_cast<const char*>(
+            sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN));
+
+    transaction.commit();
+    return value;
+}
+
+void KVStore::setupDb()
+{
+    // called only from ctor, transaction is not needed
+    mConn.exec("CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
+}
+
+void KVStore::prepareStatements()
+{
+    mGetValueStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key = ? LIMIT 1"));
+    mGetKeyExistsStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 LIMIT 1"));
+    mGetKeyPrefixExistsStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1"));
+    mGetIsEmptyStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT 1 FROM data LIMIT 1"));
+    mSetValueStmt.reset(
+        new sqlite3::Statement(mConn, "INSERT OR REPLACE INTO data (key, value) VALUES (?,?)"));
+    mRemoveValuesStmt.reset(
+        new sqlite3::Statement(mConn, "DELETE FROM data WHERE key = ?1  OR key GLOB escapeStr(?1) ||'.*' "));
+    mGetKeysStmt.reset(
+        new sqlite3::Statement(mConn, "SELECT key FROM data"));
+}
+
+void KVStore::createFunctions()
+{
+    int ret = sqlite3_create_function(mConn.get(), "escapeStr", 1, SQLITE_ANY, 0, &sqliteEscapeStr, 0, 0);
+    if (ret != SQLITE_OK) {
+        throw CargoException("Error during creating functions: " + mConn.getErrorMessage());
+    }
+}
+
+void KVStore::clear()
+{
+    Transaction transaction(*this);
+    mConn.exec("DELETE FROM data");
+    transaction.commit();
+}
+
+bool KVStore::isEmpty()
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mGetIsEmptyStmt);
+
+    int ret = ::sqlite3_step(mGetIsEmptyStmt->get());
+    if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
+        throw CargoException("Error during stepping: " + mConn.getErrorMessage());
+    }
+
+    transaction.commit();
+    return ret == SQLITE_DONE;
+}
+
+bool KVStore::exists(const std::string& key)
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mGetKeyExistsStmt);
+
+    ::sqlite3_bind_text(mGetKeyExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+
+    int ret = ::sqlite3_step(mGetKeyExistsStmt->get());
+    if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
+        throw CargoException("Error during stepping: " + mConn.getErrorMessage());
+    }
+
+    transaction.commit();
+    return ret == SQLITE_ROW;
+}
+
+bool KVStore::prefixExists(const std::string& key)
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mGetKeyPrefixExistsStmt);
+
+    ::sqlite3_bind_text(mGetKeyPrefixExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT);
+
+    int ret = ::sqlite3_step(mGetKeyPrefixExistsStmt->get());
+    if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
+        throw CargoException("Error during stepping: " + mConn.getErrorMessage());
+    }
+
+    transaction.commit();
+    return ret == SQLITE_ROW;
+}
+
+void KVStore::remove(const std::string& key)
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mRemoveValuesStmt);
+
+    ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC);
+
+    if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) {
+        throw CargoException("Error during stepping: " + mConn.getErrorMessage());
+    }
+    transaction.commit();
+}
+
+std::vector<std::string> KVStore::getKeys()
+{
+    Transaction transaction(*this);
+    ScopedReset scopedReset(mGetKeysStmt);
+
+    std::vector<std::string> result;
+
+    for (;;) {
+        int ret = ::sqlite3_step(mGetKeysStmt->get());
+        if (ret == SQLITE_DONE) {
+            break;
+        }
+        if (ret != SQLITE_ROW) {
+            throw CargoException("Error during stepping: " + mConn.getErrorMessage());
+        }
+        const char* key = reinterpret_cast<const char*>(sqlite3_column_text(mGetKeysStmt->get(),
+                                                                            FIRST_COLUMN));
+        result.push_back(key);
+    }
+
+    transaction.commit();
+    return result;
+}
+
+} // namespace internals
+
+} // namespace cargo
diff --git a/libs/cargo-sqlite/internals/kvstore.hpp b/libs/cargo-sqlite/internals/kvstore.hpp
new file mode 100644 (file)
index 0000000..68e2ca0
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Declaration of a class for key-value storage in a sqlite3 database
+ */
+
+#ifndef CARGO_SQLITE_INTERNALS_KVSTORE_HPP
+#define CARGO_SQLITE_INTERNALS_KVSTORE_HPP
+
+#include "cargo-sqlite/sqlite3/connection.hpp"
+#include "cargo-sqlite/sqlite3/statement.hpp"
+
+#include <algorithm>
+#include <initializer_list>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <atomic>
+#include <utility>
+
+namespace cargo {
+
+namespace internals {
+
+class KVStore {
+
+public:
+    /**
+     * A guard struct for thread synchronization and transaction management.
+     */
+    class Transaction {
+    public:
+        Transaction(KVStore& store);
+        ~Transaction();
+
+        Transaction(const Transaction&) = delete;
+        Transaction& operator=(const Transaction&) = delete;
+
+        void commit();
+    private:
+        std::unique_lock<std::recursive_mutex> mLock;
+        KVStore& mKVStore;
+        bool mIsOuter;
+    };
+
+    /**
+     * @param path configuration database file path
+     */
+    explicit KVStore(const std::string& path);
+    ~KVStore();
+
+    KVStore(const KVStore&) = delete;
+    KVStore& operator=(const KVStore&) = delete;
+
+    /**
+     * Clears all the stored data
+     */
+    void clear();
+
+    /**
+     * @return Is there any data stored
+     */
+    bool isEmpty();
+
+    /**
+     * @param key string of the stored value
+     *
+     * @return Does this key exist in the database
+     */
+    bool exists(const std::string& key);
+
+    /**
+     * @param key string of the stored value
+     *
+     * @return Does a key starting with an argument exist in the database
+     */
+    bool prefixExists(const std::string& key);
+
+    /**
+     * Removes values corresponding to the passed key.
+     * Many values may correspond to one key, so many values may
+     * need to be deleted
+     *
+     * @param key string regexp of the stored values
+     */
+    void remove(const std::string& key);
+
+    /**
+     * Stores a single value corresponding to the passed key
+     *
+     * @param key string key of the value
+     * @param value value corresponding to the key
+     */
+    void set(const std::string& key, const std::string& value);
+
+    /**
+     * Gets the value corresponding to the key.
+     *
+     * @param key string key of the value
+     * @return value corresponding to the key
+     */
+    std::string get(const std::string& key);
+
+    /**
+     * Returns all stored keys.
+     */
+    std::vector<std::string> getKeys();
+
+private:
+    typedef std::lock_guard<std::recursive_mutex> Lock;
+
+    std::recursive_mutex mMutex;
+    size_t mTransactionDepth;
+    bool mIsTransactionCommited;
+
+    std::string mPath;
+    sqlite3::Connection mConn;
+    std::unique_ptr<sqlite3::Statement> mGetValueStmt;
+    std::unique_ptr<sqlite3::Statement> mGetKeyExistsStmt;
+    std::unique_ptr<sqlite3::Statement> mGetKeyPrefixExistsStmt;
+    std::unique_ptr<sqlite3::Statement> mGetIsEmptyStmt;
+    std::unique_ptr<sqlite3::Statement> mGetValueListStmt;
+    std::unique_ptr<sqlite3::Statement> mSetValueStmt;
+    std::unique_ptr<sqlite3::Statement> mRemoveValuesStmt;
+    std::unique_ptr<sqlite3::Statement> mGetKeysStmt;
+
+    void setupDb();
+    void prepareStatements();
+    void createFunctions();
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_SQLITE_INTERNALS_KVSTORE_HPP
diff --git a/libs/cargo-sqlite/internals/to-kvstore-visitor.hpp b/libs/cargo-sqlite/internals/to-kvstore-visitor.hpp
new file mode 100644 (file)
index 0000000..37ea3fe
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak (j.olszak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Visitor for saving to KVStore
+ */
+
+#ifndef CARGO_SQLITE_INTERNALS_TO_KVSTORE_VISITOR_HPP
+#define CARGO_SQLITE_INTERNALS_TO_KVSTORE_VISITOR_HPP
+
+#include "cargo-sqlite/internals/kvstore.hpp"
+#include "cargo-sqlite/internals/kvstore-visitor-utils.hpp"
+#include "cargo/internals/is-visitable.hpp"
+#include "cargo/internals/is-like-tuple.hpp"
+#include "cargo/internals/is-streamable.hpp"
+#include "cargo/internals/visit-fields.hpp"
+#include "cargo/exception.hpp"
+
+#include <map>
+
+namespace cargo {
+
+namespace internals {
+
+class ToKVStoreVisitor {
+
+public:
+    ToKVStoreVisitor(KVStore& store, const std::string& prefix)
+        : mStore(store),
+          mKeyPrefix(prefix)
+    {
+    }
+
+    ToKVStoreVisitor& operator=(const ToKVStoreVisitor&) = delete;
+
+    template<typename T>
+    void visit(const std::string& name, const T& value)
+    {
+        setInternal(key(mKeyPrefix, name), value);
+    }
+
+private:
+    KVStore& mStore;
+    std::string mKeyPrefix;
+
+    ToKVStoreVisitor(const ToKVStoreVisitor& visitor, const std::string& prefix)
+        : mStore(visitor.mStore),
+          mKeyPrefix(prefix)
+    {
+    }
+
+    template<typename T, typename std::enable_if<isStreamableOut<T>::value, int>::type = 0>
+    void setInternal(const std::string& name, const T& value)
+    {
+        mStore.set(name, toString(value));
+    }
+
+    template<typename T, typename std::enable_if<isVisitable<T>::value, int>::type = 0>
+    void setInternal(const std::string& name, const T& value)
+    {
+        ToKVStoreVisitor visitor(*this, name);
+        value.accept(visitor);
+    }
+
+    template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
+    void setInternal(const std::string& name, const T& value)
+    {
+        setInternal(name, static_cast<const typename std::underlying_type<T>::type>(value));
+    }
+
+    template <typename I>
+    void setRangeInternal(const std::string& name,
+                          const I& begin,
+                          const I& end,
+                          const size_t size) {
+        KVStore::Transaction transaction(mStore);
+
+        mStore.remove(name);
+        setInternal(name, size);
+        size_t i = 0;
+        for (auto it = begin; it != end; ++it) {
+            const std::string k = key(name, std::to_string(i));
+            setInternal(k, *it);
+            ++i;
+        }
+        transaction.commit();
+    }
+
+    template<typename T>
+    void setInternal(const std::string& name, const std::vector<T>& values)
+    {
+        setRangeInternal(name, values.begin(), values.end(), values.size());
+    }
+
+    template<typename T, std::size_t N>
+    void setInternal(const std::string& name, const std::array<T, N>& values) {
+        setRangeInternal(name, values.begin(), values.end(), N);
+    }
+
+    template<typename V>
+    void setInternal(const std::string& name, const std::map<std::string, V>& values) {
+        KVStore::Transaction transaction(mStore);
+
+        mStore.remove(name);
+        setInternal(name, values.size());
+        size_t i = 0;
+        for (const auto& it : values) {
+            const std::string k = key(name, i++);
+            setInternal(k, it.first);
+            setInternal(k + ".val", it.second);
+        }
+        transaction.commit();
+    }
+
+    template<typename T>
+    void setInternal(const std::string& name, const std::initializer_list<T>& values)
+    {
+        setRangeInternal(name, values.begin(), values.end(), values.size());
+    }
+
+    template<typename T, typename std::enable_if<isLikeTuple<T>::value, int>::type = 0>
+    void setInternal(const std::string& name, const T& values)
+    {
+        KVStore::Transaction transaction(mStore);
+
+        setInternal(name, std::tuple_size<T>::value);
+
+        ToKVStoreVisitor recursiveVisitor(*this, name);
+        SetTupleVisitor visitor(recursiveVisitor);
+        visitFields(values, &visitor);
+
+        transaction.commit();
+    }
+
+    struct SetTupleVisitor
+    {
+    public:
+        SetTupleVisitor(ToKVStoreVisitor& visitor) : mVisitor(visitor) {}
+
+        template<typename T>
+        void visit(T& value)
+        {
+            mVisitor.visit(std::to_string(idx), value);
+            ++idx;
+        }
+
+    private:
+        ToKVStoreVisitor& mVisitor;
+        size_t idx = 0;
+    };
+};
+
+} // namespace internals
+
+} // namespace cargo
+
+#endif // CARGO_SQLITE_INTERNALS_TO_KVSTORE_VISITOR_HPP
diff --git a/libs/cargo-sqlite/libcargo-sqlite.pc.in b/libs/cargo-sqlite/libcargo-sqlite.pc.in
new file mode 100644 (file)
index 0000000..dd8b1b9
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: cargo SQLite library
+Version: @_LIB_VERSION_@
+Libs: -L${libdir} -l@PROJECT_NAME@
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
diff --git a/libs/cargo-sqlite/sqlite3/connection.cpp b/libs/cargo-sqlite/sqlite3/connection.cpp
new file mode 100644 (file)
index 0000000..f2a283c
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Definition of the class managing a sqlite3 database connection
+ */
+
+#include "config.hpp"
+
+#include "cargo-sqlite/sqlite3/connection.hpp"
+#include "cargo/exception.hpp"
+
+namespace cargo {
+namespace sqlite3 {
+
+Connection::Connection(const std::string& path)
+{
+    if (path.empty()) {
+        // Sqlite creates temporary database in case of empty path
+        // but we want to forbid this.
+        throw CargoException("Error opening the database: empty path");
+    }
+    if (::sqlite3_open_v2(path.c_str(),
+                          &mDbPtr,
+                          SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+                          NULL) != SQLITE_OK) {
+        throw CargoException("Error opening the database: " + getErrorMessage());
+    }
+
+    if (mDbPtr == NULL) {
+        throw CargoException("Error opening the database: Unable to allocate memory.");
+    }
+}
+
+Connection::~Connection()
+{
+    ::sqlite3_close(mDbPtr);
+}
+
+void Connection::exec(const std::string& query)
+{
+    char* mess;
+    if (::sqlite3_exec(mDbPtr, query.c_str(), 0, 0, &mess) != SQLITE_OK) {
+        throw CargoException("Error during executing statement " + std::string(mess));
+    }
+}
+
+::sqlite3* Connection::get()
+{
+    return mDbPtr;
+}
+
+std::string Connection::getErrorMessage()
+{
+    return std::string(sqlite3_errmsg(mDbPtr));
+}
+
+} // namespace sqlite3
+} // namespace cargo
diff --git a/libs/cargo-sqlite/sqlite3/connection.hpp b/libs/cargo-sqlite/sqlite3/connection.hpp
new file mode 100644 (file)
index 0000000..c32cb96
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Declaration of the class managing a sqlite3 database connection
+ */
+
+#ifndef CARGO_SQLITE_SQLITE3_CONNECTION_HPP
+#define CARGO_SQLITE_SQLITE3_CONNECTION_HPP
+
+#include <sqlite3.h>
+#include <string>
+
+namespace cargo {
+namespace sqlite3 {
+
+struct Connection {
+    /**
+     * @param path database file path
+     */
+    Connection(const std::string& path);
+    Connection(const Connection&) = delete;
+    ~Connection();
+
+    /**
+     * @return pointer to the corresponding sqlite3 database object
+     */
+    ::sqlite3* get();
+
+    /**
+     * @return last error message in the database
+     */
+    std::string getErrorMessage();
+
+    /**
+     * Executes the query in the database.
+     *
+     * @param query query to be executed
+     */
+    void exec(const std::string& query);
+
+private:
+    ::sqlite3* mDbPtr;
+};
+
+} // namespace sqlite3
+} // namespace cargo
+
+#endif // CARGO_SQLITE_SQLITE3_CONNECTION_HPP
diff --git a/libs/cargo-sqlite/sqlite3/statement.cpp b/libs/cargo-sqlite/sqlite3/statement.cpp
new file mode 100644 (file)
index 0000000..e5a4caa
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Definition of the class managing a sqlite3 statement
+ */
+
+#include "config.hpp"
+
+#include "cargo-sqlite/sqlite3/statement.hpp"
+#include "cargo/exception.hpp"
+
+
+namespace cargo {
+namespace sqlite3 {
+
+Statement::Statement(sqlite3::Connection& connRef, const std::string& query)
+    : mConnRef(connRef)
+{
+    if (::sqlite3_prepare_v2(connRef.get(),
+                             query.c_str(),
+                             query.size(),
+                             &mStmtPtr,
+                             NULL)
+            != SQLITE_OK) {
+        throw CargoException("Error during preparing statement " +
+                              mConnRef.getErrorMessage());
+    }
+
+    if (mStmtPtr == NULL) {
+        throw CargoException("Wrong query: " + query);
+    }
+}
+
+Statement::Statement::~Statement()
+{
+    if (::sqlite3_finalize(mStmtPtr) != SQLITE_OK) {
+        throw CargoException("Error during finalizing statement " +
+                              mConnRef.getErrorMessage());
+    }
+}
+
+sqlite3_stmt* Statement::get()
+{
+    return mStmtPtr;
+}
+
+void Statement::reset()
+{
+    if (::sqlite3_clear_bindings(mStmtPtr) != SQLITE_OK) {
+        throw CargoException("Error unbinding statement: " +
+                              mConnRef.getErrorMessage());
+    }
+
+    if (::sqlite3_reset(mStmtPtr) != SQLITE_OK) {
+        throw CargoException("Error reseting statement: " +
+                              mConnRef.getErrorMessage());
+    }
+}
+
+} // namespace sqlite3
+} // namespace cargo
diff --git a/libs/cargo-sqlite/sqlite3/statement.hpp b/libs/cargo-sqlite/sqlite3/statement.hpp
new file mode 100644 (file)
index 0000000..01b5400
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Declaration of the class managing a sqlite3 statement
+ */
+
+#ifndef CARGO_SQLITE_SQLITE3_STATEMENT_HPP
+#define CARGO_SQLITE_SQLITE3_STATEMENT_HPP
+
+#include "cargo-sqlite/sqlite3/connection.hpp"
+
+#include <sqlite3.h>
+#include <string>
+
+namespace cargo {
+namespace sqlite3 {
+
+struct Statement {
+
+    /**
+     * @param connRef reference to the Connection object
+     * @param query query to be executed
+     */
+    Statement(sqlite3::Connection& connRef, const std::string& query);
+    ~Statement();
+
+    /**
+     * @return pointer to the sqlite3 statement
+     */
+    sqlite3_stmt* get();
+
+    /**
+     * Clears the bindings and resets the statement.
+     * After this the statement can be executed again
+     */
+    void reset();
+
+private:
+    ::sqlite3_stmt* mStmtPtr;
+    sqlite3::Connection& mConnRef;
+};
+
+} // namespace sqlite3
+} // namespace cargo
+
+#endif // CARGO_SQLITE_SQLITE3_STATEMENT_HPP
diff --git a/libs/cargo-validator/CMakeLists.txt b/libs/cargo-validator/CMakeLists.txt
new file mode 100644 (file)
index 0000000..34f374e
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (c) 2015 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Maciej Karpiuk (m.karpiuk2@samsung.com)
+#
+
+PROJECT(cargo-validator)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+FILE(GLOB HEADERS           *.hpp)
+FILE(GLOB HEADERS_INTERNALS internals/*.hpp)
+FILE(GLOB SRCS              *.cpp)
+FILE(GLOB SRCS_INTERNALS    internals/*.cpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_INTERNALS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
+    SOVERSION   ${_LIB_SOVERSION_}
+    VERSION     ${_LIB_VERSION_}
+)
+
+ADD_DEPENDENCIES(${PROJECT_NAME} cargo-utils)
+
+## Link libraries ##############################################################
+INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${CARGO_DEPS_INCLUDE_DIRS})
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(TARGETS     ${PROJECT_NAME}
+        DESTINATION ${LIB_INSTALL_DIR}
+        COMPONENT   RuntimeLibraries)
+
+INSTALL(FILES       ${HEADERS}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
+
+INSTALL(FILES       ${HEADERS_INTERNALS}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}/internals)
+
diff --git a/libs/cargo-validator/exception.hpp b/libs/cargo-validator/exception.hpp
new file mode 100644 (file)
index 0000000..42856af
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Maciej Karpiuk (m.karpiuk2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @brief   Exceptions for libcargo-validator
+ */
+
+#ifndef CARGO_VALIDATOR_EXCEPTION_HPP
+#define CARGO_VALIDATOR_EXCEPTION_HPP
+
+#include "cargo/exception.hpp"
+#include <stdexcept>
+
+namespace cargo {
+namespace validator {
+/**
+ * Structure verification exception
+ */
+struct VerificationException: public CargoException {
+
+    VerificationException(const std::string& error) : CargoException(error) {}
+};
+
+} // namespace validator
+} // namespace cargo
+
+#endif // CARGO_VALIDATOR_EXCEPTION_HPP
diff --git a/libs/cargo-validator/internals/is-validable.hpp b/libs/cargo-validator/internals/is-validable.hpp
new file mode 100644 (file)
index 0000000..94a6595
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Maciej Karpiuk (m.karpiuk2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @brief   Internal validation helper
+ */
+
+#ifndef CARGO_VALIDATOR_INTERNALS_IS_VALIDABLE_HPP
+#define CARGO_VALIDATOR_INTERNALS_IS_VALIDABLE_HPP
+
+#include <type_traits>
+#include "cargo-validator/internals/validator-visitor.hpp"
+
+namespace cargo {
+
+template <typename T>
+struct isValidableHelper__ {
+    template <typename C> static std::true_type
+    test(decltype(std::declval<C>().template accept(cargo::validator::ValidatorVisitor()))*);
+
+    template <typename C> static std::false_type
+    test(...);
+
+    static constexpr bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
+};
+
+/**
+ * Helper for compile-time checking against existance of method 'accept' with ValidatorVisitor arg.
+ */
+template <typename T>
+struct isValidable : public std::integral_constant<bool, isValidableHelper__<T>::value> {};
+
+} // namespace cargo
+
+#endif // CARGO_VALIDATOR_INTERNALS_IS_VALIDABLE_HPP
diff --git a/libs/cargo-validator/internals/validator-visitor.hpp b/libs/cargo-validator/internals/validator-visitor.hpp
new file mode 100644 (file)
index 0000000..f4d053f
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Maciej Karpiuk (m.karpiuk2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @brief   validator visitor
+ */
+
+#ifndef CARGO_VALIDATOR_INTERNALS_VALIDATOR_VISITOR_HPP
+#define CARGO_VALIDATOR_INTERNALS_VALIDATOR_VISITOR_HPP
+
+#include "cargo-validator/exception.hpp"
+#include "cargo/internals/visit-fields.hpp"
+
+#include <string>
+#include <functional>
+
+namespace cargo {
+namespace validator {
+
+class ValidatorVisitor {
+public:
+    ValidatorVisitor& operator=(const ValidatorVisitor&) = delete;
+
+    template<typename T, typename Fn>
+    void visit(const Fn &func,
+               const std::string &field_name,
+               const T& value)
+    {
+        if (!func(value)) {
+            const std::string msg = "validation failed on field: " +
+                                    field_name +
+                                    "(" + std::string(typeid(value).name()) + ")";
+            throw VerificationException(msg);
+        }
+    }
+
+    template<typename A, typename B, typename Fn>
+    void visit(const Fn &func,
+               const std::string &field_A_name,
+               const A& arg_A,
+               const std::string &field_B_name,
+               const B& arg_B)
+    {
+        if (!func(arg_A, arg_B)) {
+            const std::string msg = "validation failed: improper fields " +
+                                    field_A_name + " and " + field_B_name +
+                                    " relationship.";
+            throw VerificationException(msg);
+        }
+    }
+};
+
+} // namespace validator
+} // namespace cargo
+
+#endif // CARGO_VALIDATOR_INTERNALS_VALIDATOR_VISITOR_HPP
diff --git a/libs/cargo-validator/libcargo-validator.pc.in b/libs/cargo-validator/libcargo-validator.pc.in
new file mode 100644 (file)
index 0000000..6ebcc53
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: Cargo Validator library
+Version: @_LIB_VERSION_@
+Libs: -L${libdir} -l@PROJECT_NAME@
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
diff --git a/libs/cargo-validator/validator.cpp b/libs/cargo-validator/validator.cpp
new file mode 100644 (file)
index 0000000..62c3271
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Maciej Karpiuk (m.karpiuk2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @brief  Pre-defined validation check functions
+*/
+
+#include "config.hpp"
+
+#include "cargo-validator/validator.hpp"
+#include "utils/fs.hpp"
+#include "utils/exception.hpp"
+
+using namespace utils;
+
+bool cargo::validator::isNonEmptyString(const std::string &s)
+{
+    return !s.empty();
+}
+
+bool cargo::validator::isAbsolutePath(const std::string &s)
+{
+    return utils::isAbsolute(s);
+}
+
+bool cargo::validator::isFilePresent(const std::string &s)
+{
+    return utils::isRegularFile(s);
+}
+
+bool cargo::validator::isDirectoryPresent(const std::string &s)
+{
+    return utils::isDir(s);
+}
diff --git a/libs/cargo-validator/validator.hpp b/libs/cargo-validator/validator.hpp
new file mode 100644 (file)
index 0000000..2053766
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Maciej Karpiuk (m.karpiuk2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author      Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @defgroup    libcargo-validator libcargo-validator
+ * @ingroup     libCargo
+ * @brief       Cargo validation functions
+ */
+#ifndef CARGO_VALIDATOR_VALIDATOR_HPP
+#define CARGO_VALIDATOR_VALIDATOR_HPP
+
+#include "cargo/fields.hpp"
+#include "cargo-validator/exception.hpp"
+#include "cargo-validator/internals/validator-visitor.hpp"
+#include "cargo-validator/internals/is-validable.hpp"
+
+namespace cargo {
+
+/*@{*/
+
+
+#define CARGO_VALIDATE(...)                                     \
+    void accept(cargo::validator::ValidatorVisitor v) const {   \
+        __VA_ARGS__                                             \
+    }                                                           \
+
+#define CARGO_CHECK(func, ...)                                  \
+    GENERATE_ELEMENTS_FOR_FUNC_(func, __VA_ARGS__)              \
+
+#define GENERATE_ELEMENTS_FOR_FUNC_(func, ...)                    \
+   BOOST_PP_LIST_FOR_EACH(GENERATE_ELEMENT_FOR_FUNC_,             \
+                          func,                                   \
+                          BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__)) \
+
+#define GENERATE_ELEMENT_FOR_FUNC_(r, func, element)              \
+    v.visit(func, #element, element);                             \
+
+#define CARGO_COMPARE(func, arg_A, arg_B)                         \
+    v.visit(func, #arg_A, arg_A, #arg_B, arg_B);                  \
+
+namespace validator
+{
+/**
+ * pre-defined checks
+ */
+bool isNonEmptyString(const std::string &string);
+bool isAbsolutePath(const std::string &path);
+bool isFilePresent(const std::string &path);
+bool isDirectoryPresent(const std::string &path);
+// add more checks here..
+
+
+
+/**
+ * Validate given structure if fields match the requirements.
+ *
+ * @param visitable     visitable structure to check
+ */
+template <class Cargo>
+void validate(const Cargo& visitable)
+{
+    static_assert(isValidable<Cargo>::value, "Use CARGO_VALIDATE macro");
+
+    ValidatorVisitor visitor;
+    visitable.accept(visitor);
+}
+
+} // namespace validator
+} // namespace cargo
+
+/*@}*/
+
+#endif // CARGO_VALIDATOR_VALIDATOR_HPP
diff --git a/libs/cargo/CMakeLists.txt b/libs/cargo/CMakeLists.txt
new file mode 100644 (file)
index 0000000..be786fa
--- /dev/null
@@ -0,0 +1,46 @@
+# Copyright (c) 2014 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Dariusz Michaluk (d.michaluk@samsung.com)
+#
+
+PROJECT(cargo)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the lib${PROJECT_NAME}...")
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+
+## Link libraries ##############################################################
+INCLUDE_DIRECTORIES(${LIBS_FOLDER})
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(DIRECTORY . DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME}
+        FILES_MATCHING PATTERN "*.hpp"
+                       PATTERN "CMakeFiles" EXCLUDE)
+
+INSTALL(FILES       ${COMMON_FOLDER}/config.hpp
+        DESTINATION ${INCLUDE_INSTALL_DIR}/${PROJECT_NAME})
diff --git a/libs/cargo/exception.hpp b/libs/cargo/exception.hpp
new file mode 100644 (file)
index 0000000..110d0e2
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ * @brief   Exceptions for libcargo
+ */
+
+#ifndef CARGO_EXCEPTION_HPP
+#define CARGO_EXCEPTION_HPP
+
+#include <stdexcept>
+
+namespace cargo {
+
+/**
+ * Base class for exceptions in libcargo.
+ */
+struct CargoException: public std::runtime_error {
+
+    CargoException(const std::string& error) : std::runtime_error(error) {}
+};
+
+/**
+ * No such key error.
+ */
+struct NoKeyException: public CargoException {
+
+    NoKeyException(const std::string& error) : CargoException(error) {}
+};
+
+/**
+ * Invalid integral type integrity error.
+ */
+struct InternalIntegrityException: public CargoException {
+
+    InternalIntegrityException(const std::string& error) : CargoException(error) {}
+};
+
+/**
+ * Container size does not match.
+ */
+struct ContainerSizeException: public CargoException {
+
+    ContainerSizeException(const std::string& error): CargoException(error) {}
+};
+
+} // namespace cargo
+
+#endif // CARGO_EXCEPTION_HPP
diff --git a/libs/cargo/fields-union.hpp b/libs/cargo/fields-union.hpp
new file mode 100644 (file)
index 0000000..5a49783
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Mateusz Malicki (m.malicki2@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Mateusz Malicki (m.malicki2@samsung.com)
+ * @brief   Macros for declare cargo fields
+ */
+
+#ifndef CARGO_FIELDS_UNION_HPP
+#define CARGO_FIELDS_UNION_HPP
+
+#include "cargo/fields.hpp"
+#include "cargo/exception.hpp"
+
+#include <utility>
+#include <boost/any.hpp>
+
+class DisableMoveAnyWrapper : public boost::any
+{
+    public:
+        DisableMoveAnyWrapper() {}
+        DisableMoveAnyWrapper(const DisableMoveAnyWrapper& any)
+            : boost::any(static_cast<const boost::any&>(any)) {};
+        DisableMoveAnyWrapper& operator=(DisableMoveAnyWrapper&& any) = delete;
+        DisableMoveAnyWrapper& operator=(const DisableMoveAnyWrapper& any) {
+            static_cast<boost::any&>(*this) = static_cast<const boost::any&>(any);
+            return *this;
+        }
+};
+
+/**
+ * @ingroup libCargo
+ *
+ * Use this macro to declare and register cargo fields.
+ *
+ * Example of fields registration:
+ * @code
+ *  struct Foo {
+ *      std::string bar;
+ *
+ *      CARGO_REGISTER
+ *      (
+ *          bar,
+ *      )
+ *  };
+ *
+ *  struct Config
+ *  {
+ *      CARGO_DECLARE_UNION
+ *      (
+ *          Foo,
+ *          int
+ *      )
+ *  };
+ * @endcode
+ *
+ * Example of valid configuration:
+ * @code
+ *   1. {
+ *        "type": "Foo",
+ *        "value": { "bar": "some string" }
+ *      }
+ *   2. {
+ *        "type": "int",
+ *        "value": 1
+ *      }
+ * @endcode
+ *
+ * Usage of existing bindings:
+ * @code
+ *   Config config;
+ *   // ...
+ *   if (config.isSet()) {
+ *      if (config.is<Foo>()) {
+ *          Foo& foo = config.as<Foo>();
+ *          // ...
+ *      }
+ *      if (config.is<int>())) {
+ *          int field = config.as<int>();
+ *          // ...
+ *      }
+ *   } else {
+ *     // Config is not set
+ *     Foo foo({"some string"});
+ *     config.set(foo);
+ *     config.set(std::move(foo));         //< copy sic!
+ *     config.set(Foo({"some string"}));
+ *   }
+ * @endcode
+ */
+#define CARGO_DECLARE_UNION(...)                                                                \
+private:                                                                                        \
+    DisableMoveAnyWrapper mCargoDeclareField;                                                   \
+                                                                                                \
+    template<typename Visitor>                                                                  \
+    void visitOption(Visitor& v, const std::string& name) {                                     \
+        GENERATE_CODE(GENERATE_UNION_VISIT__, __VA_ARGS__)                                      \
+        throw cargo::CargoException("Union type error. Unsupported type");                      \
+    }                                                                                           \
+    template<typename Visitor>                                                                  \
+    void visitOption(Visitor& v, const std::string& name) const {                               \
+        GENERATE_CODE(GENERATE_UNION_VISIT_CONST__, __VA_ARGS__)                                \
+        throw cargo::CargoException("Union type error. Unsupported type");                      \
+    }                                                                                           \
+    std::string getOptionName() const {                                                         \
+        GENERATE_CODE(GENERATE_UNION_NAME__, __VA_ARGS__)                                       \
+        return std::string();                                                                   \
+    }                                                                                           \
+    boost::any& getHolder() {                                                                   \
+        return static_cast<boost::any&>(mCargoDeclareField);                                    \
+    }                                                                                           \
+    const boost::any& getHolder() const {                                                       \
+        return static_cast<const boost::any&>(mCargoDeclareField);                              \
+    }                                                                                           \
+public:                                                                                         \
+                                                                                                \
+    template<typename Visitor>                                                                  \
+    void accept(Visitor v) {                                                                    \
+        std::string name;                                                                       \
+        v.visit("type", name);                                                                  \
+        visitOption(v, name);                                                                   \
+    }                                                                                           \
+                                                                                                \
+    template<typename Visitor>                                                                  \
+    void accept(Visitor v) const {                                                              \
+        const std::string name = getOptionName();                                               \
+        if (name.empty()) {                                                                     \
+           throw cargo::CargoException("Type is not set");                                      \
+        }                                                                                       \
+        v.visit("type", name);                                                                  \
+        visitOption(v, name);                                                                   \
+    }                                                                                           \
+                                                                                                \
+    template<typename Type>                                                                     \
+    bool is() const {                                                                           \
+        return boost::any_cast<Type>(&getHolder()) != NULL;                                     \
+    }                                                                                           \
+    template<typename Type>                                                                     \
+    typename std::enable_if<!std::is_const<Type>::value, Type>::type& as() {                    \
+        if (getHolder().empty()) {                                                              \
+            throw cargo::CargoException("Type is not set");                                     \
+        }                                                                                       \
+        return boost::any_cast<Type&>(getHolder());                                             \
+    }                                                                                           \
+    template<typename Type>                                                                     \
+    const Type& as() const {                                                                    \
+        if (getHolder().empty()) {                                                              \
+            throw cargo::CargoException("Type is not set");                                     \
+        }                                                                                       \
+        return boost::any_cast<const Type&>(getHolder());                                       \
+    }                                                                                           \
+    bool isSet() {                                                                              \
+        return !getOptionName().empty();                                                        \
+    }                                                                                           \
+    template<typename Type>                                                                     \
+    Type& set(const Type& src) {                                                                \
+        getHolder() = std::forward<const Type>(src);                                            \
+        if (getOptionName().empty()) {                                                          \
+           throw cargo::CargoException("Unsupported type");                                     \
+        }                                                                                       \
+        return as<Type>();                                                                      \
+    }                                                                                           \
+
+#define GENERATE_CODE(MACRO, ...)                                                               \
+    BOOST_PP_LIST_FOR_EACH(MACRO, _, BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__))
+
+#define GENERATE_UNION_VISIT__(r, _, TYPE_)                                                     \
+    if (#TYPE_ == name) {                                                                       \
+        v.visit("value", set(std::move(TYPE_())));                                              \
+        return;                                                                                 \
+    }
+
+#define GENERATE_UNION_VISIT_CONST__(r, _, TYPE_)                                               \
+    if (#TYPE_ == name) {                                                                       \
+        v.visit("value", as<const TYPE_>());                                                    \
+        return;                                                                                 \
+    }
+
+#define GENERATE_UNION_NAME__(r, _, TYPE_)                                                      \
+    if (is<TYPE_>()) {                                                                          \
+        return #TYPE_;                                                                          \
+    }
+
+#endif // CARGO_FIELDS_UNION_HPP
diff --git a/libs/cargo/fields.hpp b/libs/cargo/fields.hpp
new file mode 100644 (file)
index 0000000..4524d13
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author     Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @addtogroup libcargo libcargo
+ * @brief      C++ library for handling serialization
+ */
+
+#ifndef CARGO_FIELDS_HPP
+#define CARGO_FIELDS_HPP
+
+#include <boost/preprocessor/variadic/to_list.hpp>
+#include <boost/preprocessor/list/for_each.hpp>
+
+#include "cargo/types.hpp"
+
+#if BOOST_PP_VARIADICS != 1
+#error variadic macros not supported
+#endif
+
+/**
+ * @ingroup libcargo
+ * Register empty cargo class
+ */
+#define CARGO_REGISTER_EMPTY                                       \
+    template<typename Visitor>                                     \
+    static void accept(Visitor ) {                                 \
+    }                                                              \
+
+/**
+ * @ingroup libcargo
+ *
+ * Registers cargo fields within class.
+ *
+ * Example:
+ * @code
+ *   struct Foo
+ *   {
+ *       std::string bar;
+ *       std::vector<int> tab;
+ *
+ *       // SubConfigA must also register config fields
+ *       SubConfigA sub_a;
+ *
+ *       // SubConfigB must also register config fields
+ *       std::vector<SubConfigB> tab_sub;
+ *
+ *       CARGO_REGISTER
+ *       (
+ *           bar,
+ *           tab,
+ *           sub_a
+ *       )
+ *   };
+ * @endcode
+ */
+#define CARGO_REGISTER(...)                                        \
+    template<typename Visitor>                                     \
+    void accept(Visitor v) {                                       \
+        GENERATE_ELEMENTS__(__VA_ARGS__)                           \
+    }                                                              \
+    template<typename Visitor>                                     \
+    void accept(Visitor v) const {                                 \
+        GENERATE_ELEMENTS__(__VA_ARGS__)                           \
+    }                                                              \
+
+/**
+ * @ingroup libcargo
+ *
+ * Registers new cargo fields within child class.
+ *
+ * CARGO_REGISTER doesn't preserve cargo fields registered
+ * in the base class. Use this macro instead.
+ *
+ * Example:
+ * @code
+ *   struct Foo
+ *   {
+ *       std::string bar;
+ *       std::vector<int> tab;
+ *
+ *       // SubConfigA must also register config fields
+ *       SubConfigA sub_a;
+ *
+ *       // SubConfigB must also register config fields
+ *       std::vector<SubConfigB> tab_sub;
+ *
+ *       CARGO_REGISTER
+ *       (
+ *           bar,
+ *           tab,
+ *           sub_a
+ *       )
+ *   };
+ *
+ *   struct FooChild : Foo
+ *   {
+ *       std::string newField;
+ *
+ *       CARGO_EXTEND(Foo)
+ *       (
+ *           newField
+ *       )
+ *   };
+ *
+ * @endcode
+ */
+#define CARGO_EXTEND(BASE)                                         \
+    typedef BASE ParentVisitor;                                    \
+    __CARGO_EXTEND                                                 \
+
+#define __CARGO_EXTEND(...)                                        \
+    template<typename Visitor>                                     \
+    void accept(Visitor v) {                                       \
+        GENERATE_ELEMENTS__(__VA_ARGS__)                           \
+        ParentVisitor::accept(v);                                  \
+    }                                                              \
+    template<typename Visitor>                                     \
+    void accept(Visitor v) const {                                 \
+        GENERATE_ELEMENTS__(__VA_ARGS__)                           \
+        ParentVisitor::accept(v);                                  \
+    }                                                              \
+
+#define GENERATE_ELEMENTS__(...)                                   \
+    BOOST_PP_LIST_FOR_EACH(GENERATE_ELEMENT__,                     \
+                           _,                                      \
+                           BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__)) \
+
+#define GENERATE_ELEMENT__(r, _, element)                          \
+    v.visit(#element, element);                                    \
+
+#endif // CARGO_FIELDS_HPP
diff --git a/libs/cargo/internals/is-like-tuple.hpp b/libs/cargo/internals/is-like-tuple.hpp
new file mode 100644 (file)
index 0000000..7bb3299
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Tuple or pair checker
+ */
+
+#ifndef CARGO_INTERNALS_IS_LIKE_TUPLE_HPP
+#define CARGO_INTERNALS_IS_LIKE_TUPLE_HPP
+
+#include <tuple>
+
+namespace cargo {
+namespace internals {
+
+template<typename T>
+struct isLikeTuple {
+    static constexpr bool value = std::false_type::value;
+};
+
+template<typename ... R>
+struct isLikeTuple<std::tuple<R...>> {
+    static constexpr bool value = std::true_type::value;
+};
+
+template<typename ... R>
+struct isLikeTuple<std::pair<R...>> {
+    static constexpr bool value = std::true_type::value;
+};
+
+} // namespace internals
+} // namespace cargo
+
+#endif // CARGO_INTERNALS_IS_LIKE_TUPLE_HPP
+
diff --git a/libs/cargo/internals/is-streamable.hpp b/libs/cargo/internals/is-streamable.hpp
new file mode 100644 (file)
index 0000000..7b9922f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Check whether type is accepted by streaming operators
+ */
+
+#ifndef CARGO_INTERNALS_IS_STREAMABLE_HPP
+#define CARGO_INTERNALS_IS_STREAMABLE_HPP
+
+#include <sstream>
+
+namespace cargo {
+namespace internals {
+
+template<typename T>
+struct isStreamableIn {
+    static std::istream stream;
+
+    template <typename C> static std::true_type
+    test(decltype(stream >> (*(static_cast<C*>(nullptr)))));
+
+    template <typename C> static std::false_type
+    test(...);
+
+    static constexpr bool value = std::is_same<decltype(test<T>(stream)), std::true_type>::value;
+};
+
+template<typename T>
+struct isStreamableOut {
+    static std::ostream stream;
+
+    template <typename C> static std::true_type
+    test(decltype(stream << (*(static_cast<C*>(nullptr)))));
+
+    template <typename C> static std::false_type
+    test(...);
+
+    static constexpr bool value = std::is_same<decltype(test<T>(stream)), std::true_type>::value;
+};
+
+} // internals
+} // namespace cargo
+
+#endif // CARGO_INTERNALS_IS_STREAMABLE_HPP
+
diff --git a/libs/cargo/internals/is-union.hpp b/libs/cargo/internals/is-union.hpp
new file mode 100644 (file)
index 0000000..303348b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief   Internal configuration helper
+ */
+
+#ifndef CARGO_INTERNALS_IS_UNION_HPP
+#define CARGO_INTERNALS_IS_UNION_HPP
+
+#include "cargo/internals/is-visitable.hpp"
+
+namespace cargo {
+namespace internals {
+
+// generic member checker, start
+template <typename T, typename F>
+struct has_member_impl {
+    template <typename C>
+    static std::true_type check(typename F::template checker__<C>* =0);
+
+    template <typename C>
+    static std::false_type check(...);
+
+    static const bool value = std::is_same<decltype(check<T>(0)), std::true_type>::value;
+};
+
+template <typename T, typename F>
+struct has_member : public std::integral_constant<bool, has_member_impl<T, F>::value> {};
+// generic member checker, end
+
+
+template <typename X>
+struct check_union : isVisitable<X> {
+    template <typename T,
+         //list of function union must implement
+         const X& (T::*)() const = &T::as,
+         X& (T::*)(const X& src) = &T::set,
+         bool (T::*)() = &T::isSet
+    >
+    struct checker__ {};
+};
+template<typename T>
+struct isUnion : has_member<T, check_union<T>> {};
+
+//Note:
+// unfortunately, above generic has_member can't be used for isVisitable
+// because Vistable need 'accept' OR 'accept const', while has_member make exect match
+// e.g accept AND accept const
+
+} // namespace internals
+} // namespace cargo
+
+#endif // CARGO_INTERNALS_IS_UNION_HPP
+
diff --git a/libs/cargo/internals/is-visitable.hpp b/libs/cargo/internals/is-visitable.hpp
new file mode 100644 (file)
index 0000000..c2febcd
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Internal configuration helper
+ */
+
+#ifndef CARGO_INTERNALS_IS_VISITABLE_HPP
+#define CARGO_INTERNALS_IS_VISITABLE_HPP
+
+#include <type_traits>
+
+namespace cargo {
+namespace internals {
+
+template <typename T>
+struct isVisitableHelper__ {
+    struct Visitor {};
+
+    template <typename C> static std::true_type
+    test(decltype(std::declval<C>().template accept(Visitor()))*);
+
+    template <typename C> static std::false_type
+    test(...);
+
+    static constexpr bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
+};
+
+/**
+ * Helper for compile-time checking against existance of template method 'accept'.
+ */
+template <typename T>
+struct isVisitable : public std::integral_constant<bool, isVisitableHelper__<T>::value> {};
+
+} // namespace internals
+} // namespace cargo
+
+#endif // CARGO_INTERNALS_IS_VISITABLE_HPP
diff --git a/libs/cargo/internals/visit-fields.hpp b/libs/cargo/internals/visit-fields.hpp
new file mode 100644 (file)
index 0000000..f151365
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak (j.olszak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Helper function for iterating tuples, pairs and arrays
+ */
+
+
+#ifndef CARGO_INTERNALS_VISIT_FIELDS_HPP
+#define CARGO_INTERNALS_VISIT_FIELDS_HPP
+
+#include <string>
+#include <tuple>
+
+namespace {
+
+template<int I, class T, class F, typename ... A>
+struct visitImpl
+{
+    static void visit(T& t, F f, A&& ... args)
+    {
+        visitImpl<I - 1, T, F, A...>::visit(t, f, std::forward<A>(args)...);
+        f->visit(args..., std::get<I>(t));
+    }
+};
+
+template<class T, class F, typename ... A>
+struct visitImpl<0, T, F, A...>
+{
+    static void visit(T& t, F f, A&& ... args)
+    {
+        f->visit(args..., std::get<0>(t));
+    }
+};
+
+} // namespace
+
+namespace cargo {
+namespace internals {
+
+template<class T, class F, typename ... A>
+void visitFields(T& t, F f, A ... args)
+{
+    visitImpl<std::tuple_size<T>::value - 1, T, F, A...>::visit(t, f, std::forward<A>(args)...);
+}
+
+} // namespace internals
+} // namespace cargo
+
+#endif // CARGO_INTERNALS_VISIT_FIELDS_HPP
diff --git a/libs/cargo/libcargo.pc.in b/libs/cargo/libcargo.pc.in
new file mode 100644 (file)
index 0000000..8b92fd3
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: lib@PROJECT_NAME@
+Description: cargo library
+Version: @_LIB_VERSION_@
+Libs:
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
diff --git a/libs/cargo/types.hpp b/libs/cargo/types.hpp
new file mode 100644 (file)
index 0000000..30b4f0f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author Jan Olszak (j.olszak@samsung.com)
+ * @brief  Types declarations
+ */
+
+#ifndef CARGO_TYPES_HPP
+#define CARGO_TYPES_HPP
+
+namespace cargo {
+
+/**
+ * Whenever possible, this type will be serialized using Linux file descriptor passing.
+ */
+struct FileDescriptor {
+    int value;
+    FileDescriptor(int fd = -1): value(fd) {}
+    FileDescriptor& operator=(const int fd) {
+        value = fd;
+        return *this;
+    }
+};
+
+} // cargo
+
+#endif //CARGO_TYPES_HPP
diff --git a/libs/logger/CMakeLists.txt b/libs/logger/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d943b4f
--- /dev/null
@@ -0,0 +1,62 @@
+# Copyright (c) 2014 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Dariusz Michaluk (d.michaluk@samsung.com)
+#
+
+PROJECT(Logger)
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the libLogger...")
+FILE(GLOB HEADERS   *.hpp
+                    ${COMMON_FOLDER}/config.hpp)
+FILE(GLOB SRCS      *.cpp *.hpp)
+
+SET(_LIB_VERSION_ "${VERSION}")
+SET(_LIB_SOVERSION_ "0")
+SET(PC_FILE "lib${PROJECT_NAME}.pc")
+
+## Setup target ################################################################
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
+    SOVERSION   ${_LIB_SOVERSION_}
+    VERSION     ${_LIB_VERSION_}
+)
+
+ADD_DEPENDENCIES(${PROJECT_NAME} cargo-utils)
+
+## Link libraries ##############################################################
+IF(NOT WITHOUT_SYSTEMD)
+PKG_CHECK_MODULES(LOGGER_DEPS REQUIRED libsystemd-journal)
+ENDIF(NOT WITHOUT_SYSTEMD)
+
+INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${LIBS_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${LOGGER_DEPS_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} cargo-utils ${LOGGER_DEPS_LIBRARIES})
+
+## Generate the pc file ########################################################
+CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
+
+## Install #####################################################################
+INSTALL(FILES       ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE}
+        DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+
+INSTALL(TARGETS     ${PROJECT_NAME}
+        DESTINATION ${LIB_INSTALL_DIR}
+        COMPONENT   RuntimeLibraries)
+
+INSTALL(FILES       ${HEADERS}
+        DESTINATION ${INCLUDE_INSTALL_DIR}/logger)
diff --git a/libs/logger/backend-file.cpp b/libs/logger/backend-file.cpp
new file mode 100644 (file)
index 0000000..fae5548
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Roman Kubiak (r.kubiak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Roman Kubiak (r.kubiak@samsung.com)
+ * @brief   File backend for logger
+ */
+
+#include "config.hpp"
+
+#include "logger/formatter.hpp"
+#include "logger/backend-file.hpp"
+
+#include <fstream>
+
+namespace logger {
+
+void FileBackend::log(LogLevel logLevel,
+                      const std::string& file,
+                      const unsigned int& line,
+                      const std::string& func,
+                      const std::string& message)
+{
+    std::ofstream out(mfilePath, std::ios::app);
+    out << LogFormatter::getHeader(logLevel, file, line, func);
+    out << message;
+    out << std::endl;
+}
+
+
+} // namespace logger
diff --git a/libs/logger/backend-file.hpp b/libs/logger/backend-file.hpp
new file mode 100644 (file)
index 0000000..114f8e5
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Roman Kubiak (r.kubiak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Roman Kubiak (r.kubiak@samsung.com)
+ * @brief   File backend for logger
+ */
+
+#ifndef LOGGER_BACKEND_FILE_HPP
+#define LOGGER_BACKEND_FILE_HPP
+
+#include "logger/backend.hpp"
+
+namespace logger {
+
+class FileBackend : public LogBackend {
+public:
+    FileBackend(const std::string& filePath) : mfilePath(filePath) {}
+    void log(LogLevel logLevel,
+             const std::string& file,
+             const unsigned int& line,
+             const std::string& func,
+             const std::string& message) override;
+private:
+    std::string mfilePath;
+};
+
+} // namespace logger
+
+#endif // LOGGER_BACKEND_FILE_HPP
diff --git a/libs/logger/backend-journal.cpp b/libs/logger/backend-journal.cpp
new file mode 100644 (file)
index 0000000..8fd5112
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk (d.michaluk@samsung.com)
+ * @brief   Systemd journal backend for logger
+ */
+
+#ifdef HAVE_SYSTEMD
+#include "config.hpp"
+
+#include "logger/backend-journal.hpp"
+
+#define SD_JOURNAL_SUPPRESS_LOCATION
+#include <systemd/sd-journal.h>
+
+namespace logger {
+
+namespace {
+
+inline int toJournalPriority(LogLevel logLevel)
+{
+    switch (logLevel) {
+    case LogLevel::ERROR:
+        return LOG_ERR;     // 3
+    case LogLevel::WARN:
+        return LOG_WARNING; // 4
+    case LogLevel::INFO:
+        return LOG_INFO;    // 6
+    case LogLevel::DEBUG:
+        return LOG_DEBUG;   // 7
+    case LogLevel::TRACE:
+        return LOG_DEBUG;   // 7
+    case LogLevel::HELP:
+        return LOG_DEBUG;   // 7
+    default:
+        return LOG_DEBUG;   // 7
+    }
+}
+
+} // namespace
+
+void SystemdJournalBackend::log(LogLevel logLevel,
+                                const std::string& file,
+                                const unsigned int& line,
+                                const std::string& func,
+                                const std::string& message)
+{
+    sd_journal_send("PRIORITY=%d", toJournalPriority(logLevel),
+                    "CODE_FILE=%s", file.c_str(),
+                    "CODE_LINE=%d", line,
+                    "CODE_FUNC=%s", func.c_str(),
+                    "MESSAGE=%s", message.c_str(),
+                    NULL);
+}
+
+} // namespace logger
+#endif // HAVE_SYSTEMD
diff --git a/libs/logger/backend-journal.hpp b/libs/logger/backend-journal.hpp
new file mode 100644 (file)
index 0000000..f5b9fe6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk (d.michaluk@samsung.com)
+ * @brief   Systemd journal backend for logger
+ */
+
+#ifndef LOGGER_BACKEND_JOURNAL_HPP
+#define LOGGER_BACKEND_JOURNAL_HPP
+
+#include "logger/backend.hpp"
+
+namespace logger {
+
+/**
+ * systemd journal logging backend
+ */
+class SystemdJournalBackend : public LogBackend {
+public:
+    void log(LogLevel logLevel,
+             const std::string& file,
+             const unsigned int& line,
+             const std::string& func,
+             const std::string& message) override;
+};
+
+} // namespace logger
+
+#endif // LOGGER_BACKEND_JOURNAL_HPP
diff --git a/libs/logger/backend-null.hpp b/libs/logger/backend-null.hpp
new file mode 100644 (file)
index 0000000..32a4401
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Broda <p.broda@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Broda (p.broda@partner.samsung.com)
+ * @brief   Null backend for logger
+ */
+
+#ifndef LOGGER_BACKEND_NULL_HPP
+#define LOGGER_BACKEND_NULL_HPP
+
+#include "logger/backend.hpp"
+
+namespace logger {
+
+/**
+ * Null logging backend
+ */
+class NullLogger : public LogBackend {
+public:
+    void log(LogLevel            /*logLevel*/,
+             const std::string&  /*file*/,
+             const unsigned int& /*line*/,
+             const std::string&  /*func*/,
+             const std::string&  /*message*/) override {}
+};
+
+} // namespace logger
+
+#endif // LOGGER_BACKEND_NULL_HPP
diff --git a/libs/logger/backend-persistent-file.cpp b/libs/logger/backend-persistent-file.cpp
new file mode 100644 (file)
index 0000000..0a22db9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   Persistent file backend for logger
+ */
+
+#include "config.hpp"
+
+#include "logger/formatter.hpp"
+#include "logger/backend-persistent-file.hpp"
+
+#include <fstream>
+
+namespace logger {
+
+void PersistentFileBackend::log(LogLevel logLevel,
+                                const std::string& file,
+                                const unsigned int& line,
+                                const std::string& func,
+                                const std::string& message)
+{
+    mOut << LogFormatter::getHeader(logLevel, file, line, func);
+    mOut << message;
+    mOut << std::endl;
+    mOut.flush();
+}
+
+
+} // namespace logger
diff --git a/libs/logger/backend-persistent-file.hpp b/libs/logger/backend-persistent-file.hpp
new file mode 100644 (file)
index 0000000..69b5004
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   Persistent file backend for logger
+ */
+
+#ifndef LOGGER_BACKEND_PERSISTENT_FILE_HPP
+#define LOGGER_BACKEND_PERSISTENT_FILE_HPP
+
+#include "logger/backend.hpp"
+
+#include <fcntl.h>
+#include <fstream>
+#include <ext/stdio_filebuf.h>
+
+namespace logger {
+
+class PersistentFileBackend : public LogBackend {
+public:
+    PersistentFileBackend(const std::string& filePath) :
+        mfilePath(filePath),
+        mOut(mfilePath, std::ios::app)
+    {
+        using filebufType = __gnu_cxx::stdio_filebuf<std::ofstream::char_type>;
+        const int fd = static_cast<filebufType*>(mOut.rdbuf())->fd();
+        ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) | FD_CLOEXEC);
+    }
+
+    void log(LogLevel logLevel,
+             const std::string& file,
+             const unsigned int& line,
+             const std::string& func,
+             const std::string& message) override;
+private:
+    std::string mfilePath;
+    std::ofstream mOut;
+};
+
+} // namespace logger
+
+#endif // LOGGER_BACKEND_PERSISTENT_FILE_HPP
diff --git a/libs/logger/backend-stderr.cpp b/libs/logger/backend-stderr.cpp
new file mode 100644 (file)
index 0000000..0808a89
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Broda <p.broda@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Broda (p.broda@partner.samsung.com)
+ * @brief   Stderr backend for logger
+ */
+
+#include "config.hpp"
+
+#include "logger/backend-stderr.hpp"
+#include "logger/formatter.hpp"
+
+#include <iostream>
+#include <boost/tokenizer.hpp>
+
+namespace logger {
+
+void StderrBackend::log(LogLevel logLevel,
+                        const std::string& file,
+                        const unsigned int& line,
+                        const std::string& func,
+                        const std::string& message)
+{
+    typedef boost::char_separator<char> charSeparator;
+    typedef boost::tokenizer<charSeparator> tokenizer;
+
+    // example log string
+    // 06:52:35.123 [ERROR] src/util/fs.cpp:43 readFileContent: /file/file.txt is missing
+
+    const std::string logColor = LogFormatter::getConsoleColor(logLevel);
+    const std::string defaultColor = LogFormatter::getDefaultConsoleColor();
+    const std::string header = LogFormatter::getHeader(logLevel, file, line, func);
+    tokenizer tokens(message, charSeparator("\n"));
+    for (const auto& messageLine : tokens) {
+        if (!messageLine.empty()) {
+            fprintf(stderr,
+                    "%s%s %s%s\n",
+                    mUseColours ? logColor.c_str() : "",
+                    header.c_str(),
+                    messageLine.c_str(),
+                    mUseColours ? defaultColor.c_str() : "");
+        }
+    }
+}
+
+void StderrBackend::relog(LogLevel logLevel,
+                          const std::string& file,
+                          const unsigned int& line,
+                          const std::string& func,
+                          const std::istream& stream)
+{
+    const std::string header = LogFormatter::getHeader(logLevel, file, line, func);
+    if (stream.good()) {
+        std::cerr << "RELOG BEGIN: " << header << std::endl
+                  << stream.rdbuf()
+                  << "RELOG END: " << header  << std::endl;
+    }
+}
+
+} // namespace logger
diff --git a/libs/logger/backend-stderr.hpp b/libs/logger/backend-stderr.hpp
new file mode 100644 (file)
index 0000000..d78c0f1
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Broda <p.broda@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Broda (p.broda@partner.samsung.com)
+ * @brief   Stderr backend for logger
+ */
+
+#ifndef LOGGER_BACKEND_STDERR_HPP
+#define LOGGER_BACKEND_STDERR_HPP
+
+#include "logger/backend.hpp"
+
+namespace logger {
+
+/**
+ * Stderr logging backend
+ */
+class StderrBackend : public LogBackend {
+public:
+    StderrBackend(const bool useColours = true) : mUseColours(useColours) {}
+    void log(LogLevel logLevel,
+             const std::string& file,
+             const unsigned int& line,
+             const std::string& func,
+             const std::string& message) override;
+    void relog(LogLevel logLevel,
+               const std::string& file,
+               const unsigned int& line,
+               const std::string& func,
+               const std::istream& stream) override;
+private:
+    bool mUseColours;
+};
+
+} // namespace logger
+
+#endif // LOGGER_BACKEND_STDERR_HPP
diff --git a/libs/logger/backend-syslog.cpp b/libs/logger/backend-syslog.cpp
new file mode 100644 (file)
index 0000000..65b6594
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Roman Kubiak (r.kubiak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Roman Kubiak (r.kubiak@samsung.com)
+ * @brief   Syslog backend for logger
+ */
+
+#include "config.hpp"
+
+#include "logger/formatter.hpp"
+#include "logger/backend-syslog.hpp"
+
+#include <syslog.h>
+#include <sstream>
+namespace logger {
+
+namespace {
+
+inline int toSyslogPriority(LogLevel logLevel)
+{
+    switch (logLevel) {
+    case LogLevel::ERROR:
+        return LOG_ERR;     // 3
+    case LogLevel::WARN:
+        return LOG_WARNING; // 4
+    case LogLevel::INFO:
+        return LOG_INFO;    // 6
+    case LogLevel::DEBUG:
+        return LOG_DEBUG;   // 7
+    case LogLevel::TRACE:
+        return LOG_DEBUG;   // 7
+    case LogLevel::HELP:
+        return LOG_DEBUG;   // 7
+    default:
+        return LOG_DEBUG;   // 7
+    }
+}
+
+} // namespace
+
+void SyslogBackend::log(LogLevel logLevel,
+                        const std::string& file,
+                        const unsigned int& line,
+                        const std::string& func,
+                        const std::string& message)
+{
+    ::syslog(toSyslogPriority(logLevel), "%s %s", LogFormatter::getHeader(logLevel, file, line, func).c_str(), message.c_str());
+}
+
+} // namespace logger
diff --git a/libs/logger/backend-syslog.hpp b/libs/logger/backend-syslog.hpp
new file mode 100644 (file)
index 0000000..746dc4f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Roman Kubiak (r.kubiak@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Roman Kubiak (r.kubiak@samsung.com)
+ * @brief   Syslog backend for logger
+ */
+
+#ifndef LOGGER_BACKEND_SYSLOG_HPP
+#define LOGGER_BACKEND_SYSLOG_HPP
+
+#include "logger/backend.hpp"
+
+namespace logger {
+
+class SyslogBackend : public LogBackend {
+public:
+    void log(LogLevel logLevel,
+             const std::string& file,
+             const unsigned int& line,
+             const std::string& func,
+             const std::string& message) override;
+};
+
+} // namespace logger
+
+#endif // LOGGER_BACKEND_SYSLOG_HPP
diff --git a/libs/logger/backend.hpp b/libs/logger/backend.hpp
new file mode 100644 (file)
index 0000000..fbd00e5
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Broda <p.broda@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Broda (p.broda@partner.samsung.com)
+ * @brief   Logging backend
+ */
+
+#ifndef LOGGER_BACKEND_HPP
+#define LOGGER_BACKEND_HPP
+
+#include "logger/level.hpp"
+
+#include <string>
+
+namespace logger {
+
+/**
+ * @brief Abstract class for logger backends
+ * @ingroup libLogger
+ */
+class LogBackend {
+public:
+    virtual void log(LogLevel logLevel,
+                     const std::string& file,
+                     const unsigned int& line,
+                     const std::string& func,
+                     const std::string& message) = 0;
+    virtual void relog(LogLevel /*logLevel*/,
+                     const std::string& /*file*/,
+                     const unsigned int& /*line*/,
+                     const std::string& /*func*/,
+                     const std::istream& /*stream*/) {}
+
+    virtual ~LogBackend() {}
+};
+
+} // namespace logger
+
+#endif // LOGGER_BACKEND_HPP
diff --git a/libs/logger/formatter.cpp b/libs/logger/formatter.cpp
new file mode 100644 (file)
index 0000000..47b43b3
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk (d.michaluk@samsung.com)
+ * @brief   Helper formatter for logger
+ */
+
+#include "config.hpp"
+
+#include "logger/formatter.hpp"
+#include "utils/ccolor.hpp"
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <cassert>
+#include <sstream>
+#include <iomanip>
+#include <thread>
+#include <atomic>
+
+namespace logger {
+
+namespace {
+
+const int TIME_COLUMN_LENGTH = 12;
+const int SEVERITY_COLUMN_LENGTH = 8;
+const int PID_COLUMN_LENGTH = 8;
+const int TID_COLUMN_LENGTH = 2;
+const int FILE_COLUMN_LENGTH = 60;
+
+std::atomic<unsigned int> gNextThreadId(1);
+thread_local unsigned int gThisThreadId(0);
+
+} // namespace
+
+unsigned int LogFormatter::getCurrentThread(void)
+{
+    unsigned int id = gThisThreadId;
+    if (id == 0) {
+        gThisThreadId = id = gNextThreadId++;
+    }
+
+    return id;
+}
+
+std::string LogFormatter::getCurrentTime(void)
+{
+    char time[TIME_COLUMN_LENGTH + 1];
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    struct tm* tm = localtime(&tv.tv_sec);
+    snprintf(time,
+             sizeof(time),
+             "%02d:%02d:%02d.%03d",
+             tm->tm_hour,
+             tm->tm_min,
+             tm->tm_sec,
+             int(tv.tv_usec / 1000));
+
+    return std::string(time);
+}
+
+std::string LogFormatter::getConsoleColor(LogLevel logLevel)
+{
+    switch (logLevel) {
+    case LogLevel::ERROR:
+        return utils::getConsoleEscapeSequence(utils::Attributes::BOLD, utils::Color::RED);
+    case LogLevel::WARN:
+        return utils::getConsoleEscapeSequence(utils::Attributes::BOLD, utils::Color::YELLOW);
+    case LogLevel::INFO:
+        return utils::getConsoleEscapeSequence(utils::Attributes::BOLD, utils::Color::BLUE);
+    case LogLevel::DEBUG:
+        return utils::getConsoleEscapeSequence(utils::Attributes::DEFAULT, utils::Color::GREEN);
+    case LogLevel::TRACE:
+        return utils::getConsoleEscapeSequence(utils::Attributes::DEFAULT, utils::Color::BLACK);
+    case LogLevel::HELP:
+        return utils::getConsoleEscapeSequence(utils::Attributes::BOLD, utils::Color::MAGENTA);
+    default:
+        return utils::getConsoleEscapeSequence(utils::Attributes::DEFAULT, utils::Color::DEFAULT);
+    }
+}
+
+std::string LogFormatter::getDefaultConsoleColor(void)
+{
+    return utils::getConsoleEscapeSequence(utils::Attributes::DEFAULT, utils::Color::DEFAULT);
+}
+
+std::string LogFormatter::stripProjectDir(const std::string& file,
+                                          const std::string& rootDir)
+{
+    // If rootdir is empty then return full name
+    if (rootDir.empty()) {
+        return file;
+    }
+    const std::string sourceDir = rootDir + "/";
+    // If file does not belong to rootDir then also return full name
+    if (0 != file.compare(0, sourceDir.size(), sourceDir)) {
+        return file;
+    }
+    return file.substr(sourceDir.size());
+}
+
+std::string LogFormatter::getHeader(LogLevel logLevel,
+                                    const std::string& file,
+                                    const unsigned int& line,
+                                    const std::string& func)
+{
+    std::ostringstream logLine;
+    logLine << getCurrentTime() << ' '
+            << std::left << std::setw(SEVERITY_COLUMN_LENGTH) << '[' + toString(logLevel) + ']'
+            << std::right << std::setw(PID_COLUMN_LENGTH) << ::getpid() << "/"
+            << std::right << std::setw(TID_COLUMN_LENGTH) << getCurrentThread() << ": "
+            << std::left << std::setw(FILE_COLUMN_LENGTH)
+            << file + ':' + std::to_string(line) + ' ' + func + ':';
+    return logLine.str();
+}
+
+} // namespace logger
diff --git a/libs/logger/formatter.hpp b/libs/logger/formatter.hpp
new file mode 100644 (file)
index 0000000..b2df502
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk <d.michaluk@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk (d.michaluk@samsung.com)
+ * @brief   Helper formatter for logger
+ */
+
+#ifndef LOGGER_FORMATTER_HPP
+#define LOGGER_FORMATTER_HPP
+
+#include "logger/level.hpp"
+
+#include <string>
+
+namespace logger {
+
+class LogFormatter {
+public:
+    static unsigned int getCurrentThread(void);
+    static std::string getCurrentTime(void);
+    static std::string getConsoleColor(LogLevel logLevel);
+    static std::string getDefaultConsoleColor(void);
+    static std::string stripProjectDir(const std::string& file,
+                                       const std::string& rootDir);
+    static std::string getHeader(LogLevel logLevel,
+                                 const std::string& file,
+                                 const unsigned int& line,
+                                 const std::string& func);
+};
+
+} // namespace logger
+
+#endif // LOGGER_FORMATTER_HPP
diff --git a/libs/logger/level.cpp b/libs/logger/level.cpp
new file mode 100644 (file)
index 0000000..56edd42
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Functions to handle LogLevel
+ */
+
+#include "config.hpp"
+
+#include "logger/level.hpp"
+
+#include <stdexcept>
+#include <boost/algorithm/string.hpp>
+
+namespace logger {
+
+LogLevel parseLogLevel(const std::string& level)
+{
+    if (boost::iequals(level, "ERROR")) {
+        return LogLevel::ERROR;
+    } else if (boost::iequals(level, "WARN")) {
+        return LogLevel::WARN;
+    } else if (boost::iequals(level, "INFO")) {
+        return LogLevel::INFO;
+    } else if (boost::iequals(level, "DEBUG")) {
+        return LogLevel::DEBUG;
+    } else if (boost::iequals(level, "TRACE")) {
+        return LogLevel::TRACE;
+    } else if (boost::iequals(level, "HELP")) {
+        return LogLevel::HELP;
+    } else {
+        throw std::runtime_error("Invalid LogLevel to parse");
+    }
+}
+
+std::string toString(const LogLevel logLevel)
+{
+    switch (logLevel) {
+    case LogLevel::ERROR:
+        return "ERROR";
+    case LogLevel::WARN:
+        return "WARN";
+    case LogLevel::INFO:
+        return "INFO";
+    case LogLevel::DEBUG:
+        return "DEBUG";
+    case LogLevel::TRACE:
+        return "TRACE";
+    case LogLevel::HELP:
+        return "HELP";
+    default:
+        return "UNKNOWN";
+    }
+}
+} // namespace logger
diff --git a/libs/logger/level.hpp b/libs/logger/level.hpp
new file mode 100644 (file)
index 0000000..1dd82fd
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Dariusz Michaluk (d.michaluk@samsung.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Dariusz Michaluk (d.michaluk@samsung.com)
+ * @brief   LogLevel
+ */
+
+#ifndef LOGGER_LEVEL_HPP
+#define LOGGER_LEVEL_HPP
+
+#include <string>
+
+namespace logger {
+
+/**
+ * @brief Available log levels
+ * @ingroup libLogger
+ */
+enum class LogLevel : int {
+    TRACE, ///< Most detailed log level
+    DEBUG, ///< Debug logs
+    INFO,  ///< Information
+    WARN,  ///< Warnings
+    ERROR, ///< Errors
+    HELP   ///< Helper logs
+};
+
+/**
+ * @param logLevel LogLevel
+ * @return std::sting representation of the LogLevel value
+ */
+std::string toString(const LogLevel logLevel);
+
+/**
+ * @param level string representation of log level
+ * @return parsed LogLevel value
+ */
+LogLevel parseLogLevel(const std::string& level);
+
+} // namespace logger
+
+#endif // LOGGER_LEVEL_HPP
diff --git a/libs/logger/libLogger.pc.in b/libs/logger/libLogger.pc.in
new file mode 100644 (file)
index 0000000..4fcd7ca
--- /dev/null
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: libLogger
+Description: Logger library
+Version: @_LIB_VERSION_@
+Libs: -L${libdir} -l@PROJECT_NAME@
+Cflags: -I${includedir}/ -I${includedir}/@PROJECT_NAME@
diff --git a/libs/logger/logger-scope.cpp b/libs/logger/logger-scope.cpp
new file mode 100644 (file)
index 0000000..5b9678b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Scope logger class implementation
+ */
+
+#include "config.hpp"
+
+#include "logger/logger-scope.hpp"
+#include "logger/logger.hpp"
+
+namespace logger {
+
+SStreamWrapper::operator std::string() const
+{
+    return mSStream.str();
+}
+
+LoggerScope::LoggerScope(const std::string& file,
+                         const unsigned int line,
+                         const std::string& func,
+                         const std::string& message,
+                         const std::string& rootDir):
+        mFile(file),
+        mLine(line),
+        mFunc(func),
+        mMessage(message),
+        mRootDir(rootDir)
+{
+    if (logger::Logger::getLogLevel() <= logger::LogLevel::TRACE) {
+        logger::Logger::logMessage(logger::LogLevel::TRACE, "Entering: " + mMessage,
+                                   mFile, mLine, mFunc, mRootDir);
+    }
+}
+
+LoggerScope::~LoggerScope()
+{
+    if (logger::Logger::getLogLevel() <= logger::LogLevel::TRACE) {
+        logger::Logger::logMessage(logger::LogLevel::TRACE, "Leaving:  " + mMessage,
+                                   mFile, mLine, mFunc, mRootDir);
+    }
+}
+
+} // namespace logger
diff --git a/libs/logger/logger-scope.hpp b/libs/logger/logger-scope.hpp
new file mode 100644 (file)
index 0000000..810c904
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Scope logger class declaration
+ */
+
+#ifndef LOGGER_LOGGER_SCOPE_HPP
+#define LOGGER_LOGGER_SCOPE_HPP
+
+#include <string>
+#include <sstream>
+
+namespace logger {
+
+class SStreamWrapper
+{
+public:
+    operator std::string() const;
+
+    template <typename T>
+    SStreamWrapper& operator<<(const T& b)
+    {
+        this->mSStream << b;
+        return *this;
+    }
+
+private:
+    std::ostringstream mSStream;
+};
+
+/**
+ * Class specifically for scope debug logging. Should be used at the beggining of a scope.
+ * Constructor marks scope enterance, destructor marks scope leave.
+ */
+class LoggerScope
+{
+public:
+    LoggerScope(const std::string& file,
+                const unsigned int line,
+                const std::string& func,
+                const std::string& message,
+                const std::string& rootDir);
+    ~LoggerScope();
+
+private:
+    const std::string mFile;
+    const unsigned int mLine;
+    const std::string mFunc;
+    const std::string mMessage;
+    const std::string mRootDir;
+};
+
+} // namespace logger
+
+/**
+ * @brief Automatically create LoggerScope object which logs at the construction and destruction
+ * @ingroup libLogger
+ */
+#if !defined(NDEBUG)
+#define LOGS(MSG)   logger::LoggerScope logScopeObj(__FILE__, __LINE__, __func__,    \
+                                                    logger::SStreamWrapper() << MSG, \
+                                                    PROJECT_SOURCE_DIR)
+#else
+#define LOGS(MSG) do {} while (0)
+#endif
+
+#endif // LOGGER_LOGGER_SCOPE_HPP
diff --git a/libs/logger/logger.cpp b/libs/logger/logger.cpp
new file mode 100644 (file)
index 0000000..9d177c5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Broda <p.broda@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Pawel Broda (p.broda@partner.samsung.com)
+ * @brief   Logger
+ */
+
+#include "config.hpp"
+
+#include "logger/logger.hpp"
+#include "logger/formatter.hpp"
+#include "logger/backend-null.hpp"
+
+#include <memory>
+#include <mutex>
+
+namespace logger {
+
+namespace {
+
+volatile LogLevel gLogLevel = LogLevel::DEBUG;
+std::unique_ptr<LogBackend> gLogBackendPtr(new NullLogger());
+std::mutex gLogMutex;
+
+} // namespace
+
+void setupLogger(const LogType type,
+                 const LogLevel level,
+                 const std::string &arg)
+{
+    if (type == LogType::LOG_FILE || type == LogType::LOG_PERSISTENT_FILE) {
+        if (arg.empty()) {
+            throw std::runtime_error("Path needs to be specified in the agument");
+        }
+    }
+
+    switch(type) {
+    case LogType::LOG_NULL:
+        Logger::setLogBackend(new NullLogger());
+        break;
+#ifdef HAVE_SYSTEMD
+    case LogType::LOG_JOURNALD:
+        Logger::setLogBackend(new SystemdJournalBackend());
+        break;
+#endif
+    case LogType::LOG_FILE:
+        Logger::setLogBackend(new FileBackend(arg));
+        break;
+    case LogType::LOG_PERSISTENT_FILE:
+        Logger::setLogBackend(new PersistentFileBackend(arg));
+        break;
+    case LogType::LOG_SYSLOG:
+        Logger::setLogBackend(new SyslogBackend());
+        break;
+    case LogType::LOG_STDERR:
+        Logger::setLogBackend(new StderrBackend());
+        break;
+    default:
+        throw std::runtime_error("Bad logger type passed");
+    }
+
+    Logger::setLogLevel(level);
+}
+
+void Logger::logMessage(LogLevel logLevel,
+                        const std::string& message,
+                        const std::string& file,
+                        const unsigned int line,
+                        const std::string& func,
+                        const std::string& rootDir)
+{
+    std::string sfile = LogFormatter::stripProjectDir(file, rootDir);
+    std::unique_lock<std::mutex> lock(gLogMutex);
+    gLogBackendPtr->log(logLevel, sfile, line, func, message);
+}
+
+void Logger::logRelog(LogLevel logLevel,
+                      const std::istream& stream,
+                      const std::string& file,
+                      const unsigned int line,
+                      const std::string& func,
+                      const std::string& rootDir)
+{
+    std::string sfile = LogFormatter::stripProjectDir(file, rootDir);
+    std::unique_lock<std::mutex> lock(gLogMutex);
+    gLogBackendPtr->relog(logLevel, sfile, line, func, stream);
+}
+
+void Logger::setLogLevel(const LogLevel level)
+{
+    gLogLevel = level;
+}
+
+void Logger::setLogLevel(const std::string& level)
+{
+    gLogLevel = parseLogLevel(level);
+}
+
+LogLevel Logger::getLogLevel(void)
+{
+    return gLogLevel;
+}
+
+void Logger::setLogBackend(LogBackend* pBackend)
+{
+    std::unique_lock<std::mutex> lock(gLogMutex);
+    gLogBackendPtr.reset(pBackend);
+}
+
+} // namespace logger
diff --git a/libs/logger/logger.hpp b/libs/logger/logger.hpp
new file mode 100644 (file)
index 0000000..6a625d1
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author   Jan Olszak (j.olszak@samsung.com)
+ * @defgroup libLogger libLogger
+ * @brief C++ library for handling logging.
+ *
+ * There are few backends implemented and it's possible to create your own by inheriting after the @ref logger::LogBackend interface.
+ *
+ * Example usage:
+ * @code
+ * using namespace logger;
+ *
+ * // Set minimal logging level
+ * Logger::setLogLevel("TRACE");
+ *
+ *
+ * // Set one of the possible backends:
+ * Logger::setLogBackend(new NullLogger());
+ * Logger::setLogBackend(new SystemdJournalBackend());
+ * Logger::setLogBackend(new FileBackend("/tmp/logs.txt"));
+ * Logger::setLogBackend(new PersistentFileBackend("/tmp/logs.txt"));
+ * Logger::setLogBackend(new SyslogBackend());
+ * Logger::setLogBackend(new StderrBackend());
+ *
+ *
+ * // All logs should be visible:
+ * LOGE("Error");
+ * LOGW("Warning");
+ * LOGI("Information");
+ * LOGD("Debug");
+ * LOGT("Trace");
+ * LOGH("Helper");
+ *
+ * {
+ *     LOGS("Scope");
+ * }
+ *
+ * @endcode
+ */
+
+
+#ifndef LOGGER_LOGGER_HPP
+#define LOGGER_LOGGER_HPP
+
+#include "logger/level.hpp"
+#include "logger/backend-null.hpp"
+#ifdef HAVE_SYSTEMD
+#include "logger/backend-journal.hpp"
+#endif
+#include "logger/backend-file.hpp"
+#include "logger/backend-persistent-file.hpp"
+#include "logger/backend-syslog.hpp"
+#include "logger/backend-stderr.hpp"
+
+#include <sstream>
+#include <string>
+
+#ifndef PROJECT_SOURCE_DIR
+#define PROJECT_SOURCE_DIR ""
+#endif
+
+namespace logger {
+
+enum class LogType : int {
+    LOG_NULL,
+    LOG_JOURNALD,
+    LOG_FILE,
+    LOG_PERSISTENT_FILE,
+    LOG_SYSLOG,
+    LOG_STDERR
+};
+
+/**
+ * A helper function to easily and completely setup a new logger
+ *
+ * @param type      logger type to be set up
+ * @param level     maximum log level that will be logged
+ * @param arg       an argument used by some loggers, specific to them
+ *                  (e.g. file name for file loggers)
+ */
+void setupLogger(const LogType type,
+                 const LogLevel level,
+                 const std::string &arg = "");
+
+class LogBackend;
+
+class Logger {
+public:
+    static void logMessage(LogLevel logLevel,
+                           const std::string& message,
+                           const std::string& file,
+                           const unsigned int line,
+                           const std::string& func,
+                           const std::string& rootDir);
+
+    static void logRelog(LogLevel logLevel,
+                         const std::istream& stream,
+                         const std::string& file,
+                         const unsigned int line,
+                         const std::string& func,
+                         const std::string& rootDir);
+
+    static void setLogLevel(const LogLevel level);
+    static void setLogLevel(const std::string& level);
+    static LogLevel getLogLevel(void);
+    static void setLogBackend(LogBackend* pBackend);
+};
+
+} // namespace logger
+
+/*@{*/
+/// Generic logging macro
+#define LOG(SEVERITY, MESSAGE)                                             \
+    do {                                                                   \
+        if (logger::Logger::getLogLevel() <= logger::LogLevel::SEVERITY) { \
+            std::ostringstream messageStream__;                            \
+            messageStream__ << MESSAGE;                                    \
+            logger::Logger::logMessage(logger::LogLevel::SEVERITY,         \
+                                       messageStream__.str(),              \
+                                       __FILE__,                           \
+                                       __LINE__,                           \
+                                       __func__,                           \
+                                       PROJECT_SOURCE_DIR);                \
+        }                                                                  \
+    } while (0)
+
+
+/// Logging errors
+#define LOGE(MESSAGE) LOG(ERROR, MESSAGE)
+
+/// Logging warnings
+#define LOGW(MESSAGE) LOG(WARN, MESSAGE)
+
+/// Logging information
+#define LOGI(MESSAGE) LOG(INFO, MESSAGE)
+
+#if !defined(NDEBUG)
+/// Logging debug information
+#define LOGD(MESSAGE) LOG(DEBUG, MESSAGE)
+
+/// Logging helper information (for debugging purposes)
+#define LOGH(MESSAGE) LOG(HELP, MESSAGE)
+
+/// Logging tracing information
+#define LOGT(MESSAGE) LOG(TRACE, MESSAGE)
+
+#define RELOG(ISTREAM)                                                     \
+    do {                                                                   \
+        if (logger::Logger::getLogLevel() <= logger::LogLevel::DEBUG) {    \
+            logger::Logger::logRelog(logger::LogLevel::DEBUG,              \
+                                     ISTREAM,                              \
+                                     __FILE__,                             \
+                                     __LINE__,                             \
+                                     __func__,                             \
+                                     PROJECT_SOURCE_DIR);                  \
+        }                                                                  \
+    } while (0)
+
+#else
+#define LOGD(MESSAGE) do {} while (0)
+#define LOGH(MESSAGE) do {} while (0)
+#define LOGT(MESSAGE) do {} while (0)
+#define RELOG(ISTREAM) do {} while (0)
+#endif
+
+#endif // LOGGER_LOGGER_HPP
+
+/*@}*/
diff --git a/packaging/cargo.spec b/packaging/cargo.spec
new file mode 100644 (file)
index 0000000..adb1527
--- /dev/null
@@ -0,0 +1,394 @@
+%define script_dir %{_sbindir}
+# Default platform is Tizen, setup Fedora with --define 'platform_type FEDORA'
+%{!?platform_type:%define platform_type "TIZEN"}
+# Default build with systemd
+%{!?without_systemd:%define without_systemd 0}
+
+Name:           cargo
+Epoch:          1
+Version:        0.1.2
+Release:        0
+Source0:        %{name}-%{version}.tar.gz
+License:        Apache-2.0
+Group:          Security/Other
+Summary:        Cargo libraries
+BuildRequires:  cmake
+BuildRequires:  boost-devel
+
+%description
+This package provides Cargo libraries.
+
+%files
+
+%prep
+%setup -q
+
+%build
+%{!?build_type:%define build_type "RELEASE"}
+
+%if %{build_type} == "DEBUG" || %{build_type} == "PROFILING" || %{build_type} == "CCOV"
+    CFLAGS="$CFLAGS -Wp,-U_FORTIFY_SOURCE"
+    CXXFLAGS="$CXXFLAGS -Wp,-U_FORTIFY_SOURCE"
+%endif
+
+%cmake . -DVERSION=%{version} \
+         -DCMAKE_BUILD_TYPE=%{build_type} \
+         -DSCRIPT_INSTALL_DIR=%{script_dir} \
+         -DSYSTEMD_UNIT_DIR=%{_unitdir} \
+         -DDATA_DIR=%{_datadir} \
+         -DPYTHON_SITELIB=%{python_sitelib} \
+         -DWITHOUT_SYSTEMD=%{?without_systemd}
+make -k %{?jobs:-j%jobs}
+
+%install
+make install DESTDIR=${RPM_BUILD_ROOT}
+
+%clean
+rm -rf %{buildroot}
+
+## libcargo-utils-devel Package ################################################
+%package -n libcargo-utils-devel
+Summary:            Development Cargo Utils static library
+Group:              Development/Libraries
+BuildRequires:      pkgconfig(glib-2.0)
+
+%description -n libcargo-utils-devel
+The package provides libcargo-utils development tools and static library.
+
+%files -n libcargo-utils-devel
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-utils.a
+%attr(755,root,root) %{_libdir}/libcargo-utils.a
+%{_includedir}/cargo-utils
+
+## libLogger Package ###########################################################
+%package -n libLogger
+Summary:            Logger library
+Group:              Security/Other
+%if !%{without_systemd}
+BuildRequires:      pkgconfig(libsystemd-journal)
+%endif
+Requires(post):     /sbin/ldconfig
+Requires(postun):   /sbin/ldconfig
+
+%description -n libLogger
+The package provides libLogger library.
+
+%post -n libLogger -p /sbin/ldconfig
+
+%postun -n libLogger -p /sbin/ldconfig
+
+%files -n libLogger
+%defattr(644,root,root,755)
+%{_libdir}/libLogger.so.0
+%attr(755,root,root) %{_libdir}/libLogger.so.%{version}
+
+%package -n libLogger-devel
+Summary:        Development logger library
+Group:          Development/Libraries
+Requires:       libLogger = %{epoch}:%{version}-%{release}
+
+%description -n libLogger-devel
+The package provides libLogger development tools and libs.
+
+%files -n libLogger-devel
+%defattr(644,root,root,755)
+%{_libdir}/libLogger.so
+%{_includedir}/logger
+%{_libdir}/pkgconfig/libLogger.pc
+
+## libcargo-devel Package ##########################################################
+%package -n libcargo-devel
+Summary:        Development C++ object serialization library
+Group:          Development/Libraries
+Requires:       boost-devel
+Requires:       pkgconfig(libLogger)
+
+%description -n libcargo-devel
+The package provides libcargo development tools and libs.
+
+%files -n libcargo-devel
+%defattr(644,root,root,755)
+%{_includedir}/cargo
+%{_libdir}/pkgconfig/libcargo.pc
+
+## libcargo-gvariant Package ##################################################
+%package -n libcargo-gvariant-devel
+Summary:        Development cargo GVariant module
+Group:          Development/Libraries
+BuildRequires:  pkgconfig(glib-2.0)
+Requires:       libcargo-devel = %{epoch}:%{version}-%{release}
+Requires:       boost-devel
+Requires:       pkgconfig(libLogger)
+
+%description -n libcargo-gvariant-devel
+The package provides libcargo GVariant development module.
+
+%files -n libcargo-gvariant-devel
+%defattr(644,root,root,755)
+%{_includedir}/cargo-gvariant
+%{_libdir}/pkgconfig/libcargo-gvariant.pc
+
+## libcargo-json Package ######################################################
+%package -n libcargo-json
+Summary:            Cargo Json module
+Group:              Security/Other
+%if %{platform_type} == "TIZEN"
+BuildRequires:      libjson-devel >= 0.10
+Requires:           libjson >= 0.10
+%else
+BuildRequires:      json-c-devel
+Requires:           json-c
+%endif
+Requires(post):     /sbin/ldconfig
+Requires(postun):   /sbin/ldconfig
+
+%description -n libcargo-json
+The package provides libcargo Json module.
+
+%post -n libcargo-json -p /sbin/ldconfig
+
+%postun -n libcargo-json -p /sbin/ldconfig
+
+%files -n libcargo-json
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-json.so.0
+%attr(755,root,root) %{_libdir}/libcargo-json.so.%{version}
+
+%package -n libcargo-json-devel
+Summary:        Development cargo Json module
+Group:          Development/Libraries
+Requires:       libcargo-devel = %{epoch}:%{version}-%{release}
+Requires:       libcargo-json = %{epoch}:%{version}-%{release}
+Requires:       boost-devel
+Requires:       pkgconfig(libLogger)
+%if %{platform_type} == "TIZEN"
+Requires:       libjson-devel >= 0.10
+%else
+Requires:       json-c-devel
+%endif
+
+%description -n libcargo-json-devel
+The package provides libcargo Json development module.
+
+%files -n libcargo-json-devel
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-json.so
+%{_includedir}/cargo-json
+%{_libdir}/pkgconfig/libcargo-json.pc
+
+## libcargo-sqlite Package ##########################################################
+%package -n libcargo-sqlite
+Summary:            Cargo SQLite KVStore module
+Group:              Security/Other
+BuildRequires:      pkgconfig(sqlite3)
+Requires(post):     /sbin/ldconfig
+Requires(postun):   /sbin/ldconfig
+
+%description -n libcargo-sqlite
+The package provides libcargo SQLite KVStore library.
+
+%post -n libcargo-sqlite -p /sbin/ldconfig
+
+%postun -n libcargo-sqlite -p /sbin/ldconfig
+
+%files -n libcargo-sqlite
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-sqlite.so.0
+%attr(755,root,root) %{_libdir}/libcargo-sqlite.so.%{version}
+
+%package -n libcargo-sqlite-devel
+Summary:        Cargo SQLite KVStore development module
+Group:          Development/Libraries
+Requires:       libcargo-sqlite = %{epoch}:%{version}-%{release}
+Requires:       pkgconfig(sqlite3)
+Requires:       boost-devel
+Requires:       pkgconfig(libLogger)
+Requires:       pkgconfig(libcargo)
+
+%description -n libcargo-sqlite-devel
+The package provides libcargo SQLite KVStore development module.
+
+%files -n libcargo-sqlite-devel
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-sqlite.so
+%{_includedir}/cargo-sqlite
+%{_libdir}/pkgconfig/libcargo-sqlite.pc
+
+## libcargo-fd Package ##########################################################
+%package -n libcargo-fd
+Summary:            Cargo file descriptor I/O module
+Group:              Security/Other
+Requires(post):     /sbin/ldconfig
+Requires(postun):   /sbin/ldconfig
+
+%description -n libcargo-fd
+The package provides libcargo file descriptor I/O module.
+
+%post -n libcargo-fd -p /sbin/ldconfig
+
+%postun -n libcargo-fd -p /sbin/ldconfig
+
+%files -n libcargo-fd
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-fd.so.0
+%attr(755,root,root) %{_libdir}/libcargo-fd.so.%{version}
+
+%package -n libcargo-fd-devel
+Summary:        Development C++ object serialization library
+Group:          Development/Libraries
+Requires:       libcargo-devel = %{epoch}:%{version}-%{release}
+Requires:       libcargo-fd = %{epoch}:%{version}-%{release}
+Requires:       boost-devel
+Requires:       pkgconfig(libLogger)
+
+%description -n libcargo-fd-devel
+The package provides libcargo file descriptor I/O module.
+
+%files -n libcargo-fd-devel
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-fd.so
+%{_includedir}/cargo-fd
+%{_libdir}/pkgconfig/libcargo-fd.pc
+
+## libcargo-sqlite-json Package ##########################################################
+%package -n libcargo-sqlite-json-devel
+Summary:        Cargo SQLite with Json defaults development module
+Group:          Development/Libraries
+Requires:       libcargo-sqlite-devel = %{epoch}:%{version}-%{release}
+Requires:       libcargo-json-devel = %{epoch}:%{version}-%{release}
+Requires:       boost-devel
+Requires:       pkgconfig(libLogger)
+
+%description -n libcargo-sqlite-json-devel
+The package provides libcargo SQLite with Json defaults development module.
+
+%files -n libcargo-sqlite-json-devel
+%defattr(644,root,root,755)
+%{_includedir}/cargo-sqlite-json
+%{_libdir}/pkgconfig/libcargo-sqlite-json.pc
+
+## libcargo-ipc Package #######################################################
+%package -n libcargo-ipc
+Summary:            Cargo IPC library
+Group:              Security/Other
+%if !%{without_systemd}
+BuildRequires:      pkgconfig(libsystemd-daemon)
+%endif
+Requires:           libcargo-fd
+Requires:           libuuid
+BuildRequires:      libuuid-devel
+Requires(post):     /sbin/ldconfig
+Requires(postun):   /sbin/ldconfig
+
+%description -n libcargo-ipc
+The package provides libcargo-ipc library.
+
+%post -n libcargo-ipc -p /sbin/ldconfig
+
+%postun -n libcargo-ipc -p /sbin/ldconfig
+
+%files -n libcargo-ipc
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-ipc.so.0
+%attr(755,root,root) %{_libdir}/libcargo-ipc.so.%{version}
+
+%package -n libcargo-ipc-devel
+Summary:        Development cargo IPC library
+Group:          Development/Libraries
+Requires:       libcargo-ipc = %{epoch}:%{version}-%{release}
+Requires:       pkgconfig(libcargo-fd)
+Requires:       pkgconfig(libLogger)
+Requires:       pkgconfig(libcargo)
+
+%description -n libcargo-ipc-devel
+The package provides libcargo-ipc development tools and libs.
+
+%files -n libcargo-ipc-devel
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-ipc.so
+%{_includedir}/cargo-ipc
+%{_libdir}/pkgconfig/libcargo-ipc.pc
+
+## libcargo-validator Package #######################################################
+%package -n libcargo-validator
+Summary:            Cargo Validator library
+Group:              Security/Other
+Requires(post):     /sbin/ldconfig
+Requires(postun):   /sbin/ldconfig
+
+%description -n libcargo-validator
+The package provides libcargo-validator library.
+
+%post -n libcargo-validator -p /sbin/ldconfig
+
+%postun -n libcargo-validator -p /sbin/ldconfig
+
+%files -n libcargo-validator
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-validator.so.0
+%{_libdir}/pkgconfig/libcargo-validator.pc
+%attr(755,root,root) %{_libdir}/libcargo-validator.so.%{version}
+
+%package -n libcargo-validator-devel
+Summary:        Development Cargo Validator library
+Group:          Development/Libraries
+Requires:       libcargo-validator = %{epoch}:%{version}-%{release}
+Requires:       pkgconfig(libLogger)
+Requires:       pkgconfig(libcargo)
+
+%description -n libcargo-validator-devel
+The package provides libcargo-validator development tools and libs.
+
+%files -n libcargo-validator-devel
+%defattr(644,root,root,755)
+%{_libdir}/libcargo-validator.so
+%{_includedir}/cargo-validator
+%{_libdir}/pkgconfig/libcargo-validator.pc
+
+## Test Package ################################################################
+%package tests
+Summary:          Cargo Tests
+Group:            Development/Libraries
+Requires:         python
+%if %{platform_type} == "TIZEN"
+Requires:         python-xml
+%endif
+Requires:         boost-test
+
+%description tests
+Cargo Unit tests.
+
+%post tests
+%if !%{without_systemd}
+systemctl daemon-reload || :
+systemctl enable cargo-socket-test.socket || :
+systemctl start cargo-socket-test.socket || :
+%endif
+
+%preun tests
+%if !%{without_systemd}
+systemctl stop cargo-socket-test.socket || :
+systemctl disable cargo-socket-test.socket || :
+%endif
+
+%postun tests
+%if !%{without_systemd}
+systemctl daemon-reload || :
+%endif
+
+%files tests
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_bindir}/cargo-unit-tests
+%if !%{without_systemd}
+%attr(755,root,root) %{_bindir}/cargo-socket-test
+%endif
+%attr(755,root,root) %{script_dir}/cargo_all_tests.py
+%attr(755,root,root) %{script_dir}/cargo_launch_test.py
+%{script_dir}/cargo_test_parser.py
+%if !%{without_systemd}
+%{_unitdir}/cargo-socket-test.socket
+%{_unitdir}/cargo-socket-test.service
+%endif
+%dir /etc/cargo/tests/utils
+%config /etc/cargo/tests/utils/*.txt
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f96efc3
--- /dev/null
@@ -0,0 +1,24 @@
+#Copyright (c) 2014 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Lukasz Kostyra (l.kostyra@samsung.com)
+#
+
+SET(UNIT_TESTS_FOLDER ${TESTS_FOLDER}/unit_tests)
+SET(SOCKET_TEST_FOLDER ${UNIT_TESTS_FOLDER}/socket_test_service)
+
+ADD_SUBDIRECTORY(scripts)
+ADD_SUBDIRECTORY(unit_tests)
diff --git a/tests/cppcheck/cppcheck.sh b/tests/cppcheck/cppcheck.sh
new file mode 100755 (executable)
index 0000000..0b76fef
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+OPT=""
+OPT+=" --enable=all"
+OPT+=" --std=c++11"
+OPT+=" --inconclusive"
+OPT+=" --suppressions tests/cppcheck/cppcheck.suppress"
+OPT+=" --suppress=missingIncludeSystem"
+OPT+=" --inline-suppr"
+OPT+=" -iCMakeFiles"
+OPT+=" -I common"
+OPT+=" -I libs"
+OPT+=" -I tests/unit_tests"
+OPT+=" -I tests/unit_tests/socket_test_service"
+OPT+=" -U__NR_capset"
+cppcheck ${OPT} --template=gcc $@ ./ 1>/dev/null
diff --git a/tests/cppcheck/cppcheck.suppress b/tests/cppcheck/cppcheck.suppress
new file mode 100644 (file)
index 0000000..8cad745
--- /dev/null
@@ -0,0 +1,13 @@
+// public API functions (or write tests to use them all)
+unusedFunction:libs/cargo/kvstore.cpp
+unusedFunction:libs/ipc/internals/processor.cpp
+unusedFunction:common/utils/fd-utils.cpp
+
+// complains that all constructors with 1 argument should be explicit
+noExplicitConstructor
+
+// required for C abstraction
+unusedStructMember:common/utils/initctl.cpp
+
+// functions called by external utilities
+unusedFunction:tests/unit_tests/ut.cpp
diff --git a/tests/scripts/CMakeLists.txt b/tests/scripts/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5680173
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright (c) 2014 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Lukasz Kostyra (l.kostyra@samsung.com)
+#
+
+MESSAGE(STATUS "Installing scripts to " ${SCRIPT_INSTALL_DIR})
+FILE(GLOB_RECURSE scripts *.py)
+
+
+## Install #####################################################################
+INSTALL(PROGRAMS ${scripts} DESTINATION ${SCRIPT_INSTALL_DIR})
diff --git a/tests/scripts/cargo_all_tests.py b/tests/scripts/cargo_all_tests.py
new file mode 100755 (executable)
index 0000000..c0be667
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+import cargo_launch_test
+import sys
+
+# insert other test binaries to this array
+_testCmdTable = ["cargo-unit-tests"]
+
+for test in _testCmdTable:
+    cargo_launch_test.launchTest([test] + sys.argv[1:])
diff --git a/tests/scripts/cargo_launch_test.py b/tests/scripts/cargo_launch_test.py
new file mode 100755 (executable)
index 0000000..99cb149
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+
+from xml.dom import minidom
+from cargo_test_parser import Logger, Parser
+import subprocess
+import argparse
+import os
+import re
+
+_defLaunchArgs = ["--report_format=XML",
+                  "--catch_system_errors=no",
+                  "--log_level=test_suite",
+                  "--report_level=detailed",
+                 ]
+
+log = Logger()
+
+def _checkIfBinExists(binary):
+    # Check if binary is accessible through PATH env and with no additional paths
+    paths = [s + "/" for s in os.environ["PATH"].split(os.pathsep)]
+    paths.append('')
+    existsInPaths = any([os.path.isfile(path + binary) and os.access(path + binary, os.X_OK)
+                  for path in paths])
+
+    if not existsInPaths:
+        log.error(binary + " NOT FOUND.")
+
+    return existsInPaths
+
+
+
+def launchTest(cmd=[], externalToolCmd=[], parsing=True):
+    """Default function used to launch test binary.
+
+    Creates a new subprocess and parses it's output
+    """
+    if not _checkIfBinExists(cmd[0]):
+        return
+    if externalToolCmd and not _checkIfBinExists(externalToolCmd[0]):
+        return
+
+    log.info("Starting " + cmd[0] + " ...")
+
+    if parsing:
+        parser = Parser()
+        commandString = " ".join(externalToolCmd + cmd + _defLaunchArgs)
+        log.info("Invoking `" + commandString + "`")
+        p = subprocess.Popen(externalToolCmd + cmd + _defLaunchArgs,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        testResult = parser.parseOutputFromProcess(p)
+        if testResult != "":
+            domResult = minidom.parseString(testResult)
+            log.XMLSummary(domResult)
+            log.failedTestSummary(cmd[0])
+        if p.returncode < 0:
+            log.terminatedBySignal(" ".join(cmd), -p.returncode)
+    else:
+        # Launching process without coloring does not require report in XML form
+        # Avoid providing --report_format=XML, redirect std* by default to system's std*
+        commandString = " ".join(externalToolCmd + cmd + _defLaunchArgs[1:])
+        log.info("Invoking `" + commandString + "`")
+        p = subprocess.Popen(externalToolCmd + cmd + _defLaunchArgs[1:])
+        p.wait()
+
+    log.info(cmd[0] + " finished.")
+
+
+
+_valgrindCmd = ["valgrind"]
+_gdbCmd = ["gdb", "--args"]
+
+def main():
+    argparser = argparse.ArgumentParser(description="Test binary launcher for cargo.")
+    group = argparser.add_mutually_exclusive_group()
+    group.add_argument('--valgrind', action='store_true',
+                        help='Launch test binary inside Valgrind (assuming it is installed).')
+    group.add_argument('--gdb', action='store_true',
+                        help='Launch test binary with a tool specified by $CARGO_DEBUGGER variable. '
+                            +'Defaults to gdb.')
+    argparser.add_argument('binary', nargs=argparse.REMAINDER,
+                        help='Binary to be launched using script.')
+
+    args = argparser.parse_known_args()
+
+    if args[0].binary:
+        if args[0].gdb:
+            debuggerVar = os.getenv("CARGO_DEBUGGER")
+            if (debuggerVar):
+                _customDebuggerCmd = debuggerVar.split()
+            else:
+                _customDebuggerCmd = _gdbCmd
+            launchTest(args[0].binary, externalToolCmd=_customDebuggerCmd + args[1], parsing=False)
+        elif args[0].valgrind:
+            launchTest(args[0].binary, externalToolCmd=_valgrindCmd + args[1])
+        else:
+            launchTest(args[0].binary, parsing=True)
+    else:
+        log.info("Test binary not provided! Exiting.")
+
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/scripts/cargo_test_parser.py b/tests/scripts/cargo_test_parser.py
new file mode 100644 (file)
index 0000000..ea0947b
--- /dev/null
@@ -0,0 +1,132 @@
+from xml.dom import minidom
+import sys
+
+
+
+BLACK = "\033[30m"
+RED = "\033[31m"
+GREEN = "\033[32m"
+YELLOW = "\033[33m"
+BLUE = "\033[34m"
+MAGENTA = "\033[35m"
+CYAN = "\033[36m"
+WHITE = "\033[37m"
+BOLD = "\033[1m"
+ENDC = "\033[0m"
+
+
+
+class Logger(object):
+    __failedTests = []
+
+    # Create summary of test providing DOM object with parsed XML
+    def info(self, msg):
+        print BOLD + msg + ENDC
+
+    def infoTitle(self, msg):
+        print CYAN + BOLD + msg + ENDC
+
+    def error(self, msg):
+        print RED + BOLD + msg + ENDC
+
+    def success(self, msg):
+        print GREEN + BOLD + msg + ENDC
+
+
+    __indentChar = "    "
+    def testCaseSummary(self, testSuite, testName, testResult, recLevel):
+        msg = self.__indentChar * recLevel + BOLD + "{:<50} ".format(testName)
+
+        if testResult == "passed":
+            msg += GREEN
+        else:
+            msg += RED
+            self.__failedTests.append(testSuite + "/" + testName)
+
+        print msg + testResult + ENDC
+
+    def testSuiteSummary(self, suite, recLevel=0, summarize=False):
+        indPrefix = self.__indentChar * recLevel
+
+        self.infoTitle(indPrefix + suite.attributes["name"].value + " results:")
+
+        for child in suite.childNodes:
+            if child.nodeName == "TestSuite":
+                self.testSuiteSummary(child, recLevel=recLevel + 1)
+            elif child.nodeName == "TestCase":
+                self.testCaseSummary(suite.attributes["name"].value,
+                                     child.attributes["name"].value,
+                                     child.attributes["result"].value,
+                                     recLevel=recLevel + 1)
+
+        if summarize:
+            self.infoTitle(indPrefix + suite.attributes["name"].value + " summary:")
+            self.info(indPrefix + "Passed tests: " + suite.attributes["test_cases_passed"].value)
+            self.info(indPrefix + "Failed tests: " + suite.attributes["test_cases_failed"].value +
+                      '\n')
+
+    def XMLSummary(self, dom):
+        self.info("\n=========== SUMMARY ===========\n")
+
+        for result in dom.getElementsByTagName("TestResult"):
+            for child in result.childNodes:
+                if child.nodeName == "TestSuite":
+                    self.testSuiteSummary(child, summarize=True)
+
+    def failedTestSummary(self, bin):
+        if not self.__failedTests:
+            return
+
+        commandPrefix = "cargo_launch_test.py " + bin + " -t "
+        self.infoTitle("Some tests failed. Use following command(s) to launch them explicitly:")
+        for test in self.__failedTests:
+            self.error(self.__indentChar + commandPrefix + "'{0}'".format(test))
+
+    def terminatedBySignal(self, bin, signum):
+        self.error("\n=========== FAILED ===========\n")
+        signame = { 2: "SIGINT (Interrupt)",
+                    3: "SIGQUIT (Quit)",
+                    5: "SIGTRAP (Trace trap)",
+                    6: "SIGABRT (Abort)",
+                    8: "SIGFPE (Floating-point exception)",
+                    9: "SIGKILL (Kill)",
+                   11: "SIGSEGV (Segmentation fault)",
+                   15: "SIGTERM (Termination)"}
+        siginfo = signame.get(signum, 'signal ' + str(signum))
+        self.error('Terminated by ' + siginfo)
+        if signum in [5, 6, 8, 11]:
+            self.error("\nUse following command to launch debugger:")
+            self.error(self.__indentChar + "cargo_launch_test.py --gdb " + bin)
+
+
+class Parser(object):
+    _testResultBegin = "<TestResult>"
+    _testResultEnd = "</TestResult>"
+
+    def __parseAndWriteLine(self, line):
+        result = ""
+        # Entire XML is kept in one line, if line begins with <TestResult>, extract it
+        if self._testResultBegin in line:
+            result += line[line.find(self._testResultBegin):
+                           line.find(self._testResultEnd) + len(self._testResultEnd)]
+            line = line[0:line.find(self._testResultBegin)] + line[line.find(self._testResultEnd) +
+                                                                   len(self._testResultEnd):]
+        sys.stdout.write(line)
+        sys.stdout.flush()
+
+        return result
+
+
+    def parseOutputFromProcess(self, p):
+        """Parses stdout from given subprocess p.
+        """
+        testResult = ""
+        # Dump test results
+        while True:
+            outline = p.stdout.readline()
+            testResult += self.__parseAndWriteLine(outline)
+            # Break if process has ended and everything has been read
+            if not outline and p.poll() != None:
+                break
+
+        return testResult
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b647c7d
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (c) 2014 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Jan Olszak (j.olszak@samsung.com)
+#
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the Unit Tests...")
+
+FILE(GLOB_RECURSE project_SRCS *.cpp *.hpp)
+FILE(GLOB         socket_test_SRCS ${SOCKET_TEST_FOLDER}/*.cpp ${SOCKET_TEST_FOLDER}/*.hpp)
+
+# We must compile socket-test separately, exclude it from unit-test build
+LIST(REMOVE_ITEM project_SRCS ${socket_test_SRCS})
+
+## Setup target ################################################################
+SET(UT_SERVER_CODENAME "${PROJECT_NAME}-unit-tests")
+ADD_EXECUTABLE(${UT_SERVER_CODENAME} ${project_SRCS})
+
+IF(NOT WITHOUT_SYSTEMD)
+SET(SOCKET_TEST_CODENAME "${PROJECT_NAME}-socket-test")
+
+## A stub mini-service to test socket functionality
+ADD_EXECUTABLE(${SOCKET_TEST_CODENAME} ${socket_test_SRCS})
+ENDIF(NOT WITHOUT_SYSTEMD)
+
+## Link libraries ##############################################################
+FIND_PACKAGE (Boost REQUIRED COMPONENTS unit_test_framework system filesystem)
+
+INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${LIBS_FOLDER} ${UNIT_TESTS_FOLDER} ${SOCKET_TEST_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS} ${JSON_C_INCLUDE_DIRS} ${CARGO_IPC_DEPS_INCLUDE_DIRS})
+
+SET_TARGET_PROPERTIES(${UT_SERVER_CODENAME} PROPERTIES
+    COMPILE_FLAGS "-pthread"
+    LINK_FLAGS "-pthread"
+)
+
+TARGET_LINK_LIBRARIES(${UT_SERVER_CODENAME} ${Boost_LIBRARIES}
+                      Logger cargo-json cargo-sqlite cargo-fd cargo-ipc cargo-validator)
+
+IF(NOT WITHOUT_SYSTEMD)
+SET_TARGET_PROPERTIES(${SOCKET_TEST_CODENAME} PROPERTIES
+    COMPILE_FLAGS "-pthread"
+    LINK_FLAGS "-pthread"
+)
+
+TARGET_LINK_LIBRARIES(${SOCKET_TEST_CODENAME} ${Boost_LIBRARIES} Logger cargo-ipc)
+ENDIF(NOT WITHOUT_SYSTEMD)
+
+## Subdirectories ##############################################################
+SET(CARGO_TEST_CONFIG_INSTALL_DIR ${CARGO_CONFIG_INSTALL_DIR}/tests)
+ADD_DEFINITIONS(-DCARGO_TEST_CONFIG_INSTALL_DIR="${CARGO_TEST_CONFIG_INSTALL_DIR}")
+
+ADD_SUBDIRECTORY(configs)
+
+## Install #####################################################################
+INSTALL(TARGETS ${UT_SERVER_CODENAME} DESTINATION bin)
+
+IF(NOT WITHOUT_SYSTEMD)
+INSTALL(TARGETS ${SOCKET_TEST_CODENAME} DESTINATION bin)
+ENDIF(NOT WITHOUT_SYSTEMD)
diff --git a/tests/unit_tests/cargo-ipc/ut-ipc.cpp b/tests/unit_tests/cargo-ipc/ut-ipc.cpp
new file mode 100644 (file)
index 0000000..90c9aff
--- /dev/null
@@ -0,0 +1,917 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Tests of the IPC
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "cargo-ipc/service.hpp"
+#include "cargo-ipc/client.hpp"
+#include "cargo-ipc/types.hpp"
+#include "cargo-ipc/unique-id.hpp"
+#include "cargo-ipc/result.hpp"
+#include "cargo-ipc/epoll/thread-dispatcher.hpp"
+#include "cargo-ipc/epoll/glib-dispatcher.hpp"
+#include "utils/channel.hpp"
+#include "utils/glib-loop.hpp"
+#include "utils/latch.hpp"
+#include "utils/value-latch.hpp"
+#include "utils/scoped-dir.hpp"
+
+#include "cargo/fields.hpp"
+#include "logger/logger.hpp"
+
+#include <boost/filesystem.hpp>
+#include <fstream>
+#include <atomic>
+#include <string>
+#include <thread>
+#include <chrono>
+#include <utility>
+#include <future>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+
+using namespace cargo::ipc;
+using namespace epoll;
+using namespace utils;
+using namespace std::placeholders;
+namespace fs = boost::filesystem;
+
+// Timeout for sending one message
+const int TIMEOUT = 1000 /*ms*/;
+
+// Time that won't cause "TIMEOUT" methods to throw
+const int SHORT_OPERATION_TIME = TIMEOUT / 100;
+
+// Time that will cause "TIMEOUT" methods to throw
+const int LONG_OPERATION_TIME = 1000 + TIMEOUT;
+
+const std::string TEST_DIR = "/tmp/ut-ipc";
+const std::string SOCKET_PATH = TEST_DIR + "/test.socket";
+const std::string TEST_FILE = TEST_DIR + "/file.txt";
+
+struct FixtureBase {
+    ScopedDir mTestPathGuard;
+
+    FixtureBase()
+        : mTestPathGuard(TEST_DIR)
+    {
+    }
+};
+
+struct ThreadedFixture : FixtureBase {
+    ThreadDispatcher dispatcher;
+
+    EventPoll& getPoll() {
+        return dispatcher.getPoll();
+    }
+};
+
+struct GlibFixture : FixtureBase {
+    ScopedGlibLoop glibLoop;
+    GlibDispatcher dispatcher;
+
+    EventPoll& getPoll() {
+        return dispatcher.getPoll();
+    }
+};
+
+struct SendData {
+    int intVal;
+    SendData(int i): intVal(i) {}
+
+    CARGO_REGISTER
+    (
+        intVal
+    )
+};
+
+struct RecvData {
+    int intVal;
+    RecvData(): intVal(-1) {}
+
+    CARGO_REGISTER
+    (
+        intVal
+    )
+};
+
+struct FDData {
+    cargo::FileDescriptor fd;
+    FDData(int fd = -1): fd(fd) {}
+
+    CARGO_REGISTER
+    (
+        fd
+    )
+};
+
+struct LongSendData {
+    LongSendData(int i, int waitTime): mSendData(i), mWaitTime(waitTime), intVal(i) {}
+
+    template<typename Visitor>
+    void accept(Visitor visitor)
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime));
+        mSendData.accept(visitor);
+    }
+    template<typename Visitor>
+    void accept(Visitor visitor) const
+    {
+        std::this_thread::sleep_for(std::chrono::milliseconds(mWaitTime));
+        mSendData.accept(visitor);
+    }
+
+    SendData mSendData;
+    int mWaitTime;
+    int intVal;
+};
+
+struct EmptyData {
+    CARGO_REGISTER_EMPTY
+};
+
+struct ThrowOnAcceptData {
+    template<typename Visitor>
+    static void accept(Visitor)
+    {
+        throw std::runtime_error("intentional failure in accept");
+    }
+};
+
+HandlerExitCode returnEmptyCallback(const PeerID,
+                                                std::shared_ptr<EmptyData>&,
+                                                MethodResult::Pointer methodResult)
+{
+    methodResult->setVoid();
+    return HandlerExitCode::SUCCESS;
+}
+
+HandlerExitCode returnDataCallback(const PeerID,
+                                   std::shared_ptr<RecvData>&,
+                                   MethodResult::Pointer methodResult)
+{
+    auto returnData = std::make_shared<SendData>(1);
+    methodResult->set(returnData);
+    return HandlerExitCode::SUCCESS;
+}
+
+HandlerExitCode echoCallback(const PeerID,
+                             std::shared_ptr<RecvData>& data,
+                             MethodResult::Pointer methodResult)
+{
+    auto returnData = std::make_shared<SendData>(data->intVal);
+    methodResult->set(returnData);
+    return HandlerExitCode::SUCCESS;
+}
+
+HandlerExitCode longEchoCallback(const PeerID,
+                                 std::shared_ptr<RecvData>& data,
+                                 MethodResult::Pointer methodResult)
+{
+    std::this_thread::sleep_for(std::chrono::milliseconds(LONG_OPERATION_TIME));
+    auto returnData = std::make_shared<SendData>(data->intVal);
+    methodResult->set(returnData);
+    return HandlerExitCode::SUCCESS;
+}
+
+HandlerExitCode shortEchoCallback(const PeerID,
+                                  std::shared_ptr<RecvData>& data,
+                                  MethodResult::Pointer methodResult)
+{
+    std::this_thread::sleep_for(std::chrono::milliseconds(SHORT_OPERATION_TIME));
+    auto returnData = std::make_shared<SendData>(data->intVal);
+    methodResult->set(returnData);
+    return HandlerExitCode::SUCCESS;
+}
+
+PeerID connectPeer(Service& s, Client& c)
+{
+    // Connects the Client to the Service and returns Clients PeerID
+    ValueLatch<PeerID> peerIDLatch;
+    auto newPeerCallback = [&peerIDLatch](const PeerID newID, const FileDescriptor) {
+        peerIDLatch.set(newID);
+    };
+
+    s.setNewPeerCallback(newPeerCallback);
+
+    if (!s.isStarted()) {
+        s.start();
+    }
+
+    c.start();
+
+    PeerID peerID = peerIDLatch.get(TIMEOUT);
+    s.setNewPeerCallback(nullptr);
+    BOOST_REQUIRE_NE(peerID, static_cast<std::string>(cargo::ipc::UniqueID()));
+    return peerID;
+}
+
+void testEcho(Client& c, const MethodID methodID)
+{
+    std::shared_ptr<SendData> sentData(new SendData(34));
+    std::shared_ptr<RecvData> recvData = c.callSync<SendData, RecvData>(methodID, sentData, TIMEOUT);
+    BOOST_REQUIRE(recvData);
+    BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
+}
+
+void testEcho(Service& s, const MethodID methodID, const PeerID& peerID)
+{
+    std::shared_ptr<SendData> sentData(new SendData(56));
+    std::shared_ptr<RecvData> recvData = s.callSync<SendData, RecvData>(methodID, peerID, sentData, TIMEOUT);
+    BOOST_REQUIRE(recvData);
+    BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
+}
+
+BOOST_AUTO_TEST_SUITE(IPCSuite)
+
+MULTI_FIXTURE_TEST_CASE(ConstructorDestructor, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+}
+
+MULTI_FIXTURE_TEST_CASE(ServiceAddRemoveMethod, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<EmptyData, EmptyData>(1, returnEmptyCallback);
+    s.setMethodHandler<SendData, RecvData>(1, returnDataCallback);
+
+    s.start();
+
+    s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    s.setMethodHandler<SendData, RecvData>(2, returnDataCallback);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+    testEcho(c, 1);
+
+    s.removeMethod(1);
+    s.removeMethod(2);
+
+    BOOST_CHECK_THROW(testEcho(c, 2), IPCException);
+}
+
+MULTI_FIXTURE_TEST_CASE(ClientAddRemoveMethod, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.setMethodHandler<EmptyData, EmptyData>(1, returnEmptyCallback);
+    c.setMethodHandler<SendData, RecvData>(1, returnDataCallback);
+
+    PeerID peerID = connectPeer(s, c);
+
+    c.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    c.setMethodHandler<SendData, RecvData>(2, returnDataCallback);
+
+    testEcho(s, 1, peerID);
+
+    c.removeMethod(1);
+    c.removeMethod(2);
+
+    BOOST_CHECK_THROW(testEcho(s, 1, peerID), IPCException);
+}
+
+MULTI_FIXTURE_TEST_CASE(MethodResultGetPeerID, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+
+    PeerID peerID = connectPeer(s, c);
+
+    s.setMethodHandler<SendData, RecvData>(
+        1,
+        [&peerID](const PeerID,
+                  std::shared_ptr<RecvData>&,
+    MethodResult::Pointer methodResult) {
+        methodResult->setVoid();
+        BOOST_CHECK_EQUAL(peerID, methodResult->getPeerID());
+        return HandlerExitCode::SUCCESS;
+    }
+    );
+
+    std::shared_ptr<SendData> sentData(new SendData(32));
+    std::shared_ptr<RecvData> recvData = c.callSync<SendData, RecvData>(1,
+                                         sentData,
+                                         TIMEOUT);
+}
+
+MULTI_FIXTURE_TEST_CASE(ServiceStartStop, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+
+    s.setMethodHandler<SendData, RecvData>(1, returnDataCallback);
+
+    s.start();
+    s.stop();
+    s.start();
+    s.stop();
+
+    s.start();
+    s.start();
+}
+
+MULTI_FIXTURE_TEST_CASE(ClientStartStop, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.setMethodHandler<SendData, RecvData>(1, returnDataCallback);
+
+    c.start();
+    c.stop();
+    c.start();
+    c.stop();
+
+    c.start();
+    c.start();
+
+    c.stop();
+    c.stop();
+}
+
+MULTI_FIXTURE_TEST_CASE(SyncClientToServiceEcho, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    s.setMethodHandler<SendData, RecvData>(2, echoCallback);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    testEcho(c, 1);
+    testEcho(c, 2);
+}
+
+MULTI_FIXTURE_TEST_CASE(Restart, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    s.start();
+    s.setMethodHandler<SendData, RecvData>(2, echoCallback);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.start();
+    testEcho(c, 1);
+    testEcho(c, 2);
+
+    c.stop();
+    c.start();
+
+    testEcho(c, 1);
+    testEcho(c, 2);
+
+    s.stop();
+    s.start();
+
+    BOOST_CHECK_THROW(testEcho(c, 2), IPCException);
+
+    c.stop();
+    c.start();
+
+    testEcho(c, 1);
+    testEcho(c, 2);
+}
+
+MULTI_FIXTURE_TEST_CASE(SyncServiceToClientEcho, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    PeerID peerID = connectPeer(s, c);
+
+    std::shared_ptr<SendData> sentData(new SendData(56));
+    std::shared_ptr<RecvData> recvData = s.callSync<SendData, RecvData>(1, peerID, sentData);
+    BOOST_REQUIRE(recvData);
+    BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
+}
+
+MULTI_FIXTURE_TEST_CASE(AsyncClientToServiceEcho, F, ThreadedFixture, GlibFixture)
+{
+    std::shared_ptr<SendData> sentData(new SendData(34));
+    ValueLatch<std::shared_ptr<RecvData>> recvDataLatch;
+
+    // Setup Service and Client
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    s.start();
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.start();
+
+    //Async call
+    auto dataBack = [&recvDataLatch](Result<RecvData> && r) {
+        recvDataLatch.set(r.get());
+    };
+    c.callAsync<SendData, RecvData>(1, sentData, dataBack);
+
+    // Wait for the response
+    std::shared_ptr<RecvData> recvData(recvDataLatch.get(TIMEOUT));
+    BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
+}
+
+MULTI_FIXTURE_TEST_CASE(AsyncServiceToClientEcho, F, ThreadedFixture, GlibFixture)
+{
+    std::shared_ptr<SendData> sentData(new SendData(56));
+    ValueLatch<std::shared_ptr<RecvData>> recvDataLatch;
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    PeerID peerID = connectPeer(s, c);
+
+    // Async call
+    auto dataBack = [&recvDataLatch](Result<RecvData> && r) {
+        recvDataLatch.set(r.get());
+    };
+
+    s.callAsync<SendData, RecvData>(1, peerID, sentData, dataBack);
+
+    // Wait for the response
+    std::shared_ptr<RecvData> recvData(recvDataLatch.get(TIMEOUT));
+    BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
+}
+
+
+MULTI_FIXTURE_TEST_CASE(SyncTimeout, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, longEchoCallback);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    std::shared_ptr<SendData> sentData(new SendData(78));
+    BOOST_REQUIRE_THROW((c.callSync<SendData, RecvData>(1, sentData, TIMEOUT)), IPCException);
+}
+
+MULTI_FIXTURE_TEST_CASE(SerializationError, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    std::shared_ptr<ThrowOnAcceptData> throwingData(new ThrowOnAcceptData());
+
+    BOOST_CHECK_THROW((c.callSync<ThrowOnAcceptData, RecvData>(1, throwingData)), IPCSerializationException);
+
+}
+
+MULTI_FIXTURE_TEST_CASE(ParseError, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+    s.start();
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.start();
+
+    std::shared_ptr<SendData> sentData(new SendData(78));
+    BOOST_CHECK_THROW((c.callSync<SendData, ThrowOnAcceptData>(1, sentData, 10000)), IPCParsingException);
+}
+
+MULTI_FIXTURE_TEST_CASE(DisconnectedPeerError, F, ThreadedFixture, GlibFixture)
+{
+    ValueLatch<Result<RecvData>> retStatusLatch;
+
+    Service s(F::getPoll(), SOCKET_PATH);
+
+    auto method = [](const PeerID, std::shared_ptr<ThrowOnAcceptData>&, MethodResult::Pointer methodResult) {
+        auto resultData = std::make_shared<SendData>(1);
+        methodResult->set<SendData>(resultData);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    // Method will throw during serialization and disconnect automatically
+    s.setMethodHandler<SendData, ThrowOnAcceptData>(1, method);
+    s.start();
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.start();
+
+    auto dataBack = [&retStatusLatch](Result<RecvData> && r) {
+        retStatusLatch.set(std::move(r));
+    };
+
+    std::shared_ptr<SendData> sentData(new SendData(78));
+    c.callAsync<SendData, RecvData>(1, sentData, dataBack);
+
+    // Wait for the response
+    Result<RecvData> result = retStatusLatch.get(TIMEOUT);
+
+    // The disconnection might have happened:
+    // - after sending the message (PEER_DISCONNECTED)
+    // - during external serialization (SERIALIZATION_ERROR)
+    BOOST_CHECK_THROW(result.get(), IPCException);
+}
+
+
+MULTI_FIXTURE_TEST_CASE(ReadTimeout, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    auto longEchoCallback = [](const PeerID, std::shared_ptr<RecvData>& data, MethodResult::Pointer methodResult) {
+        auto resultData = std::make_shared<LongSendData>(data->intVal, LONG_OPERATION_TIME);
+        methodResult->set<LongSendData>(resultData);
+        return HandlerExitCode::SUCCESS;
+    };
+    s.setMethodHandler<LongSendData, RecvData>(1, longEchoCallback);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    // Test timeout on read
+    std::shared_ptr<SendData> sentData(new SendData(334));
+    BOOST_CHECK_THROW((c.callSync<SendData, RecvData>(1, sentData, TIMEOUT)), IPCException);
+}
+
+
+MULTI_FIXTURE_TEST_CASE(WriteTimeout, F, ThreadedFixture, GlibFixture)
+{
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, shortEchoCallback);
+    s.start();
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    c.start();
+
+    // Test echo with a minimal timeout
+    std::shared_ptr<LongSendData> sentDataA(new LongSendData(34, SHORT_OPERATION_TIME));
+    std::shared_ptr<RecvData> recvData = c.callSync<LongSendData, RecvData>(1, sentDataA, TIMEOUT);
+    BOOST_REQUIRE(recvData);
+    BOOST_CHECK_EQUAL(recvData->intVal, sentDataA->intVal);
+
+    // Test timeout on write
+    std::shared_ptr<LongSendData> sentDataB(new LongSendData(34, LONG_OPERATION_TIME));
+    BOOST_CHECK_THROW((c.callSync<LongSendData, RecvData>(1, sentDataB, TIMEOUT)), IPCTimeoutException);
+}
+
+
+MULTI_FIXTURE_TEST_CASE(AddSignalInRuntime, F, ThreadedFixture, GlibFixture)
+{
+    ValueLatch<std::shared_ptr<RecvData>> recvDataLatchA;
+    ValueLatch<std::shared_ptr<RecvData>> recvDataLatchB;
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    auto handlerA = [&recvDataLatchA](const PeerID, std::shared_ptr<RecvData>& data) {
+        recvDataLatchA.set(data);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    auto handlerB = [&recvDataLatchB](const PeerID, std::shared_ptr<RecvData>& data) {
+        recvDataLatchB.set(data);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    c.setSignalHandler<RecvData>(1, handlerA);
+    c.setSignalHandler<RecvData>(2, handlerB);
+
+    // Wait for the signals to propagate to the Service
+    std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT));
+
+    auto sendDataA = std::make_shared<SendData>(1);
+    auto sendDataB = std::make_shared<SendData>(2);
+    s.signal<SendData>(2, sendDataB);
+    s.signal<SendData>(1, sendDataA);
+
+    // Wait for the signals to arrive
+    std::shared_ptr<RecvData> recvDataA(recvDataLatchA.get(TIMEOUT));
+    std::shared_ptr<RecvData> recvDataB(recvDataLatchB.get(TIMEOUT));
+    BOOST_CHECK_EQUAL(recvDataA->intVal, sendDataA->intVal);
+    BOOST_CHECK_EQUAL(recvDataB->intVal, sendDataB->intVal);
+}
+
+
+MULTI_FIXTURE_TEST_CASE(AddSignalOffline, F, ThreadedFixture, GlibFixture)
+{
+    ValueLatch<std::shared_ptr<RecvData>> recvDataLatchA;
+    ValueLatch<std::shared_ptr<RecvData>> recvDataLatchB;
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+
+    auto handlerA = [&recvDataLatchA](const PeerID, std::shared_ptr<RecvData>& data) {
+        recvDataLatchA.set(data);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    auto handlerB = [&recvDataLatchB](const PeerID, std::shared_ptr<RecvData>& data) {
+        recvDataLatchB.set(data);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    c.setSignalHandler<RecvData>(1, handlerA);
+    c.setSignalHandler<RecvData>(2, handlerB);
+
+    connectPeer(s, c);
+
+    // Wait for the information about the signals to propagate
+    std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT));
+
+    auto sendDataA = std::make_shared<SendData>(1);
+    auto sendDataB = std::make_shared<SendData>(2);
+    s.signal<SendData>(2, sendDataB);
+    s.signal<SendData>(1, sendDataA);
+
+    // Wait for the signals to arrive
+    std::shared_ptr<RecvData> recvDataA(recvDataLatchA.get(TIMEOUT));
+    std::shared_ptr<RecvData> recvDataB(recvDataLatchB.get(TIMEOUT));
+    BOOST_CHECK_EQUAL(recvDataA->intVal, sendDataA->intVal);
+    BOOST_CHECK_EQUAL(recvDataB->intVal, sendDataB->intVal);
+}
+
+MULTI_FIXTURE_TEST_CASE(UsersError, F, ThreadedFixture, GlibFixture)
+{
+    const int TEST_ERROR_CODE = -234;
+    const std::string TEST_ERROR_MESSAGE = "Ay, caramba!";
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+    auto clientID = connectPeer(s, c);
+
+    auto throwingMethodHandler = [&](const PeerID, std::shared_ptr<RecvData>&, MethodResult::Pointer) {
+        throw IPCUserException(TEST_ERROR_CODE, TEST_ERROR_MESSAGE);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    auto sendErrorMethodHandler = [&](const PeerID, std::shared_ptr<RecvData>&, MethodResult::Pointer methodResult) {
+        methodResult->setError(TEST_ERROR_CODE, TEST_ERROR_MESSAGE);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    s.setMethodHandler<SendData, RecvData>(1, throwingMethodHandler);
+    s.setMethodHandler<SendData, RecvData>(2, sendErrorMethodHandler);
+    c.setMethodHandler<SendData, RecvData>(1, throwingMethodHandler);
+    c.setMethodHandler<SendData, RecvData>(2, sendErrorMethodHandler);
+
+    std::shared_ptr<SendData> sentData(new SendData(78));
+
+    auto hasProperData = [&](const IPCUserException & e) {
+        return e.getCode() == TEST_ERROR_CODE && e.what() == TEST_ERROR_MESSAGE;
+    };
+
+    BOOST_CHECK_EXCEPTION((c.callSync<SendData, RecvData>(1, sentData, TIMEOUT)), IPCUserException, hasProperData);
+    BOOST_CHECK_EXCEPTION((s.callSync<SendData, RecvData>(1, clientID, sentData, TIMEOUT)), IPCUserException, hasProperData);
+    BOOST_CHECK_EXCEPTION((c.callSync<SendData, RecvData>(2, sentData, TIMEOUT)), IPCUserException, hasProperData);
+    BOOST_CHECK_EXCEPTION((s.callSync<SendData, RecvData>(2, clientID, sentData, TIMEOUT)), IPCUserException, hasProperData);
+}
+
+MULTI_FIXTURE_TEST_CASE(AsyncResult, F, ThreadedFixture, GlibFixture)
+{
+    const int TEST_ERROR_CODE = -567;
+    const std::string TEST_ERROR_MESSAGE = "Ooo jooo!";
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    Client c(F::getPoll(), SOCKET_PATH);
+    auto clientID = connectPeer(s, c);
+
+    auto errorMethodHandler = [&](const PeerID, std::shared_ptr<RecvData>&, MethodResult::Pointer methodResult) {
+        std::async(std::launch::async, [&, methodResult] {
+            std::this_thread::sleep_for(std::chrono::milliseconds(SHORT_OPERATION_TIME));
+            methodResult->setError(TEST_ERROR_CODE, TEST_ERROR_MESSAGE);
+        });
+        return HandlerExitCode::SUCCESS;
+    };
+
+    auto voidMethodHandler = [&](const PeerID, std::shared_ptr<RecvData>&, MethodResult::Pointer methodResult) {
+        std::async(std::launch::async, [methodResult] {
+            std::this_thread::sleep_for(std::chrono::milliseconds(SHORT_OPERATION_TIME));
+            methodResult->setVoid();
+        });
+        return HandlerExitCode::SUCCESS;
+    };
+
+    auto dataMethodHandler = [&](const PeerID, std::shared_ptr<RecvData>& data, MethodResult::Pointer methodResult) {
+        std::async(std::launch::async, [data, methodResult] {
+            std::this_thread::sleep_for(std::chrono::milliseconds(SHORT_OPERATION_TIME));
+            methodResult->set(data);
+        });
+        return HandlerExitCode::SUCCESS;
+    };
+
+    s.setMethodHandler<SendData, RecvData>(1, errorMethodHandler);
+    s.setMethodHandler<EmptyData, RecvData>(2, voidMethodHandler);
+    s.setMethodHandler<SendData, RecvData>(3, dataMethodHandler);
+    c.setMethodHandler<SendData, RecvData>(1, errorMethodHandler);
+    c.setMethodHandler<EmptyData, RecvData>(2, voidMethodHandler);
+    c.setMethodHandler<SendData, RecvData>(3, dataMethodHandler);
+
+    std::shared_ptr<SendData> sentData(new SendData(90));
+
+    auto hasProperData = [&](const IPCUserException & e) {
+        return e.getCode() == TEST_ERROR_CODE && e.what() == TEST_ERROR_MESSAGE;
+    };
+
+    BOOST_CHECK_EXCEPTION((s.callSync<SendData, RecvData>(1, clientID, sentData, TIMEOUT)), IPCUserException, hasProperData);
+    BOOST_CHECK_EXCEPTION((c.callSync<SendData, RecvData>(1, sentData, TIMEOUT)), IPCUserException, hasProperData);
+
+    BOOST_CHECK_NO_THROW((s.callSync<SendData, EmptyData>(2, clientID, sentData, TIMEOUT)));
+    BOOST_CHECK_NO_THROW((c.callSync<SendData, EmptyData>(2, sentData, TIMEOUT)));
+
+    std::shared_ptr<RecvData> recvData;
+    recvData = s.callSync<SendData, RecvData>(3, clientID, sentData, TIMEOUT);
+    BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
+    recvData = c.callSync<SendData, RecvData>(3, sentData, TIMEOUT);
+    BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
+}
+
+MULTI_FIXTURE_TEST_CASE(MixOperations, F, ThreadedFixture, GlibFixture)
+{
+    utils::Latch l;
+
+    auto signalHandler = [&l](const PeerID, std::shared_ptr<RecvData>&) {
+        l.set();
+        return HandlerExitCode::SUCCESS;
+    };
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    s.setSignalHandler<RecvData>(2, signalHandler);
+
+    connectPeer(s, c);
+
+    testEcho(c, 1);
+
+    auto data = std::make_shared<SendData>(1);
+    c.signal<SendData>(2, data);
+
+    BOOST_CHECK(l.wait(TIMEOUT));
+}
+
+MULTI_FIXTURE_TEST_CASE(FDSendReceive, F, ThreadedFixture, GlibFixture)
+{
+    const char DATA[] = "Content of the file";
+    {
+        // Fill the file
+        fs::remove(TEST_FILE);
+        std::ofstream file(TEST_FILE);
+        file << DATA;
+        file.close();
+    }
+
+    auto methodHandler = [&](const PeerID, std::shared_ptr<EmptyData>&, MethodResult::Pointer methodResult) {
+        int fd = ::open(TEST_FILE.c_str(), O_RDONLY);
+        auto returnData = std::make_shared<FDData>(fd);
+        methodResult->set(returnData);
+        return HandlerExitCode::SUCCESS;
+    };
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<FDData, EmptyData>(1, methodHandler);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    std::shared_ptr<FDData> fdData;
+    std::shared_ptr<EmptyData> sentData(new EmptyData());
+    fdData = c.callSync<EmptyData, FDData>(1, sentData, TIMEOUT);
+
+    // Use the file descriptor
+    char buffer[sizeof(DATA)];
+    BOOST_REQUIRE(::read(fdData->fd.value, buffer, sizeof(buffer))>0);
+    BOOST_REQUIRE(strncmp(DATA, buffer, strlen(DATA))==0);
+    ::close(fdData->fd.value);
+}
+
+MULTI_FIXTURE_TEST_CASE(OneShotMethodHandler, F, ThreadedFixture, GlibFixture)
+{
+    auto methodHandler = [&](const PeerID, std::shared_ptr<EmptyData>&, MethodResult::Pointer methodResult) {
+        methodResult->setVoid();
+        return HandlerExitCode::REMOVE_HANDLER;
+    };
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setMethodHandler<EmptyData, EmptyData>(1, methodHandler);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    std::shared_ptr<EmptyData> sentData(new EmptyData());
+    c.callSync<EmptyData, EmptyData>(1, sentData, TIMEOUT);
+    BOOST_REQUIRE_THROW((c.callSync<EmptyData, EmptyData>(1, sentData, TIMEOUT)), IPCNaughtyPeerException);
+}
+
+MULTI_FIXTURE_TEST_CASE(OneShotSignalHandler, F, ThreadedFixture, GlibFixture)
+{
+    utils::Latch latch;
+
+    auto signalHandler = [&latch](const PeerID, std::shared_ptr<EmptyData>&) {
+        latch.set();
+        return HandlerExitCode::REMOVE_HANDLER;
+    };
+
+    Service s(F::getPoll(), SOCKET_PATH);
+    s.setSignalHandler<EmptyData>(1, signalHandler);
+
+    Client c(F::getPoll(), SOCKET_PATH);
+    connectPeer(s, c);
+
+    // Wait for the signals to propagate to the Service
+    std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT));
+
+    std::shared_ptr<EmptyData> sentData(new EmptyData());
+    c.signal<EmptyData>(1, sentData);
+
+    // Wait for the signal
+    BOOST_REQUIRE(latch.wait(TIMEOUT));
+    BOOST_REQUIRE(!s.isHandled(1));
+}
+
+BOOST_AUTO_TEST_CASE(ConnectionLimit)
+{
+    const unsigned oldLimit = utils::getMaxFDNumber();
+    const unsigned newLimit = 128;
+    ScopedDir scopedDir(TEST_DIR);
+
+    Channel c;
+
+    const pid_t chpid = ::fork();
+    BOOST_CHECK_NE(chpid, -1);
+
+    if (chpid) {
+        // Setup Service
+        ThreadDispatcher td;
+        Service s(td.getPoll(), SOCKET_PATH);
+        s.setMethodHandler<SendData, RecvData>(1, echoCallback);
+        s.start();
+
+        c.setLeft();
+        try {
+            // inform the Client
+            c.write(true);
+        } catch (...) {
+            kill(chpid, 9);
+            throw;
+        }
+
+        int status;
+        BOOST_CHECK_EQUAL(::waitpid(chpid, &status, 0), chpid);
+        BOOST_CHECK_EQUAL(status, EXIT_SUCCESS);
+    } else {
+        int ret = EXIT_FAILURE;
+
+        c.setRight();
+        try {
+            // wait for the Service
+            c.read<char>();
+        } catch(...) {
+            ::_exit(EXIT_FAILURE);
+        }
+
+        ThreadDispatcher td;
+        std::list<Client> clients;
+
+        utils::setMaxFDNumber(newLimit);
+
+        // Setup Clients
+        try {
+            for (unsigned i = 0; i < 2 * newLimit; ++i) {
+                clients.emplace_back(td.getPoll(), SOCKET_PATH);
+                clients.back().start();
+            }
+        } catch (const EventFDException& e) {
+            ret = EXIT_SUCCESS;
+        } catch (const IPCSocketException& e) {
+            if (e.getCode() == EMFILE) {
+                ret = EXIT_SUCCESS;
+            }
+        }
+
+        utils::setMaxFDNumber(oldLimit);
+
+        ::_exit(ret);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/cargo-ipc/ut-socket.cpp b/tests/unit_tests/cargo-ipc/ut-socket.cpp
new file mode 100644 (file)
index 0000000..4cdd7b5
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Socket IPC module tests
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+#include "logger/logger.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+
+#include <thread>
+
+using namespace cargo::ipc;
+using namespace cargo::ipc::internals;
+
+const std::string SOCKET_PATH = "/tmp/test.socket";
+
+BOOST_AUTO_TEST_SUITE(SocketSuite)
+
+#ifdef HAVE_SYSTEMD
+#include "socket-test.hpp"
+
+BOOST_AUTO_TEST_CASE(SystemdSocket)
+{
+    std::string readMessage;
+
+    {
+        Socket socket = Socket::connectUNIX(socket_test::SOCKET_PATH);
+        BOOST_REQUIRE_GT(socket.getFD(), -1);
+
+        readMessage.resize(socket_test::TEST_MESSAGE.size());
+        socket.read(&readMessage.front(), readMessage.size());
+    }
+
+    BOOST_REQUIRE_EQUAL(readMessage, socket_test::TEST_MESSAGE);
+}
+#endif // HAVE_SYSTEMD
+
+BOOST_AUTO_TEST_CASE(GetSocketType)
+{
+    {
+        Socket socket;
+        BOOST_CHECK(socket.getType() == Socket::Type::INVALID);
+    }
+
+    {
+        Socket socket = Socket::createINET("localhost", "");
+        BOOST_CHECK(socket.getType() == Socket::Type::INET);
+    }
+
+    {
+        Socket socket = Socket::createUNIX(SOCKET_PATH);
+        BOOST_CHECK(socket.getType() == Socket::Type::UNIX);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(InternetSocket)
+{
+    const char msg[] = "MESSAGE";
+    const std::string host = "127.0.0.1";
+
+    Socket server = Socket::createINET(host, "");
+    const unsigned short port = server.getPort();
+
+    BOOST_CHECK(server.getType() == Socket::Type::INET);
+
+    auto clientThread = std::thread([&] {
+        Socket client = Socket::connectINET(host, std::to_string(port));
+        BOOST_CHECK(client.getType() == Socket::Type::INET);
+        client.write(msg, sizeof(msg));
+
+        char buffer[sizeof(msg)];
+        client.read(buffer, sizeof(msg));
+        BOOST_CHECK_EQUAL(buffer, msg);
+    });
+
+    auto connection = server.accept();
+    char buffer[sizeof(msg)];
+
+    connection->read(buffer, sizeof(msg));
+    BOOST_CHECK_EQUAL(buffer, msg);
+
+    connection->write(msg, sizeof(msg));
+
+    clientThread.join();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/cargo-ipc/ut-unique-id.cpp b/tests/unit_tests/cargo-ipc/ut-unique-id.cpp
new file mode 100644 (file)
index 0000000..9410b60
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Unit tests of UID
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "cargo-ipc/unique-id.hpp"
+
+#include <string>
+#include <sstream>
+#include <uuid/uuid.h>
+
+namespace {
+
+const std::string EMPTY_UUID = "00000000-0000-0000-0000-000000000000";
+
+} // namespace
+
+BOOST_AUTO_TEST_SUITE(UniqueIDSuite)
+
+// constructor should provide empty timestamp and UUID
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+    cargo::ipc::UniqueID uid;
+
+    BOOST_CHECK_EQUAL(static_cast<std::int64_t>(uid.mTime.tv_sec), 0);
+    BOOST_CHECK_EQUAL(uid.mTime.tv_nsec, 0);
+    char uuid[37]; // 36 chars + terminating zero
+    ::uuid_unparse(uid.mUUID, uuid);
+    BOOST_CHECK(EMPTY_UUID.compare(uuid) == 0);
+}
+
+// generate one UID and compare with empty
+BOOST_AUTO_TEST_CASE(Generate)
+{
+    cargo::ipc::UniqueID uid, emptyuid;
+    uid.generate();
+
+    BOOST_CHECK_NE(uid, emptyuid);
+}
+
+// generate two UIDs and compare them
+BOOST_AUTO_TEST_CASE(DoubleGenerate)
+{
+    cargo::ipc::UniqueID uid1, uid2;
+
+    uid1.generate();
+    uid2.generate();
+    BOOST_CHECK_NE(uid1, uid2);
+}
+
+// compare two empty UIDs
+BOOST_AUTO_TEST_CASE(EmptyCompare)
+{
+    cargo::ipc::UniqueID uid1, uid2;
+
+    BOOST_CHECK_EQUAL(uid1, uid2);
+}
+
+// pass empty UID to a stream
+BOOST_AUTO_TEST_CASE(StreamOperator)
+{
+    cargo::ipc::UniqueID uid;
+    std::stringstream ss;
+
+    ss << uid;
+    BOOST_CHECK_EQUAL(ss.str(), "0.0:" + EMPTY_UUID);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/cargo/testconfig-example.hpp b/tests/unit_tests/cargo/testconfig-example.hpp
new file mode 100644 (file)
index 0000000..adfcc72
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Krzysztof Dynowski (k.dynowski@samsumg.com)
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief   Test configuration struct to be used in unit tests
+ */
+
+#ifndef TESTCONFIG_EXAMPLE_HPP
+#define TESTCONFIG_EXAMPLE_HPP
+
+#include <array>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <map>
+#include <tuple>
+
+#include "cargo/fields.hpp"
+#include "cargo/fields-union.hpp"
+#include "cargo-validator/validator.hpp"
+
+enum class TestEnum: int {
+    FIRST = 0,
+    SECOND = 12,
+    THIRD = 13
+};
+
+struct TestMapStruct
+{
+    std::string type;
+    std::string source;
+    std::vector<std::string> options;
+
+    bool operator==(const TestMapStruct& m) const
+    {
+        return ((m.type == type) && (m.source == source) && (m.options == options));
+    }
+
+    CARGO_REGISTER
+    (
+        type,
+        source,
+        options
+    )
+};
+
+struct TestConfig {
+    // subtree class
+    struct SubConfig {
+
+        struct SubSubConfig {
+
+            SubSubConfig() : intVal(), moved(false) {}
+            SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {}
+            SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) {
+                config.moved = true;
+            }
+            SubSubConfig& operator=(const SubSubConfig& config) {
+                intVal = config.intVal;
+                moved = false;
+                return *this;
+            }
+            SubSubConfig& operator=(SubSubConfig&& config) {
+                intVal = std::move(config.intVal);
+                moved = false;
+                config.moved = true;
+                return *this;
+            }
+            bool isMoved() const {
+                return moved;
+            }
+
+            int intVal;
+            bool moved;
+            CARGO_REGISTER
+            (
+                intVal
+            )
+        };
+
+        int intVal;
+        std::vector<int> intVector;
+        SubSubConfig subSubObj;
+
+        CARGO_REGISTER
+        (
+            intVal,
+            intVector,
+            subSubObj
+        )
+    };
+
+    struct SubConfigOption {
+        CARGO_DECLARE_UNION
+        (
+            SubConfig,
+            int
+        )
+    };
+
+    typedef std::tuple<std::string, std::pair<int, double>> ComplexTuple;
+
+    std::int8_t int8Val;
+    std::int16_t int16Val;
+    int intVal;
+    std::int64_t int64Val;
+    std::uint8_t uint8Val;
+    std::uint32_t uint32Val;
+    std::uint64_t uint64Val;
+    std::string stringVal;
+    double doubleVal;
+    bool boolVal;
+    TestEnum enumVal;
+
+    std::vector<int> emptyIntVector;
+    std::vector<int> intVector;
+    std::vector<std::string> stringVector;
+    std::vector<double> doubleVector;
+
+    std::array<int, 2> intArray;
+
+    std::pair<int, int> intIntPair;
+    ComplexTuple complexTuple;
+    std::tuple<SubConfig> subObjTuple;
+    std::pair<SubConfig, int> subObjIntPair;
+
+    SubConfig subObj;
+    std::vector<SubConfig> subVector;
+
+    SubConfigOption union1;
+    SubConfigOption union2;
+    std::vector<SubConfigOption> unions;
+    std::map<std::string, std::string> simpleMap;
+    std::map<std::string, TestMapStruct> map;
+    std::string dirPath;
+    std::string filePath;
+
+    static bool isShorter(const std::string &shorter, const std::string &longer) {
+        return shorter.size() < longer.size();
+    }
+
+    CARGO_REGISTER
+    (
+        int8Val,
+        int16Val,
+        intVal,
+        int64Val,
+        uint8Val,
+        uint32Val,
+        uint64Val,
+        stringVal,
+        doubleVal,
+        boolVal,
+        enumVal,
+
+        emptyIntVector,
+        intVector,
+        stringVector,
+        doubleVector,
+
+        intArray,
+
+        intIntPair,
+        complexTuple,
+        subObjTuple,
+        subObjIntPair,
+
+        subObj,
+        subVector,
+
+        union1,
+        union2,
+        unions,
+
+        simpleMap,
+        map,
+        dirPath,
+        filePath
+    )
+
+    CARGO_VALIDATE
+    (
+        using namespace std::placeholders;
+
+        CARGO_CHECK(cargo::validator::isNonEmptyString, stringVal, dirPath)
+        CARGO_CHECK(std::bind(cargo::validator::isNonEmptyString, _1), filePath);
+        CARGO_CHECK(cargo::validator::isAbsolutePath, dirPath, filePath)
+        CARGO_CHECK(cargo::validator::isFilePresent, filePath)
+        CARGO_CHECK(cargo::validator::isDirectoryPresent, dirPath)
+
+        // custom validator
+        CARGO_CHECK([](const std::string & v)->bool { return v.compare("blah")==0; }, stringVal)
+
+        CARGO_COMPARE([](const std::uint32_t arg_A,
+                         const std::uint64_t arg_B) {
+                             return arg_A < arg_B;
+                         },
+                         int8Val,
+                         uint8Val)
+        CARGO_COMPARE(std::bind(isShorter, _1, _2), filePath, dirPath)
+    )
+};
+
+struct PartialTestConfig {
+    // subtree class
+    struct SubConfig {
+        // a subset of TestConfig::SubConfig fields
+        int intVal = 64;
+
+        CARGO_REGISTER
+        (
+            intVal
+        )
+    };
+
+    struct SubConfigOption {
+        CARGO_DECLARE_UNION
+        (
+            SubConfig,
+            int
+        )
+    };
+
+    // a subset of TestConfig fields
+    std::string stringVal = "partialConfig";
+    std::vector<int> intVector = {1, 2, 4, 8, 16};
+    TestEnum enumVal = TestEnum::THIRD;
+    std::vector<SubConfig> subVector = {SubConfig()};
+    SubConfigOption union1;
+
+    PartialTestConfig() {
+        union1.set(SubConfig());
+    }
+
+    CARGO_REGISTER
+    (
+        stringVal,
+        intVector,
+        enumVal,
+        subVector,
+        union1
+    )
+};
+
+struct IncompatibleTestConfig: public PartialTestConfig {
+    std::array<int, 3> intArray = {{1, 2 ,4}};
+
+    CARGO_EXTEND(PartialTestConfig)
+    (
+        intArray
+    )
+};
+
+/**
+ * JSON string used in ConfigSuite test cases
+ * For the purpose of these tests the order of this string
+ * has to be equal to the above REGISTER order
+ */
+const std::string jsonTestString =
+    "{ \"int8Val\": 5, "
+    "\"int16Val\": 11235, "
+    "\"intVal\": 12345, "
+    "\"int64Val\": -1234567890123456789, "
+    "\"uint8Val\": 42, "
+    "\"uint32Val\": 123456, "
+    "\"uint64Val\": 1234567890123456789, "
+    "\"stringVal\": \"blah\", "
+    "\"doubleVal\": -1.234000, "
+    "\"boolVal\": true, "
+    "\"enumVal\": 12, "
+    "\"emptyIntVector\": [ ], "
+    "\"intVector\": [ 1, 2, 3 ], "
+    "\"stringVector\": [ \"a\", \"b\" ], "
+    "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], "
+    "\"intArray\": [ 0, 1 ], "
+    "\"intIntPair\": [ 8, 9 ], "
+    "\"complexTuple\": [ \"tuple\", [ 54, -1.234000 ] ], "
+    "\"subObjTuple\": [ { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } } ], "
+    "\"subObjIntPair\": [ { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, 50 ], "
+    "\"subObj\": { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, "
+    "\"subVector\": [ { \"intVal\": 123, \"intVector\": [ 3, 4 ], \"subSubObj\": { \"intVal\": 345 } }, "
+    "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], "
+    "\"union1\": { \"type\": \"int\", \"value\": 2 }, "
+    "\"union2\": { \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], "
+    "\"subSubObj\": { \"intVal\": 234 } } }, "
+    "\"unions\": [ "
+    "{ \"type\": \"int\", \"value\": 2 }, "
+    "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], "
+    "\"subSubObj\": { \"intVal\": 234 } } } ], "
+    "\"simpleMap\": { \"key\": \"value\", \"key2\": \"value2\" }, "
+    "\"map\": { \"dev\": { \"type\": \"tmpfs\", \"source\": \"tmpfs\", \"options\": "
+                               "[ \"nosuid\", \"strictatime\", \"mode=755\", \"size=65536k\" ] }, "
+               "\"proc\": { \"type\": \"proc\", \"source\": \"proc\", \"options\": [ ] } }, "
+    "\"dirPath\": \"\\/usr\\/local\\/lib\", "
+    "\"filePath\": \"\\/bin\\/bash\" }";
+
+const std::string jsonEmptyTestString =
+    "{ \"int8Val\": 0, "
+    "\"int16Val\": 0, "
+    "\"intVal\": 0, "
+    "\"int64Val\": 0, "
+    "\"uint8Val\": 0, "
+    "\"uint32Val\": 0, "
+    "\"uint64Val\": 0, "
+    "\"stringVal\": \"\", "
+    "\"doubleVal\": 0.0, "
+    "\"boolVal\": false, "
+    "\"enumVal\": 0, "
+    "\"emptyIntVector\": [ ], "
+    "\"intVector\": [ ], "
+    "\"stringVector\": [ ], "
+    "\"doubleVector\": [ ], "
+    "\"intArray\": [ 0, 0 ], "
+    "\"intIntPair\": [ 0, 0 ], "
+    "\"complexTuple\": [ \"\", [ 0, 0.0 ] ], "
+    "\"subObjTuple\": [ { \"intVal\": 0, \"intVector\": [ 0, 0 ], \"subSubObj\": { \"intVal\": 0 } } ], "
+    "\"subObjIntPair\": [ { \"intVal\": 0, \"intVector\": [ 0, 0 ], \"subSubObj\": { \"intVal\": 0 } }, 0 ], "
+    "\"subObj\": { \"intVal\": 0, \"intVector\": [ ], \"subSubObj\": { \"intVal\": 0 } }, "
+    "\"subVector\": [ ], "
+    "\"union1\": { \"type\": \"int\", \"value\": 0 }, "
+    "\"union2\": { \"type\": \"int\", \"value\": 0 }, "
+    "\"unions\": [ ], "
+    "\"simpleMap\": { }, "
+    "\"map\": { }, "
+    "\"dirPath\": \"\", "
+    "\"filePath\": \"\" }";
+
+#endif
diff --git a/tests/unit_tests/cargo/ut-cargo.cpp b/tests/unit_tests/cargo/ut-cargo.cpp
new file mode 100644 (file)
index 0000000..3b6862d
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Michal Witanowski <m.witanowski@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Michal Witanowski (m.witanowski@samsung.com)
+ * @brief   Unit test of Configuration
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+#include "testconfig-example.hpp"
+#include "cargo-gvariant/cargo-gvariant.hpp"
+#include "cargo-fd/cargo-fd.hpp"
+#include "cargo-sqlite/cargo-sqlite.hpp"
+#include "cargo-json/cargo-json.hpp"
+#include "cargo-sqlite-json/cargo-sqlite-json.hpp"
+#include "utils/scoped-dir.hpp"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace {
+
+using namespace utils;
+using namespace cargo;
+using namespace cargo::internals;
+
+const std::string UT_PATH = "/tmp/ut-config/";
+const std::string DB_PATH = UT_PATH + "kvstore.db3";
+const std::string DB_PREFIX = "ut";
+
+// Floating point tolerance as a number of rounding errors
+const int TOLERANCE = 1;
+
+struct Fixture {
+    ScopedDir mUTDirGuard;
+    Fixture() : mUTDirGuard(UT_PATH) {}
+};
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(CargoSuite, Fixture)
+
+BOOST_AUTO_TEST_CASE(FromJsonString)
+{
+    TestConfig testConfig;
+
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+
+    BOOST_CHECK_EQUAL(12345, testConfig.intVal);
+    BOOST_CHECK_EQUAL(-1234567890123456789ll, testConfig.int64Val);
+    BOOST_CHECK_EQUAL(123456, testConfig.uint32Val);
+    BOOST_CHECK_EQUAL(1234567890123456789ll, testConfig.uint64Val);
+    BOOST_CHECK_EQUAL("blah", testConfig.stringVal);
+    BOOST_CHECK_CLOSE(-1.234, testConfig.doubleVal, TOLERANCE);
+    BOOST_CHECK_EQUAL(true, testConfig.boolVal);
+    BOOST_CHECK(TestEnum::SECOND == testConfig.enumVal);
+
+    BOOST_REQUIRE_EQUAL(0, testConfig.emptyIntVector.size());
+
+    BOOST_REQUIRE_EQUAL(3, testConfig.intVector.size());
+    BOOST_CHECK_EQUAL(1,   testConfig.intVector[0]);
+    BOOST_CHECK_EQUAL(2,   testConfig.intVector[1]);
+    BOOST_CHECK_EQUAL(3,   testConfig.intVector[2]);
+
+    BOOST_REQUIRE_EQUAL(2, testConfig.stringVector.size());
+    BOOST_CHECK_EQUAL("a", testConfig.stringVector[0]);
+    BOOST_CHECK_EQUAL("b", testConfig.stringVector[1]);
+
+    BOOST_REQUIRE_EQUAL(3, testConfig.doubleVector.size());
+    BOOST_CHECK_CLOSE(0.0, testConfig.doubleVector[0], TOLERANCE);
+    BOOST_CHECK_CLOSE(1.0, testConfig.doubleVector[1], TOLERANCE);
+    BOOST_CHECK_CLOSE(2.0, testConfig.doubleVector[2], TOLERANCE);
+
+    BOOST_REQUIRE_EQUAL(2, testConfig.intArray.size());
+    BOOST_CHECK_EQUAL(0,   testConfig.intArray[0]);
+    BOOST_CHECK_EQUAL(1,   testConfig.intArray[1]);
+
+    BOOST_CHECK_EQUAL(8, testConfig.intIntPair.first);
+    BOOST_CHECK_EQUAL(9, testConfig.intIntPair.second);
+
+    BOOST_CHECK_EQUAL("tuple",   std::get<0>(testConfig.complexTuple));
+    BOOST_CHECK_EQUAL(54,        std::get<1>(testConfig.complexTuple).first);
+    BOOST_CHECK_EQUAL(-1.234000, std::get<1>(testConfig.complexTuple).second);
+
+    BOOST_CHECK_EQUAL(54321,     std::get<0>(testConfig.subObjTuple).intVal);
+    BOOST_CHECK_EQUAL(2,         std::get<0>(testConfig.subObjTuple).intVector.size());
+    BOOST_CHECK_EQUAL(1,         std::get<0>(testConfig.subObjTuple).intVector[0]);
+    BOOST_CHECK_EQUAL(2,         std::get<0>(testConfig.subObjTuple).intVector[1]);
+    BOOST_CHECK_EQUAL(234,       std::get<0>(testConfig.subObjTuple).subSubObj.intVal);
+
+    BOOST_CHECK_EQUAL(54321, testConfig.subObjIntPair.first.intVal);
+    BOOST_CHECK_EQUAL(2,     testConfig.subObjIntPair.first.intVector.size());
+    BOOST_CHECK_EQUAL(1,     testConfig.subObjIntPair.first.intVector[0]);
+    BOOST_CHECK_EQUAL(2,     testConfig.subObjIntPair.first.intVector[1]);
+    BOOST_CHECK_EQUAL(234,   testConfig.subObjIntPair.first.subSubObj.intVal);
+    BOOST_CHECK_EQUAL(50,    testConfig.subObjIntPair.second);
+
+    BOOST_CHECK_EQUAL(54321, testConfig.subObj.intVal);
+    BOOST_CHECK_EQUAL(2,     testConfig.subObj.intVector.size());
+    BOOST_CHECK_EQUAL(1,     testConfig.subObj.intVector[0]);
+    BOOST_CHECK_EQUAL(2,     testConfig.subObj.intVector[1]);
+    BOOST_CHECK_EQUAL(234,   testConfig.subObj.subSubObj.intVal);
+
+    BOOST_REQUIRE_EQUAL(2, testConfig.subVector.size());
+    BOOST_CHECK_EQUAL(123, testConfig.subVector[0].intVal);
+    BOOST_CHECK_EQUAL(456, testConfig.subVector[1].intVal);
+    BOOST_CHECK_EQUAL(345, testConfig.subVector[0].subSubObj.intVal);
+    BOOST_CHECK_EQUAL(567, testConfig.subVector[1].subSubObj.intVal);
+    BOOST_CHECK_EQUAL(3,   testConfig.subVector[0].intVector[0]);
+    BOOST_CHECK_EQUAL(5,   testConfig.subVector[1].intVector[0]);
+    BOOST_CHECK_EQUAL(4,   testConfig.subVector[0].intVector[1]);
+    BOOST_CHECK_EQUAL(6,   testConfig.subVector[1].intVector[1]);
+
+    BOOST_CHECK(testConfig.union1.is<int>());
+    BOOST_CHECK_EQUAL(2, testConfig.union1.as<int>());
+
+    BOOST_CHECK(testConfig.union2.is<TestConfig::SubConfig>());
+    BOOST_CHECK_EQUAL(54321, testConfig.union2.as<TestConfig::SubConfig>().intVal);
+    BOOST_REQUIRE_EQUAL(1,   testConfig.union2.as<TestConfig::SubConfig>().intVector.size());
+    BOOST_CHECK_EQUAL(1,     testConfig.union2.as<TestConfig::SubConfig>().intVector[0]);
+    BOOST_CHECK_EQUAL(234,   testConfig.union2.as<TestConfig::SubConfig>().subSubObj.intVal);
+
+    BOOST_REQUIRE_EQUAL(2, testConfig.unions.size());
+    BOOST_CHECK(testConfig.unions[0].is<int>());
+    BOOST_CHECK_EQUAL(2, testConfig.unions[0].as<int>());
+
+    BOOST_CHECK(testConfig.unions[1].is<TestConfig::SubConfig>());
+    BOOST_CHECK_EQUAL(54321, testConfig.unions[1].as<TestConfig::SubConfig>().intVal);
+    BOOST_REQUIRE_EQUAL(1,   testConfig.unions[1].as<TestConfig::SubConfig>().intVector.size());
+    BOOST_CHECK_EQUAL(1,     testConfig.unions[1].as<TestConfig::SubConfig>().intVector[0]);
+    BOOST_CHECK_EQUAL(234,   testConfig.unions[1].as<TestConfig::SubConfig>().subSubObj.intVal);
+
+    BOOST_REQUIRE_EQUAL(2, testConfig.simpleMap.size());
+    BOOST_CHECK(testConfig.simpleMap["key"] == "value");
+    BOOST_CHECK(testConfig.simpleMap["key2"] == "value2");
+    BOOST_REQUIRE_EQUAL(2, testConfig.map.size());
+    TestMapStruct mnt_proc({"proc", "proc", {}});
+    TestMapStruct mnt_dev = {"tmpfs", "tmpfs", {"nosuid", "strictatime", "mode=755", "size=65536k"}};
+    BOOST_CHECK(mnt_proc == testConfig.map["proc"]);
+    BOOST_CHECK(mnt_dev == testConfig.map["dev"]);
+}
+
+
+BOOST_AUTO_TEST_CASE(ToJsonString)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+
+    std::string out = saveToJsonString(testConfig);
+    BOOST_CHECK_EQUAL(out, jsonTestString);
+
+    TestConfig::SubConfigOption unionConfig;
+    BOOST_CHECK_THROW(saveToJsonString(unionConfig), CargoException);
+}
+
+namespace loadErrorsTest {
+
+#define DECLARE_CONFIG(name, type) \
+    struct name { \
+        type field; \
+        CARGO_REGISTER(field) \
+    };
+DECLARE_CONFIG(IntConfig, int)
+DECLARE_CONFIG(StringConfig, std::string)
+DECLARE_CONFIG(DoubleConfig, double)
+DECLARE_CONFIG(BoolConfig, bool)
+DECLARE_CONFIG(ArrayConfig, std::vector<int>)
+DECLARE_CONFIG(ObjectConfig, IntConfig)
+#undef DECLARE_CONFIG
+struct UnionConfig {
+    CARGO_DECLARE_UNION
+    (
+            int,
+            bool
+    )
+};
+
+} // namespace loadErrorsTest
+
+BOOST_AUTO_TEST_CASE(JsonLoadErrors)
+{
+    using namespace loadErrorsTest;
+
+    IntConfig config;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString("{\"field\":1}", config));
+
+    BOOST_CHECK_THROW(loadFromJsonString("", config), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{", config), CargoException); // invalid json
+    BOOST_CHECK_THROW(loadFromJsonString("{}", config), CargoException); // missing field
+
+    // invalid type
+
+    IntConfig intConfig;
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"field\": 1}", intConfig));
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": \"1\"}", intConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1.0}", intConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": true}", intConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": []}", intConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": {}}", intConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1234567890123456789}", intConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": -1234567890123456789}", intConfig), CargoException);
+
+    StringConfig stringConfig;
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1}", stringConfig), CargoException);
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"field\": \"1\"}", stringConfig));
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1.0}", stringConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": true}", stringConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": []}", stringConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": {}}", stringConfig), CargoException);
+
+    DoubleConfig doubleConfig;
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1}", doubleConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": \"1\"}", doubleConfig), CargoException);
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"field\": 1.0}", doubleConfig));
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": true}", doubleConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": []}", doubleConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": {}}", doubleConfig), CargoException);
+
+    BoolConfig boolConfig;
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1}", boolConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": \"1\"}", boolConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1.0}", boolConfig), CargoException);
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"field\": true}", boolConfig));
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": []}", boolConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": {}}", boolConfig), CargoException);
+
+    ArrayConfig arrayConfig;
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1}", arrayConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": \"1\"}", arrayConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1.0}", arrayConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": true}", arrayConfig), CargoException);
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"field\": []}", arrayConfig));
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": {}}", arrayConfig), CargoException);
+
+    ObjectConfig objectConfig;
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1}", objectConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": \"1\"}", objectConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": 1.0}", objectConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": true}", objectConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": []}", objectConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"field\": {}}", objectConfig), CargoException);
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"field\": {\"field\": 1}}", objectConfig));
+
+    UnionConfig unionConfig;
+    BOOST_CHECK_THROW(loadFromJsonString("{\"type\": \"long\", \"value\": 1}", unionConfig), CargoException);
+    BOOST_CHECK_THROW(loadFromJsonString("{\"type\": \"int\"}", unionConfig), CargoException);
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"type\": \"int\", \"value\": 1}", unionConfig));
+    BOOST_CHECK_NO_THROW(loadFromJsonString("{\"type\": \"bool\", \"value\": true}", unionConfig));
+}
+
+namespace hasVisitableTest {
+
+struct NotVisitable {};
+struct Visitable {
+    template<typename V>
+    void accept(V v);
+};
+struct ConstVisitable {
+    template<typename V>
+    void accept(V v) const;
+};
+struct FullVisitable {
+    template<typename V>
+    void accept(V v);
+    template<typename V>
+    void accept(V v) const;
+};
+struct DerivedVisitable : FullVisitable {};
+struct MissingArg {
+    template<typename V>
+    void accept();
+};
+struct WrongArg {
+    template<typename V>
+    void accept(int v);
+};
+struct NotFunction {
+    int accept;
+};
+
+} // namespace hasVisitableTest
+
+BOOST_AUTO_TEST_CASE(HasVisibleInternalHelper)
+{
+    using namespace hasVisitableTest;
+
+    static_assert(isVisitable<Visitable>::value, "");
+    static_assert(isVisitable<ConstVisitable>::value, "");
+    static_assert(isVisitable<FullVisitable>::value, "");
+    static_assert(isVisitable<DerivedVisitable>::value, "");
+
+    static_assert(!isVisitable<NotVisitable>::value, "");
+    static_assert(!isVisitable<MissingArg>::value, "");
+    static_assert(!isVisitable<WrongArg>::value, "");
+    static_assert(!isVisitable<NotFunction>::value, "");
+
+    BOOST_CHECK(isVisitable<Visitable>());
+}
+
+BOOST_AUTO_TEST_CASE(FromToKVStore)
+{
+    TestConfig config;
+    loadFromJsonString(jsonTestString, config);
+
+    saveToKVStore(DB_PATH, config, DB_PREFIX);
+    TestConfig outConfig;
+    loadFromKVStore(DB_PATH, outConfig, DB_PREFIX);
+
+    std::string out = saveToJsonString(outConfig);
+    BOOST_CHECK_EQUAL(out, jsonTestString);
+}
+
+BOOST_AUTO_TEST_CASE(FromToFD)
+{
+    TestConfig config;
+    loadFromJsonString(jsonTestString, config);
+    // Setup fd
+    std::string fifoPath = UT_PATH + "fdstore";
+    BOOST_CHECK(::mkfifo(fifoPath.c_str(), S_IWUSR | S_IRUSR) >= 0);
+    int fd = ::open(fifoPath.c_str(), O_RDWR);
+    BOOST_REQUIRE(fd >= 0);
+
+    // The test
+    saveToFD(fd, config);
+    TestConfig outConfig;
+    loadFromFD(fd, outConfig);
+    std::string out = saveToJsonString(outConfig);
+    BOOST_CHECK_EQUAL(out, jsonTestString);
+
+    // Cleanup
+    BOOST_CHECK(::close(fd) >= 0);
+}
+
+BOOST_AUTO_TEST_CASE(FromToInternetFD)
+{
+    TestConfig config;
+    loadFromJsonString(jsonTestString, config);
+    // Setup fd
+    std::string fifoPath = UT_PATH + "fdstore";
+    BOOST_CHECK(::mkfifo(fifoPath.c_str(), S_IWUSR | S_IRUSR) >= 0);
+    int fd = ::open(fifoPath.c_str(), O_RDWR);
+    BOOST_REQUIRE(fd >= 0);
+
+    // The test
+    saveToInternetFD(fd, config);
+    TestConfig outConfig;
+    loadFromInternetFD(fd, outConfig);
+    std::string out = saveToJsonString(outConfig);
+    BOOST_CHECK_EQUAL(out, jsonTestString);
+
+    // Cleanup
+    BOOST_CHECK(::close(fd) >= 0);
+}
+
+BOOST_AUTO_TEST_CASE(FromKVWithDefaults)
+{
+    TestConfig config;
+    loadFromJsonString(jsonTestString, config);
+
+    // nothing in db
+    TestConfig outConfig1;
+    loadFromKVStoreWithJson(DB_PATH, jsonTestString, outConfig1, DB_PREFIX);
+
+    std::string out1 = saveToJsonString(outConfig1);
+    BOOST_CHECK_EQUAL(out1, jsonTestString);
+
+    // all in db
+    saveToKVStore(DB_PATH, config, DB_PREFIX);
+    TestConfig outConfig2;
+    loadFromKVStoreWithJson(DB_PATH, jsonEmptyTestString, outConfig2, DB_PREFIX);
+
+    std::string out2 = saveToJsonString(outConfig2);
+    BOOST_CHECK_EQUAL(out2, jsonTestString);
+}
+
+BOOST_AUTO_TEST_CASE(FromIncompleteKVWithDefaults)
+{
+    IncompatibleTestConfig partialConfig;
+    saveToKVStore(DB_PATH, partialConfig, DB_PREFIX);
+
+    TestConfig outConfig;
+    loadFromKVStoreWithJson(DB_PATH, jsonTestString, outConfig, DB_PREFIX);
+
+    BOOST_CHECK_EQUAL(outConfig.stringVal, partialConfig.stringVal);
+    BOOST_CHECK(outConfig.intVector == partialConfig.intVector);
+
+    // array from KV is not compatible, should default to JSON
+    std::array<int, 2> defaultIntArray = {{0, 1}};
+    BOOST_CHECK(outConfig.intArray == defaultIntArray);
+
+    BOOST_CHECK(outConfig.enumVal == TestEnum::THIRD);
+
+    BOOST_CHECK_EQUAL(outConfig.subVector[0].intVal, partialConfig.subVector[0].intVal);
+
+    BOOST_CHECK_EQUAL(outConfig.union1.as<TestConfig::SubConfig>().intVal,
+                      partialConfig.union1.as<IncompatibleTestConfig::SubConfig>().intVal);
+
+    // values missing from partial config
+    BOOST_CHECK_EQUAL(outConfig.intVal, 12345);
+    BOOST_CHECK_EQUAL(outConfig.subObj.subSubObj.intVal, 234);
+}
+
+BOOST_AUTO_TEST_CASE(PartialConfig)
+{
+    // check if partial config is fully supported
+    TestConfig config;
+    loadFromJsonString(jsonTestString, config);
+
+    // from string
+    {
+        PartialTestConfig partialConfig;
+        loadFromJsonString(jsonTestString, partialConfig);
+
+        BOOST_CHECK_EQUAL(config.stringVal, partialConfig.stringVal);
+        BOOST_CHECK(config.intVector == partialConfig.intVector);
+    }
+
+    // from kv
+    {
+        PartialTestConfig partialConfig;
+        saveToKVStore(DB_PATH, config, DB_PREFIX);
+        loadFromKVStore(DB_PATH, partialConfig, DB_PREFIX);
+
+        BOOST_CHECK_EQUAL(config.stringVal, partialConfig.stringVal);
+        BOOST_CHECK(config.intVector == partialConfig.intVector);
+    }
+
+    // from kv with defaults
+    {
+        PartialTestConfig partialConfig;
+        loadFromKVStoreWithJson(DB_PATH, jsonTestString, partialConfig, DB_PREFIX);
+
+        BOOST_CHECK_EQUAL(config.stringVal, partialConfig.stringVal);
+        BOOST_CHECK(config.intVector == partialConfig.intVector);
+    }
+
+    // save to kv
+    {
+        PartialTestConfig partialConfig;
+        partialConfig.stringVal = "partial";
+        partialConfig.intVector = {7};
+        cargo::saveToKVStore(DB_PATH, partialConfig, DB_PREFIX);
+    }
+
+    // from gvariant (partial is not supported!)
+    {
+        PartialTestConfig partialConfig;
+        std::unique_ptr<GVariant, decltype(&g_variant_unref)> v(saveToGVariant(config),
+                                                                g_variant_unref);
+        BOOST_CHECK_THROW(loadFromGVariant(v.get(), partialConfig), CargoException);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(CorruptedVector)
+{
+    KVStore store(DB_PATH);
+
+    TestConfig config;
+    loadFromJsonString(jsonTestString, config);
+
+    saveToKVStore(DB_PATH, config, DB_PREFIX);
+    KVStore::Transaction transaction(store);
+    store.set(DB_PREFIX + ".intVector", "8");
+    transaction.commit();
+
+    TestConfig outConfig;
+    BOOST_CHECK_THROW(loadFromKVStore(DB_PATH, outConfig, DB_PREFIX), InternalIntegrityException);
+}
+
+BOOST_AUTO_TEST_CASE(ConfigUnion)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+
+    BOOST_CHECK(testConfig.union1.is<int>());
+    BOOST_CHECK(!testConfig.union1.is<TestConfig::SubConfig>());
+    BOOST_CHECK_EQUAL(testConfig.union1.as<int>(), 2);
+    BOOST_CHECK(!testConfig.union2.is<int>());
+    BOOST_CHECK(testConfig.union2.is<TestConfig::SubConfig>());
+    TestConfig::SubConfig& subConfig = testConfig.union2.as<TestConfig::SubConfig>();
+    BOOST_CHECK_EQUAL(subConfig.intVal, 54321);
+    BOOST_CHECK(testConfig.unions[0].is<int>());
+    BOOST_CHECK(testConfig.unions[1].is<TestConfig::SubConfig>());
+    std::string out = saveToJsonString(testConfig);
+    BOOST_CHECK_EQUAL(out, jsonTestString);
+
+    //Check copy
+
+    std::vector<TestConfig::SubConfigOption> unions(2);
+    unions[0].set<int>(2);
+    //set from const lvalue reference (copy)
+    unions[1].set(testConfig.unions[1].as<const TestConfig::SubConfig>());
+    BOOST_CHECK(!testConfig.unions[1].as<TestConfig::SubConfig>().subSubObj.isMoved());
+    //set from lvalue reference (copy)
+    unions[1].set(testConfig.unions[1].as<TestConfig::SubConfig>());
+    BOOST_CHECK(!testConfig.unions[1].as<TestConfig::SubConfig>().subSubObj.isMoved());
+    //set from const rvalue reference (copy)
+    unions[1].set(std::move(testConfig.unions[1].as<const TestConfig::SubConfig>()));
+    BOOST_CHECK(!testConfig.unions[1].as<TestConfig::SubConfig>().subSubObj.isMoved());
+    //set rvalue reference (copy -- move is disabled)
+    unions[1].set(std::move(testConfig.unions[1].as<TestConfig::SubConfig>()));
+    BOOST_CHECK(!testConfig.unions[1].as<TestConfig::SubConfig>().subSubObj.isMoved());
+    //assign lvalue reference (copy)
+    testConfig.unions[1] = unions[1];
+    BOOST_CHECK(!unions[1].as<TestConfig::SubConfig>().subSubObj.isMoved());
+    //assign rvalue reference (copy -- move is disabled)
+    testConfig.unions[1] = std::move(unions[1]);
+    BOOST_CHECK(!unions[1].as<TestConfig::SubConfig>().subSubObj.isMoved());
+
+    testConfig.unions.clear();
+    testConfig.unions = unions;
+    out = saveToJsonString(testConfig);
+    BOOST_CHECK_EQUAL(out, jsonTestString);
+}
+
+
+BOOST_AUTO_TEST_CASE(GVariantVisitor)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    std::unique_ptr<GVariant, decltype(&g_variant_unref)> v(saveToGVariant(testConfig),
+                                                            g_variant_unref);
+    TestConfig testConfig2;
+    loadFromGVariant(v.get(), testConfig2);
+    std::string out = saveToJsonString(testConfig2);
+    BOOST_CHECK_EQUAL(out, jsonTestString);
+
+    PartialTestConfig partialConfig;
+    partialConfig.stringVal = testConfig.stringVal;
+    partialConfig.intVector = testConfig.intVector;
+    v.reset(saveToGVariant(partialConfig));
+    BOOST_CHECK_THROW(loadFromGVariant(v.get(), testConfig), CargoException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/cargo/ut-dynvisit.cpp b/tests/unit_tests/cargo/ut-dynvisit.cpp
new file mode 100644 (file)
index 0000000..98cc40e
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Krzysztof Dynowski (k.dynowski@samsumg.com)
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
+ * @brief   Unit test of combine kvstore with defaults from json
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+#include "testconfig-example.hpp"
+#include "utils/scoped-dir.hpp"
+#include "cargo-sqlite/cargo-sqlite.hpp"
+#include "cargo-json/cargo-json.hpp"
+#include "cargo-sqlite-json/cargo-sqlite-json.hpp"
+
+namespace {
+
+using namespace utils;
+using namespace cargo;
+using namespace cargo::internals;
+
+const std::string UT_PATH = "/tmp/ut-config/";
+
+struct Fixture {
+    ScopedDir mUTDirGuard;
+    std::string dbPath;
+    std::string dbPrefix;
+
+    Fixture()
+        : mUTDirGuard(UT_PATH)
+        , dbPath(UT_PATH + "kvstore.db3")
+        , dbPrefix("conf")
+    {
+    }
+};
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(DynVisitSuite, Fixture)
+
+void checkJsonConfig(const TestConfig& cfg, const std::string& json)
+{
+    TestConfig cfg2;
+    loadFromJsonString(json, cfg2);
+    BOOST_CHECK_EQUAL(cfg2.intVal, cfg.intVal);
+    BOOST_CHECK_EQUAL(cfg2.int64Val, cfg.int64Val);
+    BOOST_CHECK_EQUAL(cfg2.boolVal, cfg.boolVal);
+    BOOST_CHECK_EQUAL(cfg2.stringVal, cfg.stringVal);
+    BOOST_CHECK_EQUAL(cfg2.intVector.size(), cfg.intVector.size());
+    BOOST_CHECK_EQUAL(cfg2.subObj.intVal, cfg.subObj.intVal);
+}
+
+void checkKVConfig(const TestConfig& cfg, const std::string& db)
+{
+    KVStore store(db);
+    BOOST_CHECK_EQUAL(store.get("conf.intVal"), std::to_string(cfg.intVal));
+    BOOST_CHECK_EQUAL(store.get("conf.int64Val"), std::to_string(cfg.int64Val));
+    BOOST_CHECK_EQUAL(store.get("conf.boolVal"), std::to_string(cfg.boolVal));
+    BOOST_CHECK_EQUAL(store.get("conf.stringVal"), cfg.stringVal);
+    BOOST_CHECK_EQUAL(store.get("conf.intVector"), std::to_string(cfg.intVector.size()));
+    BOOST_CHECK_EQUAL(store.get("conf.subObj.intVal"), std::to_string(cfg.subObj.intVal));
+}
+
+BOOST_AUTO_TEST_CASE(ReadConfigDefaults)
+{
+    TestConfig cfg;
+    loadFromKVStoreWithJson(dbPath, jsonTestString, cfg, dbPrefix);
+    checkJsonConfig(cfg, jsonTestString);
+}
+
+BOOST_AUTO_TEST_CASE(ReadConfigNoDefaults)
+{
+    TestConfig cfg;
+    loadFromKVStoreWithJson(dbPath, jsonTestString, cfg, dbPrefix);
+    // modify and save config
+    cfg.intVal += 5;
+    cfg.int64Val += 7777;
+    cfg.boolVal = !cfg.boolVal;
+    cfg.stringVal += "-changed";
+    cargo::saveToKVStore(dbPath, cfg, dbPrefix);
+
+    TestConfig cfg2;
+    loadFromKVStoreWithJson(dbPath, jsonTestString, cfg2, dbPrefix);
+    checkKVConfig(cfg2, dbPath);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/cargo/ut-kvstore.cpp b/tests/unit_tests/cargo/ut-kvstore.cpp
new file mode 100644 (file)
index 0000000..b241b5e
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Unit test of KVStore class
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "cargo-sqlite/internals/kvstore.hpp"
+#include "cargo/exception.hpp"
+#include "utils/scoped-dir.hpp"
+#include "utils/latch.hpp"
+
+#include <iostream>
+#include <memory>
+#include <thread>
+#include <boost/filesystem.hpp>
+
+using namespace cargo;
+using namespace utils;
+using namespace cargo::internals;
+namespace fs = boost::filesystem;
+
+namespace {
+
+const std::string UT_PATH = "/tmp/ut-config/";
+
+struct Fixture {
+    ScopedDir mUTDirGuard;
+    std::string dbPath;
+    KVStore c;
+
+    Fixture()
+        : mUTDirGuard(UT_PATH)
+        , dbPath(UT_PATH + "kvstore.db3")
+        , c(dbPath)
+    {
+    }
+};
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(KVStoreSuite, Fixture)
+
+const std::string KEY = "KEY";
+
+BOOST_AUTO_TEST_CASE(SimpleConstructorDestructor)
+{
+    std::unique_ptr<KVStore> conPtr;
+    BOOST_REQUIRE_NO_THROW(conPtr.reset(new KVStore(dbPath)));
+    BOOST_CHECK(fs::exists(dbPath));
+    BOOST_REQUIRE_NO_THROW(conPtr.reset(new KVStore(dbPath)));
+    BOOST_CHECK(fs::exists(dbPath));
+    BOOST_REQUIRE_NO_THROW(conPtr.reset());
+    BOOST_CHECK(fs::exists(dbPath));
+}
+
+BOOST_AUTO_TEST_CASE(EscapedCharacters)
+{
+    // '*' ?' '[' ']' are escaped
+    // They shouldn't influence the internal implementation
+    for (char sc: {'[', ']', '?', '*'}) {
+        std::string HARD_KEY = sc + KEY;
+
+        BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A"));
+        BOOST_CHECK_NO_THROW(c.set(KEY, "B"));
+
+        BOOST_CHECK(c.exists(HARD_KEY));
+        BOOST_CHECK(c.exists(KEY));
+
+        BOOST_CHECK_NO_THROW(c.clear());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(PrefixExists)
+{
+    // '*' ?' '[' ']' are escaped
+    // They shouldn't influence the internal implementation
+    for (char sc: {'[', ']', '?', '*'}) {
+        std::string HARD_KEY = sc + KEY;
+        std::string FIELD_HARD_KEY = HARD_KEY + ".field";
+
+        BOOST_CHECK_NO_THROW(c.set(FIELD_HARD_KEY, "C"));
+
+        BOOST_CHECK(!c.exists(KEY));
+        BOOST_CHECK(!c.exists(HARD_KEY));
+        BOOST_CHECK(c.exists(FIELD_HARD_KEY));
+
+        BOOST_CHECK(!c.prefixExists(KEY));
+        BOOST_CHECK(c.prefixExists(HARD_KEY));
+        BOOST_CHECK(c.prefixExists(FIELD_HARD_KEY));
+
+        BOOST_CHECK_NO_THROW(c.clear());
+    }
+}
+
+namespace {
+void testSingleValue(Fixture& f, const std::string& a, const std::string& b)
+{
+    // Set
+    BOOST_CHECK_NO_THROW(f.c.set(KEY, a));
+    BOOST_CHECK_EQUAL(f.c.get(KEY), a);
+
+    // Update
+    BOOST_CHECK_NO_THROW(f.c.set(KEY, b));
+    BOOST_CHECK_EQUAL(f.c.get(KEY), b);
+    BOOST_CHECK(f.c.exists(KEY));
+
+    // Remove
+    BOOST_CHECK_NO_THROW(f.c.remove(KEY));
+    BOOST_CHECK(!f.c.exists(KEY));
+    BOOST_CHECK_THROW(f.c.get(KEY), CargoException);
+}
+} // namespace
+
+
+BOOST_AUTO_TEST_CASE(SingleValue)
+{
+    testSingleValue(*this, "A", "B");
+}
+
+BOOST_AUTO_TEST_CASE(Clear)
+{
+    BOOST_CHECK_NO_THROW(c.clear());
+    BOOST_CHECK_NO_THROW(c.set(KEY, "2"));
+    BOOST_CHECK_NO_THROW(c.set(KEY + ".0", "A"));
+    BOOST_CHECK_NO_THROW(c.set(KEY + ".1", "B"));
+    BOOST_CHECK_NO_THROW(c.clear());
+    BOOST_CHECK(c.isEmpty());
+
+    BOOST_CHECK_NO_THROW(c.remove(KEY));
+    BOOST_CHECK_THROW(c.get(KEY), CargoException);
+    BOOST_CHECK_THROW(c.get(KEY), CargoException);
+}
+
+BOOST_AUTO_TEST_CASE(Transaction)
+{
+    {
+        KVStore::Transaction trans(c);
+        c.set(KEY, "a");
+        trans.commit();
+    }
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
+
+    {
+        KVStore::Transaction trans(c);
+        c.set(KEY, "b");
+        // no commit
+    }
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
+
+    {
+        KVStore::Transaction trans(c);
+        trans.commit();
+        BOOST_CHECK_THROW(trans.commit(), CargoException);
+        BOOST_CHECK_THROW(KVStore::Transaction{c}, CargoException);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(TransactionStacked)
+{
+    {
+        KVStore::Transaction transOuter(c);
+        KVStore::Transaction transInner(c);
+    }
+
+    {
+        KVStore::Transaction transOuter(c);
+        {
+            KVStore::Transaction transInner(c);
+            c.set(KEY, "a");
+            // no inner commit
+        }
+        transOuter.commit();
+    }
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
+
+    {
+        KVStore::Transaction transOuter(c);
+        {
+            KVStore::Transaction transInner(c);
+            c.set(KEY, "b");
+            transInner.commit();
+        }
+        // no outer commit
+    }
+    BOOST_CHECK_EQUAL(c.get(KEY), "a");
+
+    {
+        KVStore::Transaction transOuter(c);
+        KVStore::Transaction transInner(c);
+        transOuter.commit();
+        BOOST_CHECK_THROW(transInner.commit(), CargoException);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(TransactionThreads)
+{
+    Latch trans1Started, trans1Release, trans2Released;
+    std::thread thread1([&] {
+        KVStore::Transaction trans1(c);
+        trans1Started.set();
+        trans1Release.wait();
+    });
+    std::thread thread2([&] {
+        trans1Started.wait();
+        KVStore::Transaction trans2(c);
+        trans2Released.set();
+    });
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    BOOST_CHECK(trans2Released.empty());
+    trans1Release.set();
+    thread1.join();
+    trans2Released.wait();
+    thread2.join();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/cargo/ut-validator.cpp b/tests/unit_tests/cargo/ut-validator.cpp
new file mode 100644 (file)
index 0000000..666cb72
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Maciej Karpiuk <m.karpiuk2@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Maciej Karpiuk <m.karpiuk2@samsung.com>
+ * @brief   Unit test for cargo field validation
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+#include "cargo-json/cargo-json.hpp"
+#include "testconfig-example.hpp"
+#include "cargo-validator/exception.hpp"
+#include "cargo-validator/validator.hpp"
+
+using namespace cargo;
+using namespace validator;
+
+BOOST_AUTO_TEST_SUITE(CargoValidatorSuite)
+
+BOOST_AUTO_TEST_CASE(SuccessfulRun)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+
+    BOOST_REQUIRE_NO_THROW(validate(testConfig));
+}
+
+BOOST_AUTO_TEST_CASE(EmptyContents)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonEmptyTestString, testConfig));
+
+    BOOST_REQUIRE_THROW(validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_CASE(OneFieldModified)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    testConfig.stringVal = std::string("wrong");
+
+    BOOST_REQUIRE_THROW(validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_CASE(TwoFieldRelationship)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    testConfig.int8Val = 127;
+
+    BOOST_REQUIRE_THROW(validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_CASE(FileNotPresent)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    testConfig.filePath = std::string("coco jumbo");
+
+    BOOST_REQUIRE_THROW(validator::validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_CASE(FilePointsToDirectory)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    testConfig.filePath = std::string("/usr");
+
+    BOOST_REQUIRE_THROW(validator::validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_CASE(DirectoryNotPresent)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    testConfig.dirPath = std::string("/cocojumbo");
+
+    BOOST_REQUIRE_THROW(validator::validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_CASE(NotADirectory)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    testConfig.dirPath = std::string("/bin/bash");
+
+    BOOST_REQUIRE_THROW(validator::validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_CASE(RelativePath)
+{
+    TestConfig testConfig;
+    BOOST_REQUIRE_NO_THROW(loadFromJsonString(jsonTestString, testConfig));
+    testConfig.filePath = std::string("../myFile");
+
+    BOOST_REQUIRE_THROW(validator::validate(testConfig), VerificationException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/configs/CMakeLists.txt b/tests/unit_tests/configs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7f5e161
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright (c) 2014 Samsung Electronics Co., Ltd 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.
+#
+#
+# @file   CMakeLists.txt
+# @author Dariusz Michaluk (d.michaluk@samsung.com)
+#
+
+MESSAGE(STATUS "Installing configs for the Unit Tests")
+
+## Generate ####################################################################
+IF(NOT WITHOUT_SYSTEMD)
+CONFIGURE_FILE(systemd/cargo-socket-test.service.in
+               ${CMAKE_BINARY_DIR}/systemd/cargo-socket-test.service)
+ENDIF(NOT WITHOUT_SYSTEMD)
+
+## Install #####################################################################
+INSTALL(FILES        utils/file.txt
+        DESTINATION  ${CARGO_TEST_CONFIG_INSTALL_DIR}/utils)
+
+IF(NOT WITHOUT_SYSTEMD)
+INSTALL(FILES        systemd/cargo-socket-test.socket
+                     ${CMAKE_BINARY_DIR}/systemd/cargo-socket-test.service
+        DESTINATION  ${SYSTEMD_UNIT_DIR})
+ENDIF(NOT WITHOUT_SYSTEMD)
diff --git a/tests/unit_tests/configs/systemd/cargo-socket-test.service.in b/tests/unit_tests/configs/systemd/cargo-socket-test.service.in
new file mode 100644 (file)
index 0000000..236c5eb
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Socket tests mini-service
+
+[Service]
+Type=simple
+ExecStart=${CMAKE_INSTALL_PREFIX}/bin/cargo-socket-test
+Sockets=cargo-socket-test.socket
+StartLimitInterval=0
+StartLimitBurst=0
diff --git a/tests/unit_tests/configs/systemd/cargo-socket-test.socket b/tests/unit_tests/configs/systemd/cargo-socket-test.socket
new file mode 100644 (file)
index 0000000..a4a8ace
--- /dev/null
@@ -0,0 +1,9 @@
+[Socket]
+ListenStream=/run/cargo-socket-test.socket
+SocketMode=0755
+SmackLabelIPIn=*
+SmackLabelIPOut=@
+Service=cargo-socket-test.service
+
+[Install]
+WantedBy=sockets.target
diff --git a/tests/unit_tests/configs/utils/file.txt b/tests/unit_tests/configs/utils/file.txt
new file mode 100644 (file)
index 0000000..ea99ddf
--- /dev/null
@@ -0,0 +1,3 @@
+File content
+Line 1
+Line 2
diff --git a/tests/unit_tests/epoll/ut-event-poll.cpp b/tests/unit_tests/epoll/ut-event-poll.cpp
new file mode 100644 (file)
index 0000000..e3f8b3e
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Unit tests of event poll
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include "logger/logger.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+#include "utils/value-latch.hpp"
+#include "utils/glib-loop.hpp"
+#include "cargo-ipc/epoll/glib-dispatcher.hpp"
+#include "cargo-ipc/epoll/thread-dispatcher.hpp"
+
+using namespace utils;
+using namespace cargo::ipc;
+using namespace cargo::ipc::epoll;
+using namespace cargo::ipc::internals;
+
+namespace {
+
+const int unsigned TIMEOUT = 1000;
+
+} // namespace
+
+BOOST_AUTO_TEST_SUITE(EventPollSuite)
+
+BOOST_AUTO_TEST_CASE(EmptyPoll)
+{
+    EventPoll poll;
+    BOOST_CHECK(!poll.dispatchIteration(0));
+}
+
+BOOST_AUTO_TEST_CASE(ThreadedPoll)
+{
+    ThreadDispatcher dispatcher;
+}
+
+BOOST_AUTO_TEST_CASE(GlibPoll)
+{
+    ScopedGlibLoop loop;
+
+    GlibDispatcher dispatcher;
+}
+
+void doSocketTest(EventPoll& poll)
+{
+    using namespace std::placeholders;
+
+    const std::string PATH = "/tmp/ut-poll.sock";
+    const size_t REQUEST_LEN = 5;
+    const std::string REQUEST_GOOD = "GET 1";
+    const std::string REQUEST_BAD = "GET 7";
+    const std::string RESPONSE = "This is a response message";
+
+    // Scenario 1:
+    // client connects to server listening socket
+    // client ---good-request---> server
+    // server ---response---> client
+    // client disconnects
+    //
+    // Scenario 2:
+    // client connects to server listening socket
+    // client ---bad-request----> server
+    // server disconnects
+
+    // { server setup
+
+    auto serverCallback = [&](int /*fd*/,
+                              Events events,
+                              std::shared_ptr<Socket> socket,
+                              CallbackGuard::Tracker) {
+        LOGD("Server events: " << eventsToString(events));
+
+        if (events & EPOLLIN) {
+            std::string request(REQUEST_LEN, 'x');
+            socket->read(&request.front(), request.size());
+            if (request == REQUEST_GOOD) {
+                poll.modifyFD(socket->getFD(), EPOLLRDHUP | EPOLLOUT);
+            } else {
+                // disconnect (socket is kept in callback)
+                poll.removeFD(socket->getFD());
+            }
+        }
+
+        if (events & EPOLLOUT) {
+            socket->write(RESPONSE.data(), RESPONSE.size());
+            poll.modifyFD(socket->getFD(), EPOLLRDHUP);
+        }
+
+        if (events & EPOLLRDHUP) {
+            // client has disconnected
+            poll.removeFD(socket->getFD());
+        }
+    };
+
+    Socket listenSocket = Socket::createUNIX(PATH);
+    CallbackGuard serverSocketsGuard;
+
+    auto listenCallback = [&](int /*fd*/, Events events) {
+        LOGD("Listen events: " << eventsToString(events));
+        if (events & EPOLLIN) {
+            // accept new server connection
+            std::shared_ptr<Socket> socket = listenSocket.accept();
+            poll.addFD(socket->getFD(),
+                       EPOLLRDHUP | EPOLLIN,
+                       std::bind(serverCallback, _1, _2, socket, serverSocketsGuard.spawn()));
+        }
+    };
+
+    poll.addFD(listenSocket.getFD(), EPOLLIN, listenCallback);
+
+    // } server setup
+
+    // { client setup
+
+    auto clientCallback = [&](int /*fd*/,
+                              Events events,
+                              Socket& socket,
+                              const std::string& request,
+                              ValueLatch<std::string>& response) {
+        LOGD("Client events: " << eventsToString(events));
+
+        if (events & EPOLLOUT) {
+            socket.write(request.data(), request.size());
+            poll.modifyFD(socket.getFD(), EPOLLRDHUP | EPOLLIN);
+        }
+
+        if (events & EPOLLIN) {
+            try {
+                std::string msg(RESPONSE.size(), 'x');
+                socket.read(&msg.front(), msg.size());
+                response.set(msg);
+            } catch (UtilsException&) {
+                response.set(std::string());
+            }
+            poll.modifyFD(socket.getFD(), EPOLLRDHUP);
+        }
+
+        if (events & EPOLLRDHUP) {
+            LOGD("Server has disconnected");
+            poll.removeFD(socket.getFD()); //prevent active loop
+        }
+    };
+
+    // } client setup
+
+    // Scenario 1
+    LOGD("Scerario 1");
+    {
+        Socket client = Socket::connectUNIX(PATH);
+        ValueLatch<std::string> response;
+
+        poll.addFD(client.getFD(),
+                   EPOLLRDHUP | EPOLLOUT,
+                   std::bind(clientCallback,
+                             _1,
+                             _2,
+                             std::ref(client),
+                             REQUEST_GOOD,
+                             std::ref(response)));
+
+        BOOST_CHECK(response.get(TIMEOUT) == RESPONSE);
+
+        poll.removeFD(client.getFD());
+    }
+
+    // Scenario 2
+    LOGD("Scerario 2");
+    {
+        Socket client = Socket::connectUNIX(PATH);
+        ValueLatch<std::string> response;
+
+        poll.addFD(client.getFD(),
+                   EPOLLRDHUP | EPOLLOUT,
+                   std::bind(clientCallback,
+                             _1,
+                             _2,
+                             std::ref(client),
+                             REQUEST_BAD,
+                             std::ref(response)));
+
+        BOOST_CHECK(response.get(TIMEOUT) == std::string());
+
+        poll.removeFD(client.getFD());
+    }
+    LOGD("Done");
+
+    poll.removeFD(listenSocket.getFD());
+
+    // wait for all server sockets (ensure all EPOLLRDHUP are processed)
+    BOOST_REQUIRE(serverSocketsGuard.waitForTrackers(TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(ThreadedPollSocket)
+{
+    ThreadDispatcher dispatcher;
+
+    doSocketTest(dispatcher.getPoll());
+}
+
+BOOST_AUTO_TEST_CASE(GlibPollSocket)
+{
+    ScopedGlibLoop loop;
+
+    GlibDispatcher dispatcher;
+
+    doSocketTest(dispatcher.getPoll());
+}
+
+BOOST_AUTO_TEST_CASE(PollStacking)
+{
+    ThreadDispatcher dispatcher;
+
+    EventPoll innerPoll;
+
+    auto dispatchInner = [&](int, Events) {
+        innerPoll.dispatchIteration(0);
+    };
+    dispatcher.getPoll().addFD(innerPoll.getPollFD(), EPOLLIN, dispatchInner);
+    doSocketTest(innerPoll);
+    dispatcher.getPoll().removeFD(innerPoll.getPollFD());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/tests/unit_tests/log/ut-logger.cpp b/tests/unit_tests/log/ut-logger.cpp
new file mode 100644 (file)
index 0000000..4c02e8a
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Broda <p.broda@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Pawel Broda (p.broda@partner.samsung.com)
+ * @brief   Unit tests of the log utility
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+#include "logger/logger.hpp"
+#include "logger/logger-scope.hpp"
+#include "logger/formatter.hpp"
+#include "logger/backend.hpp"
+#include "logger/backend-stderr.hpp"
+
+#include <stdexcept>
+
+BOOST_AUTO_TEST_SUITE(LoggerSuite)
+
+using namespace logger;
+
+namespace {
+
+class StubbedBackend : public LogBackend {
+public:
+    StubbedBackend(std::ostringstream& s) : mLogStream(s) {}
+
+    // stubbed function
+    void log(LogLevel logLevel,
+             const std::string& file,
+             const unsigned int& line,
+             const std::string& func,
+             const std::string& message) override
+    {
+        mLogStream << '[' + toString(logLevel) + ']' + ' '
+                   << file + ':' + std::to_string(line) + ' ' + func + ':'
+                   << message << std::endl;
+    }
+
+private:
+    std::ostringstream& mLogStream;
+};
+
+class TestLog {
+public:
+    TestLog(LogLevel level)
+    {
+        Logger::setLogLevel(level);
+        Logger::setLogBackend(new StubbedBackend(mLogStream));
+    }
+
+    ~TestLog()
+    {
+        Logger::setLogLevel(LogLevel::TRACE);
+        Logger::setLogBackend(new StderrBackend());
+    }
+
+    // helpers
+    bool logContains(const std::string& expression) const
+    {
+        std::string s = mLogStream.str();
+        if (s.find(expression) != std::string::npos) {
+            return true;
+        }
+        return false;
+    }
+private:
+    std::ostringstream mLogStream;
+};
+
+void exampleTestLogs(void)
+{
+    LOGE("test log error " << "1");
+    LOGW("test log warn "  << "2");
+    LOGI("test log info "  << "3");
+    LOGD("test log debug " << "4");
+    LOGT("test log trace " << "5");
+}
+
+} // namespace
+
+BOOST_AUTO_TEST_CASE(LogLevelSetAndGet)
+{
+    Logger::setLogLevel(LogLevel::TRACE);
+    BOOST_CHECK(LogLevel::TRACE == Logger::getLogLevel());
+
+    Logger::setLogLevel(LogLevel::DEBUG);
+    BOOST_CHECK(LogLevel::DEBUG == Logger::getLogLevel());
+
+    Logger::setLogLevel(LogLevel::INFO);
+    BOOST_CHECK(LogLevel::INFO == Logger::getLogLevel());
+
+    Logger::setLogLevel(LogLevel::WARN);
+    BOOST_CHECK(LogLevel::WARN == Logger::getLogLevel());
+
+    Logger::setLogLevel(LogLevel::ERROR);
+    BOOST_CHECK(LogLevel::ERROR == Logger::getLogLevel());
+}
+
+BOOST_AUTO_TEST_CASE(StringLogLevelSetAndGet)
+{
+    Logger::setLogLevel("TRACE");
+    BOOST_CHECK(LogLevel::TRACE == Logger::getLogLevel());
+
+    Logger::setLogLevel("traCE");
+    BOOST_CHECK(LogLevel::TRACE == Logger::getLogLevel());
+
+    Logger::setLogLevel("DEBUG");
+    BOOST_CHECK(LogLevel::DEBUG == Logger::getLogLevel());
+
+    Logger::setLogLevel("INFO");
+    BOOST_CHECK(LogLevel::INFO == Logger::getLogLevel());
+
+    Logger::setLogLevel("WARN");
+    BOOST_CHECK(LogLevel::WARN == Logger::getLogLevel());
+
+    Logger::setLogLevel("ERROR");
+    BOOST_CHECK(LogLevel::ERROR == Logger::getLogLevel());
+
+    BOOST_REQUIRE_EXCEPTION(Logger::setLogLevel("UNKNOWN"),
+                            std::runtime_error,
+                            WhatEquals("Invalid LogLevel to parse"));
+}
+
+BOOST_AUTO_TEST_CASE(LogsLevelError)
+{
+    TestLog tf(LogLevel::ERROR);
+    exampleTestLogs();
+
+    BOOST_CHECK(tf.logContains("[ERROR]") == true);
+    BOOST_CHECK(tf.logContains("[WARN]")  == false);
+    BOOST_CHECK(tf.logContains("[INFO]")  == false);
+    BOOST_CHECK(tf.logContains("[DEBUG]") == false);
+    BOOST_CHECK(tf.logContains("[TRACE]") == false);
+}
+
+BOOST_AUTO_TEST_CASE(LogsLevelWarn)
+{
+    TestLog tf(LogLevel::WARN);
+    exampleTestLogs();
+
+    BOOST_CHECK(tf.logContains("[ERROR]") == true);
+    BOOST_CHECK(tf.logContains("[WARN]")  == true);
+    BOOST_CHECK(tf.logContains("[INFO]")  == false);
+    BOOST_CHECK(tf.logContains("[DEBUG]") == false);
+    BOOST_CHECK(tf.logContains("[TRACE]") == false);
+}
+
+BOOST_AUTO_TEST_CASE(LogsLevelInfo)
+{
+    TestLog tf(LogLevel::INFO);
+    exampleTestLogs();
+
+    BOOST_CHECK(tf.logContains("[ERROR]") == true);
+    BOOST_CHECK(tf.logContains("[WARN]")  == true);
+    BOOST_CHECK(tf.logContains("[INFO]")  == true);
+    BOOST_CHECK(tf.logContains("[DEBUG]") == false);
+    BOOST_CHECK(tf.logContains("[TRACE]") == false);
+}
+
+#if !defined(NDEBUG)
+BOOST_AUTO_TEST_CASE(LogsLevelDebug)
+{
+    TestLog tf(LogLevel::DEBUG);
+    exampleTestLogs();
+
+    BOOST_CHECK(tf.logContains("[ERROR]") == true);
+    BOOST_CHECK(tf.logContains("[WARN]")  == true);
+    BOOST_CHECK(tf.logContains("[INFO]")  == true);
+    BOOST_CHECK(tf.logContains("[DEBUG]") == true);
+    BOOST_CHECK(tf.logContains("[TRACE]") == false);
+}
+
+BOOST_AUTO_TEST_CASE(LogsLevelTrace)
+{
+    TestLog tf(LogLevel::TRACE);
+    exampleTestLogs();
+
+    BOOST_CHECK(tf.logContains("[ERROR]") == true);
+    BOOST_CHECK(tf.logContains("[WARN]")  == true);
+    BOOST_CHECK(tf.logContains("[INFO]")  == true);
+    BOOST_CHECK(tf.logContains("[DEBUG]") == true);
+    BOOST_CHECK(tf.logContains("[TRACE]") == true);
+}
+#endif
+
+BOOST_AUTO_TEST_CASE(LoggerScope)
+{
+    LOGS("Main function scope");
+
+    {
+        LOGS("Scope inside function");
+        LOGD("Some additional information in-between scoped logs");
+        {
+            LOGS("Additional scope with " << "stringstream" << ' ' << "test" << 3 << ' ' << 3.42);
+            LOGD("More additional information in-between scoped logs");
+        }
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/tests/unit_tests/socket_test_service/socket-test.cpp b/tests/unit_tests/socket_test_service/socket-test.cpp
new file mode 100644 (file)
index 0000000..b4d8bba
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Mini-service for IPC Socket mechanism tests
+ */
+
+#include "config.hpp"
+
+#include "socket-test.hpp"
+#include "logger/logger.hpp"
+#include "logger/backend-journal.hpp"
+#include "cargo-ipc/internals/socket.hpp"
+#include "cargo-ipc/exception.hpp"
+
+#include <cstring>
+#include <memory>
+
+using namespace socket_test;
+using namespace cargo::ipc;
+using namespace cargo::ipc::internals;
+using namespace logger;
+
+/**
+ * This is a single-usage program, only meant to test cargo::ipc::Socket module.
+ * It's purpose is to be activated when needed by systemd socket activation mechanism.
+ */
+int main()
+{
+    Logger::setLogLevel(LogLevel::TRACE);
+    Logger::setLogBackend(new SystemdJournalBackend());
+
+    try {
+        Socket listeningSocket(Socket::createUNIX(SOCKET_PATH));
+        if (listeningSocket.getFD() < 0) {
+            LOGE("Failed to connect to socket!");
+            return 1;
+        }
+
+        std::shared_ptr<Socket> clientSocket = listeningSocket.accept();
+        LOGI("Connected! Emitting message to client.");
+        clientSocket->write(TEST_MESSAGE.c_str(), TEST_MESSAGE.size());
+        LOGI("Message sent through socket! Exiting.");
+    } catch (const IPCException& e) {
+        LOGE("IPC exception caught! " << e.what());
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/tests/unit_tests/socket_test_service/socket-test.hpp b/tests/unit_tests/socket_test_service/socket-test.hpp
new file mode 100644 (file)
index 0000000..c9ef918
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Mini-service for IPC Socket mechanism tests
+ */
+
+#ifndef TESTS_SOCKET_TEST_SERVICE_HPP
+#define TESTS_SOCKET_TEST_SERVICE_HPP
+
+#include <string>
+
+namespace socket_test {
+
+const std::string SOCKET_PATH = "/run/cargo-socket-test.socket";
+const std::string TEST_MESSAGE = "Some great messages, ey!";
+
+} // namespace socket_test
+
+#endif // TESTS_SOCKET_TEST_SERVICE_HPP
diff --git a/tests/unit_tests/ut.cpp b/tests/unit_tests/ut.cpp
new file mode 100644 (file)
index 0000000..fa0a10d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Main file for the Cargo Daemon unit tests
+ */
+
+#include "config.hpp"
+
+#include "logger/logger.hpp"
+#include "logger/backend-stderr.hpp"
+
+#include <boost/test/included/unit_test.hpp>
+
+#include "utils/signal.hpp"
+
+using namespace boost::unit_test;
+using namespace logger;
+
+test_suite* init_unit_test_suite(int /*argc*/, char** /*argv*/)
+{
+    Logger::setLogLevel(LogLevel::TRACE);
+    Logger::setLogBackend(new StderrBackend());
+
+    utils::signalBlock(SIGPIPE);
+    return NULL;
+}
diff --git a/tests/unit_tests/ut.hpp b/tests/unit_tests/ut.hpp
new file mode 100644 (file)
index 0000000..e96d363
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Common unit tests include file
+ */
+
+#ifndef UNIT_TESTS_UT_HPP
+#define UNIT_TESTS_UT_HPP
+
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+
+#include <boost/mpl/vector.hpp>
+
+#include <string>
+
+/**
+ * Usage example:
+ *
+ * MULTI_FIXTURE_TEST_CASE(Test, T, Fixture1, Fixture2, Fixture3) {
+ *     std::cout << T::i << "\n";
+ * }
+ */
+#define MULTI_FIXTURE_TEST_CASE(NAME, TPARAM, ...) \
+    typedef boost::mpl::vector<__VA_ARGS__> NAME##_fixtures; \
+    BOOST_FIXTURE_TEST_CASE_TEMPLATE(NAME, TPARAM, NAME##_fixtures, TPARAM)
+
+
+/**
+ * An exception message checker
+ *
+ * Usage example:
+ * BOOST_CHECK_EXCEPTION(foo(), SomeException, WhatEquals("oops"))
+ */
+class WhatEquals {
+public:
+    explicit WhatEquals(const std::string& message)
+        : mMessage(message) {}
+
+    template <typename T>
+    bool operator()(const T& e)
+    {
+        BOOST_WARN_EQUAL(e.what(), mMessage); // additional failure info
+        return e.what() == mMessage;
+    }
+private:
+    std::string mMessage;
+};
+
+#endif // UNIT_TESTS_UT_HPP
diff --git a/tests/unit_tests/utils/ut-callback-guard.cpp b/tests/unit_tests/utils/ut-callback-guard.cpp
new file mode 100644 (file)
index 0000000..d68bcb0
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Unit tests of callback guard
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/callback-guard.hpp"
+#include "utils/latch.hpp"
+
+#include <future>
+#include <thread>
+
+BOOST_AUTO_TEST_SUITE(CallbackGuardSuite)
+
+using namespace utils;
+
+const int unsigned TIMEOUT = 1000;
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+    CallbackGuard guard;
+    BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+    BOOST_CHECK(guard.waitForTrackers(TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(Simple)
+{
+    CallbackGuard guard;
+    guard.spawn();
+    guard.spawn();
+    BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+    CallbackGuard::Tracker tracker1 = guard.spawn();
+    CallbackGuard::Tracker tracker2 = guard.spawn();
+    BOOST_CHECK_EQUAL(2, guard.getTrackersCount());
+    CallbackGuard::Tracker tracker2Copy = tracker2;
+    BOOST_CHECK_EQUAL(2, guard.getTrackersCount());
+    tracker2.reset();
+    BOOST_CHECK_EQUAL(2, guard.getTrackersCount());
+    tracker2Copy.reset();
+    BOOST_CHECK_EQUAL(1, guard.getTrackersCount());
+    tracker1.reset();
+    BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+    BOOST_CHECK(guard.waitForTrackers(TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(Thread)
+{
+    Latch trackerCreated;
+    Latch trackerCanBeDestroyed;
+    CallbackGuard guard;
+
+    std::future<bool> future = std::async(std::launch::async, [&]() -> bool {
+            CallbackGuard::Tracker tracker = guard.spawn();
+            trackerCreated.set();
+            if (!trackerCanBeDestroyed.wait(TIMEOUT)) {
+                return false;
+            }
+            std::this_thread::sleep_for(std::chrono::milliseconds(200));
+            return true;
+        });
+
+    BOOST_CHECK(trackerCreated.wait(TIMEOUT));
+    BOOST_CHECK_EQUAL(1, guard.getTrackersCount());
+
+    trackerCanBeDestroyed.set();
+    BOOST_CHECK(guard.waitForTrackers(TIMEOUT));
+    BOOST_CHECK_EQUAL(0, guard.getTrackersCount());
+
+    future.wait();
+    BOOST_CHECK(future.get());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-cargs.cpp b/tests/unit_tests/utils/ut-cargs.cpp
new file mode 100644 (file)
index 0000000..4768a60
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Krzysztof Dynowski (k.dynowski@samsumg.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsung.com)
+ * @brief   Unit tests of c-like args builder
+ */
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "utils/c-args.hpp"
+
+BOOST_AUTO_TEST_SUITE(UtilsCArgsSuite)
+
+using namespace utils;
+
+BOOST_AUTO_TEST_CASE(CArgsBuilderTest1)
+{
+    CArgsBuilder args;
+
+    {
+        std::string vx = std::to_string(20);
+        args.add(std::to_string(10))
+            .add(vx);
+    }
+    BOOST_CHECK(std::string("10") == args[0]);
+    BOOST_CHECK(std::string("20") == args[1]);
+
+    {
+        std::string vx = std::to_string(22);
+        args.add(std::to_string(12))
+            .add(vx);
+    }
+    BOOST_CHECK(std::string("10") == args[0]);
+    BOOST_CHECK(std::string("20") == args[1]);
+    BOOST_CHECK(std::string("12") == args[2]);
+    BOOST_CHECK(std::string("22") == args[3]);
+}
+
+BOOST_AUTO_TEST_CASE(CArgsBuilderTest2)
+{
+    CArgsBuilder args;
+    for (int i = 0; i < 10; ++i) {
+        args.add(i + 10);
+    }
+
+    for (int i = 0; i < 10; ++i) {
+        int t = std::stoi(args[i]);
+        BOOST_CHECK(t == i + 10);
+    }
+
+    const char * const * c_array = args.c_array();
+    for (int i = 0; i < 10; ++i) {
+        int t = std::stoi(std::string(c_array[i]));
+        BOOST_CHECK(t == i + 10);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-channel.cpp b/tests/unit_tests/utils/ut-channel.cpp
new file mode 100644 (file)
index 0000000..f9d69a4
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd 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
+ */
+
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Unit tests of the channel class
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/channel.hpp"
+#include "utils/execute.hpp"
+
+BOOST_AUTO_TEST_SUITE(ChannelSuite)
+
+using namespace utils;
+
+BOOST_AUTO_TEST_CASE(ConstructorDestructor)
+{
+    Channel c;
+}
+
+BOOST_AUTO_TEST_CASE(SetLeftRight)
+{
+    const int TEST_PASSED = 0;
+    const int ERROR = 1;
+    const int DATA = 1234;
+
+    Channel c;
+
+    pid_t pid = ::fork();
+    if (pid == -1) {
+        BOOST_REQUIRE(false);
+    }
+
+    if (pid == 0) {
+        try {
+            c.setLeft();
+            c.write(DATA);
+            c.shutdown();
+            ::_exit(TEST_PASSED);
+        } catch(...) {
+            ::_exit(ERROR);
+        }
+    }
+
+    c.setRight();
+
+    int recData = c.read<int>();
+
+    BOOST_REQUIRE(recData == DATA);
+
+    int status = -1;
+    BOOST_REQUIRE(utils::waitPid(pid, status));
+    BOOST_REQUIRE(status == TEST_PASSED);
+    c.shutdown();
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-counting-map.cpp b/tests/unit_tests/utils/ut-counting-map.cpp
new file mode 100644 (file)
index 0000000..de536db
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Unit tests of counting map
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/counting-map.hpp"
+
+BOOST_AUTO_TEST_SUITE(CountingMapSuite)
+
+using namespace utils;
+
+BOOST_AUTO_TEST_CASE(Counting)
+{
+    CountingMap<std::string> map;
+
+    BOOST_CHECK(map.empty());
+    BOOST_CHECK_EQUAL(0, map.get("ala"));
+
+    BOOST_CHECK_EQUAL(1, map.increment("ala"));
+    BOOST_CHECK_EQUAL(1, map.increment("ma"));
+
+    BOOST_CHECK(!map.empty());
+    BOOST_CHECK_EQUAL(1, map.get("ala"));
+    BOOST_CHECK_EQUAL(1, map.get("ma"));
+    BOOST_CHECK_EQUAL(0, map.get("kota"));
+
+    BOOST_CHECK_EQUAL(2, map.increment("ala"));
+    BOOST_CHECK_EQUAL(2, map.increment("ma"));
+    BOOST_CHECK_EQUAL(3, map.increment("ma"));
+
+    BOOST_CHECK(!map.empty());
+    BOOST_CHECK_EQUAL(2, map.get("ala"));
+    BOOST_CHECK_EQUAL(3, map.get("ma"));
+    BOOST_CHECK_EQUAL(0, map.get("kota"));
+
+    BOOST_CHECK_EQUAL(1, map.decrement("ala"));
+    BOOST_CHECK_EQUAL(0, map.decrement("kota"));
+
+    BOOST_CHECK(!map.empty());
+    BOOST_CHECK_EQUAL(1, map.get("ala"));
+    BOOST_CHECK_EQUAL(3, map.get("ma"));
+    BOOST_CHECK_EQUAL(0, map.get("kota"));
+
+    BOOST_CHECK_EQUAL(0, map.decrement("ala"));
+
+    BOOST_CHECK(!map.empty());
+    BOOST_CHECK_EQUAL(0, map.get("ala"));
+    BOOST_CHECK_EQUAL(3, map.get("ma"));
+    BOOST_CHECK_EQUAL(0, map.get("kota"));
+
+    BOOST_CHECK_EQUAL(2, map.decrement("ma"));
+    BOOST_CHECK_EQUAL(1, map.decrement("ma"));
+    BOOST_CHECK_EQUAL(0, map.decrement("ma"));
+
+    BOOST_CHECK(map.empty());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-fd-utils.cpp b/tests/unit_tests/utils/ut-fd-utils.cpp
new file mode 100644 (file)
index 0000000..5cbf675
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Pawel Kubik (p.kubik@samsung.com)
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Pawel Kubik (p.kubik@samsung.com)
+ * @brief   Unit tests of fd utils
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/fd-utils.hpp"
+
+#include "logger/logger.hpp"
+
+
+using namespace utils;
+
+
+BOOST_AUTO_TEST_SUITE(FDUtilsSuite)
+
+BOOST_AUTO_TEST_CASE(GetSetMaxFDNumber)
+{
+    unsigned oldLimit = utils::getMaxFDNumber();
+    unsigned newLimit = 50;
+
+    utils::setMaxFDNumber(newLimit);
+    BOOST_CHECK_EQUAL(newLimit, utils::getMaxFDNumber());
+
+    utils::setMaxFDNumber(oldLimit);
+    BOOST_CHECK_EQUAL(oldLimit, utils::getMaxFDNumber());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-fs.cpp b/tests/unit_tests/utils/ut-fs.cpp
new file mode 100644 (file)
index 0000000..d6b50ff
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ * @brief   Unit tests of utils
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/fs.hpp"
+#include "utils/exception.hpp"
+#include "utils/scoped-dir.hpp"
+
+#include <memory>
+#include <sys/mount.h>
+#include <boost/filesystem.hpp>
+
+using namespace utils;
+
+namespace {
+
+const std::string TEST_PATH = "/tmp/ut-fsutils";
+const std::string REFERENCE_FILE_PATH = CARGO_TEST_CONFIG_INSTALL_DIR "/utils/file.txt";
+const std::string REFERENCE_FILE_CONTENT = "File content\n"
+                                           "Line 1\n"
+                                           "Line 2\n";
+const std::string FILE_CONTENT_2 = "Some other content\n"
+                                   "Just to see if\n"
+                                   "everything is copied correctly\n";
+const std::string FILE_CONTENT_3 = "More content\n"
+                                   "More and more content\n"
+                                   "That's a lot of data to test\n";
+const std::string BUGGY_FILE_PATH = TEST_PATH + "/missing/file.txt";
+const std::string FILE_PATH = TEST_PATH + "/testFile";
+const std::string MOUNT_POINT_1 = TEST_PATH + "/mountPoint-1";
+const std::string MOUNT_POINT_2 = TEST_PATH + "/mountPoint-2";
+const std::string FILE_DIR_1 = "testDir-1";
+const std::string FILE_DIR_2 = "testDir-2";
+const std::string FILE_DIR_3 = "testDir-3";
+const std::string FILE_DIR_4 = "testDir-4";
+const std::string FILE_NAME_1 = "testFile-1";
+const std::string FILE_NAME_2 = "testFile-2";
+
+struct Fixture {
+    utils::ScopedDir mTestPathGuard;
+    Fixture()
+        : mTestPathGuard(TEST_PATH)
+    {}
+};
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(UtilsFSSuite, Fixture)
+
+BOOST_AUTO_TEST_CASE(ReadFileContent)
+{
+    BOOST_CHECK_EQUAL(REFERENCE_FILE_CONTENT, readFileContent(REFERENCE_FILE_PATH));
+    BOOST_CHECK_EXCEPTION(readFileContent(BUGGY_FILE_PATH),
+                          UtilsException,
+                          WhatEquals("Read failed"));
+}
+
+BOOST_AUTO_TEST_CASE(SaveFileContent)
+{
+    BOOST_REQUIRE(saveFileContent(FILE_PATH, REFERENCE_FILE_CONTENT));
+    BOOST_CHECK_EQUAL(REFERENCE_FILE_CONTENT, readFileContent(FILE_PATH));
+}
+
+BOOST_AUTO_TEST_CASE(RemoveFile)
+{
+    BOOST_REQUIRE(saveFileContent(FILE_PATH, REFERENCE_FILE_CONTENT));
+    BOOST_REQUIRE(removeFile(FILE_PATH));
+    BOOST_REQUIRE(!boost::filesystem::exists(FILE_PATH));
+}
+
+BOOST_AUTO_TEST_CASE(MountPoint)
+{
+    bool result;
+    namespace fs = boost::filesystem;
+    boost::system::error_code ec;
+
+    BOOST_REQUIRE(fs::create_directory(MOUNT_POINT_1, ec));
+    BOOST_REQUIRE(isMountPoint(MOUNT_POINT_1, result));
+    BOOST_CHECK_EQUAL(result, false);
+    BOOST_REQUIRE(hasSameMountPoint(TEST_PATH, MOUNT_POINT_1, result));
+    BOOST_CHECK_EQUAL(result, true);
+
+    BOOST_REQUIRE(mountRun(MOUNT_POINT_1));
+    BOOST_REQUIRE(isMountPoint(MOUNT_POINT_1, result));
+    BOOST_CHECK_EQUAL(result, true);
+    BOOST_REQUIRE(hasSameMountPoint(TEST_PATH, MOUNT_POINT_1, result));
+    BOOST_CHECK_EQUAL(result, false);
+
+    BOOST_REQUIRE(umount(MOUNT_POINT_1));
+    BOOST_REQUIRE(fs::remove(MOUNT_POINT_1, ec));
+}
+
+BOOST_AUTO_TEST_CASE(MoveFile)
+{
+    namespace fs = boost::filesystem;
+    boost::system::error_code ec;
+    std::string src, dst;
+
+    // same mount point
+    src = TEST_PATH + "/" + FILE_NAME_1;
+    dst = TEST_PATH + "/" + FILE_NAME_2;
+
+    BOOST_REQUIRE(saveFileContent(src, REFERENCE_FILE_CONTENT));
+
+    BOOST_CHECK(moveFile(src, dst));
+    BOOST_CHECK(!fs::exists(src));
+    BOOST_CHECK_EQUAL(readFileContent(dst), REFERENCE_FILE_CONTENT);
+
+    BOOST_REQUIRE(fs::remove(dst));
+
+    // different mount point
+    src = TEST_PATH + "/" + FILE_NAME_1;
+    dst = MOUNT_POINT_2 + "/" + FILE_NAME_2;
+
+    BOOST_REQUIRE(fs::create_directory(MOUNT_POINT_2, ec));
+    BOOST_REQUIRE(mountRun(MOUNT_POINT_2));
+    BOOST_REQUIRE(saveFileContent(src, REFERENCE_FILE_CONTENT));
+
+    BOOST_CHECK(moveFile(src, dst));
+    BOOST_CHECK(!fs::exists(src));
+    BOOST_CHECK_EQUAL(readFileContent(dst), REFERENCE_FILE_CONTENT);
+
+    BOOST_REQUIRE(fs::remove(dst));
+    BOOST_REQUIRE(umount(MOUNT_POINT_2));
+    BOOST_REQUIRE(fs::remove(MOUNT_POINT_2, ec));
+}
+
+BOOST_AUTO_TEST_CASE(CopyDirContents)
+{
+    namespace fs = boost::filesystem;
+    std::string src, src_inner, src_inner2, dst, dst_inner, dst_inner2;
+    boost::system::error_code ec;
+
+    src = TEST_PATH + "/" + FILE_DIR_1;
+    src_inner = src + "/" + FILE_DIR_3;
+    src_inner2 = src + "/" + FILE_DIR_4;
+
+    dst = TEST_PATH + "/" + FILE_DIR_2;
+    dst_inner = dst + "/" + FILE_DIR_3;
+    dst_inner2 = dst + "/" + FILE_DIR_4;
+
+    // template dir structure:
+    // |-src
+    //    |-FILE_NAME_1
+    //    |-FILE_NAME_2
+    //    |-src_inner (rw directory)
+    //    |  |-FILE_NAME_1
+    //    |
+    //    |-src_inner2 (ro directory)
+    //       |-FILE_NAME_1
+    //       |-FILE_NAME_2
+
+    // create entire structure with files
+    BOOST_REQUIRE(fs::create_directory(src, ec));
+    BOOST_REQUIRE(ec.value() == 0);
+    BOOST_REQUIRE(fs::create_directory(src_inner, ec));
+    BOOST_REQUIRE(ec.value() == 0);
+    BOOST_REQUIRE(fs::create_directory(src_inner2, ec));
+    BOOST_REQUIRE(ec.value() == 0);
+
+    BOOST_REQUIRE(saveFileContent(src + "/" + FILE_NAME_1, REFERENCE_FILE_CONTENT));
+    BOOST_REQUIRE(saveFileContent(src + "/" + FILE_NAME_2, FILE_CONTENT_2));
+    BOOST_REQUIRE(saveFileContent(src_inner + "/" + FILE_NAME_1, FILE_CONTENT_3));
+    BOOST_REQUIRE(saveFileContent(src_inner2 + "/" + FILE_NAME_1, FILE_CONTENT_3));
+    BOOST_REQUIRE(saveFileContent(src_inner2 + "/" + FILE_NAME_2, FILE_CONTENT_2));
+
+    // change permissions of src_inner2 directory
+    fs::permissions(src_inner2, fs::owner_read, ec);
+    BOOST_REQUIRE(ec.value() == 0);
+
+    // create dst directory
+    BOOST_REQUIRE(fs::create_directory(dst, ec));
+    BOOST_REQUIRE(ec.value() == 0);
+
+    // copy data
+    BOOST_CHECK(copyDirContents(src, dst));
+
+    // check if copy is successful
+    BOOST_CHECK(fs::exists(dst + "/" + FILE_NAME_1));
+    BOOST_CHECK(fs::exists(dst + "/" + FILE_NAME_2));
+    BOOST_CHECK(fs::exists(dst_inner));
+    BOOST_CHECK(fs::exists(dst_inner + "/" + FILE_NAME_1));
+    BOOST_CHECK(fs::exists(dst_inner2));
+    BOOST_CHECK(fs::exists(dst_inner2 + "/" + FILE_NAME_1));
+    BOOST_CHECK(fs::exists(dst_inner2 + "/" + FILE_NAME_2));
+
+    BOOST_CHECK_EQUAL(readFileContent(dst + "/" + FILE_NAME_1), REFERENCE_FILE_CONTENT);
+    BOOST_CHECK_EQUAL(readFileContent(dst + "/" + FILE_NAME_2), FILE_CONTENT_2);
+    BOOST_CHECK_EQUAL(readFileContent(dst_inner + "/" + FILE_NAME_1), FILE_CONTENT_3);
+    BOOST_CHECK_EQUAL(readFileContent(dst_inner2 + "/" + FILE_NAME_1), FILE_CONTENT_3);
+    BOOST_CHECK_EQUAL(readFileContent(dst_inner2 + "/" + FILE_NAME_2), FILE_CONTENT_2);
+
+    fs::file_status st = fs::status(fs::path(dst_inner2));
+    BOOST_CHECK(fs::owner_read == st.permissions());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-glib-loop.cpp b/tests/unit_tests/utils/ut-glib-loop.cpp
new file mode 100644 (file)
index 0000000..3d211d6
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
+ * @brief   Unit tests of glib-loop
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/glib-loop.hpp"
+
+#include <atomic>
+
+BOOST_AUTO_TEST_SUITE(GlibLoopSuite)
+
+using namespace utils;
+
+
+namespace {
+
+const unsigned int TIMER_INTERVAL_MS = 100;
+const unsigned int TIMER_NUMBER      = 4;
+const unsigned int TIMER_WAIT_FOR    = 2 * TIMER_NUMBER * TIMER_INTERVAL_MS;
+
+} // namespace
+
+BOOST_AUTO_TEST_CASE(GlibLoopTest)
+{
+    ScopedGlibLoop loop;
+}
+
+BOOST_AUTO_TEST_CASE(GlibTimerEvent)
+{
+    ScopedGlibLoop loop;
+    std::atomic_uint counter(0);
+
+    CallbackGuard guard;
+
+    auto callback = [&]()-> bool {
+        if (++counter >= TIMER_NUMBER) {
+            return false;
+        }
+        return true;
+    };
+
+    Glib::addTimerEvent(TIMER_INTERVAL_MS, callback, guard);
+
+    BOOST_CHECK(counter < TIMER_NUMBER);
+    BOOST_CHECK(guard.waitForTrackers(TIMER_WAIT_FOR));
+    BOOST_CHECK_EQUAL(counter, TIMER_NUMBER);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-inotify.cpp b/tests/unit_tests/utils/ut-inotify.cpp
new file mode 100644 (file)
index 0000000..ce840ee
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Unit tests of Inotify
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/inotify.hpp"
+#include "utils/fs.hpp"
+#include "utils/scoped-dir.hpp"
+#include "utils/value-latch.hpp"
+
+#include "logger/logger.hpp"
+
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include "cargo-ipc/epoll/thread-dispatcher.hpp"
+
+#include <boost/filesystem.hpp>
+
+using namespace utils;
+namespace fs = boost::filesystem;
+
+namespace {
+
+const std::string TEST_DIR  = "/tmp/ut-inotify/";
+const std::string DIR_NAME  = "dir";
+const std::string FILE_NAME = "file.txt";
+
+const std::string DIR_PATH  = TEST_DIR + DIR_NAME;
+const std::string FILE_PATH = TEST_DIR + FILE_NAME;
+
+
+struct Fixture {
+    utils::ScopedDir mTestDir;
+
+    Fixture()
+        :mTestDir(TEST_DIR)
+    {}
+};
+
+} // namespace
+
+
+BOOST_FIXTURE_TEST_SUITE(InotifySuite, Fixture)
+
+BOOST_AUTO_TEST_CASE(ConstructorDesctructor)
+{
+    cargo::ipc::epoll::EventPoll poll;
+    Inotify i(poll);
+}
+
+BOOST_AUTO_TEST_CASE(CreateDeleteFileHandler)
+{
+    cargo::ipc::epoll::ThreadDispatcher dispatcher;
+    Inotify i(dispatcher.getPoll());
+
+    // Callback on creation
+    ValueLatch<std::string> createResult;
+    i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+        createResult.set(name);
+    });
+    utils::createFile(FILE_PATH, O_WRONLY | O_CREAT, 0666);
+    BOOST_REQUIRE_EQUAL(createResult.get(), FILE_NAME);
+
+    // Redefine the callback for delete
+    ValueLatch<std::string> deleteResult;
+    i.setHandler(TEST_DIR, IN_DELETE, [&](const std::string& name, uint32_t) {
+        deleteResult.set(name);
+    });
+    fs::remove(FILE_PATH);
+    BOOST_REQUIRE_EQUAL(deleteResult.get(), FILE_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(CreateDeleteDirHandler)
+{
+    cargo::ipc::epoll::ThreadDispatcher dispatcher;
+    Inotify i(dispatcher.getPoll());
+
+    // Callback on creation
+    ValueLatch<std::string> createResult;
+    i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+        createResult.set(name);
+    });
+    utils::createEmptyDir(DIR_PATH);
+    BOOST_REQUIRE_EQUAL(createResult.get(), DIR_NAME);
+
+
+    // Redefine the callback for delete
+    ValueLatch<std::string> deleteResult;
+    i.setHandler(TEST_DIR, IN_DELETE, [&](const std::string& name, uint32_t) {
+        deleteResult.set(name);
+    });
+    fs::remove_all(DIR_PATH);
+    BOOST_REQUIRE_EQUAL(deleteResult.get(), DIR_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(NoFalseEventHandler)
+{
+    cargo::ipc::epoll::ThreadDispatcher dispatcher;
+    Inotify i(dispatcher.getPoll());
+
+    utils::createFile(FILE_PATH, O_WRONLY | O_CREAT, 0666);
+
+    // Callback on creation shouldn't be called
+    ValueLatch<std::string> createResult;
+    i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+        createResult.set(name);
+    });
+    fs::remove(FILE_PATH);
+    BOOST_REQUIRE_THROW(createResult.get(10), UtilsException);
+}
+
+BOOST_AUTO_TEST_CASE(RemoveHandler)
+{
+    cargo::ipc::epoll::ThreadDispatcher dispatcher;
+    Inotify i(dispatcher.getPoll());
+
+    // Callback on creation
+    ValueLatch<std::string> createResult;
+    i.setHandler(TEST_DIR, IN_CREATE, [&](const std::string& name, uint32_t) {
+        createResult.set(name);
+    });
+    i.removeHandler(TEST_DIR);
+    utils::createFile(FILE_PATH, O_WRONLY | O_CREAT, 0666);
+    fs::remove(FILE_PATH);
+    BOOST_REQUIRE_THROW(createResult.get(10), UtilsException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-paths.cpp b/tests/unit_tests/utils/ut-paths.cpp
new file mode 100644 (file)
index 0000000..dfd9a4a
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Unit tests of utils
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/paths.hpp"
+
+#include <memory>
+
+BOOST_AUTO_TEST_SUITE(UtilsPathsSuite)
+
+using namespace utils;
+
+BOOST_AUTO_TEST_CASE(CreateFilePath)
+{
+    BOOST_CHECK_EQUAL("", createFilePath());
+
+    BOOST_CHECK_EQUAL("a", createFilePath("a"));
+    BOOST_CHECK_EQUAL("/", createFilePath("/"));
+
+    BOOST_CHECK_EQUAL("", createFilePath("", ""));
+    BOOST_CHECK_EQUAL("a", createFilePath("a", ""));
+    BOOST_CHECK_EQUAL("b", createFilePath("", "b"));
+    BOOST_CHECK_EQUAL("/", createFilePath("", "/"));
+    BOOST_CHECK_EQUAL("/", createFilePath("/", ""));
+    BOOST_CHECK_EQUAL("/", createFilePath("/", "/"));
+
+    BOOST_CHECK_EQUAL("a/b", createFilePath("a", "b"));
+    BOOST_CHECK_EQUAL("a/b", createFilePath("a/", "b"));
+    BOOST_CHECK_EQUAL("a/b", createFilePath("a", "/b"));
+    BOOST_CHECK_EQUAL("a/b", createFilePath("a/", "/b"));
+
+    BOOST_CHECK_EQUAL("a/b.txt",  createFilePath("a", "b", ".txt"));
+    BOOST_CHECK_EQUAL("a/b.txt",  createFilePath("a/", "b", ".txt"));
+    BOOST_CHECK_EQUAL("a/b.txt",  createFilePath("a", "/b", ".txt"));
+    BOOST_CHECK_EQUAL("a/b/.txt", createFilePath("a", "/b", "/.txt"));
+    BOOST_CHECK_EQUAL("a/b/.txt", createFilePath("a", "/b/", "/.txt"));
+}
+
+BOOST_AUTO_TEST_CASE(DirName)
+{
+    BOOST_CHECK_EQUAL(".", dirName(""));
+    BOOST_CHECK_EQUAL(".", dirName("."));
+    BOOST_CHECK_EQUAL(".", dirName("./"));
+    BOOST_CHECK_EQUAL(".", dirName(".///"));
+    BOOST_CHECK_EQUAL("/", dirName("/"));
+    BOOST_CHECK_EQUAL("/", dirName("///"));
+
+    BOOST_CHECK_EQUAL("/", dirName("/level1"));
+    BOOST_CHECK_EQUAL("/", dirName("/level1/"));
+    BOOST_CHECK_EQUAL("/level1", dirName("/level1/level2"));
+    BOOST_CHECK_EQUAL("/level1", dirName("/level1/level2/"));
+    BOOST_CHECK_EQUAL("/level1/level2", dirName("/level1/level2/level3"));
+    BOOST_CHECK_EQUAL("/level1/level2", dirName("/level1/level2/level3/"));
+
+    BOOST_CHECK_EQUAL(".", dirName("level1"));
+    BOOST_CHECK_EQUAL(".", dirName("level1/"));
+    BOOST_CHECK_EQUAL("level1", dirName("level1/level2"));
+    BOOST_CHECK_EQUAL("level1", dirName("level1/level2"));
+    BOOST_CHECK_EQUAL("level1", dirName("level1/level2/"));
+    BOOST_CHECK_EQUAL("level1/level2", dirName("level1/level2/level3"));
+    BOOST_CHECK_EQUAL("level1/level2", dirName("level1/level2/level3/"));
+
+    BOOST_CHECK_EQUAL(".", dirName("./level1"));
+    BOOST_CHECK_EQUAL(".", dirName("./level1/"));
+    BOOST_CHECK_EQUAL("./level1", dirName("./level1/level2"));
+    BOOST_CHECK_EQUAL("./level1", dirName("./level1/level2/"));
+    BOOST_CHECK_EQUAL("./level1/level2", dirName("./level1/level2/level3"));
+    BOOST_CHECK_EQUAL("./level1/level2", dirName("./level1/level2/level3/"));
+
+    BOOST_CHECK_EQUAL(".", dirName(".."));
+    BOOST_CHECK_EQUAL(".", dirName("../"));
+    BOOST_CHECK_EQUAL("..", dirName("../level1"));
+    BOOST_CHECK_EQUAL("..", dirName("../level1/"));
+    BOOST_CHECK_EQUAL("../level1", dirName("../level1/level2"));
+    BOOST_CHECK_EQUAL("../level1", dirName("../level1/level2/"));
+
+    BOOST_CHECK_EQUAL("/", dirName("/.."));
+    BOOST_CHECK_EQUAL("/", dirName("/../"));
+    BOOST_CHECK_EQUAL("/level1", dirName("/level1/.."));
+    BOOST_CHECK_EQUAL("/level1", dirName("/level1/../"));
+    BOOST_CHECK_EQUAL("/level1/..", dirName("/level1/../level2"));
+    BOOST_CHECK_EQUAL("/level1/..", dirName("/level1/../level2/"));
+
+    BOOST_CHECK_EQUAL("/", dirName("///.."));
+    BOOST_CHECK_EQUAL("/", dirName("//..///"));
+    BOOST_CHECK_EQUAL("/level1", dirName("//level1//.."));
+    BOOST_CHECK_EQUAL("/level1", dirName("//level1//..///"));
+    BOOST_CHECK_EQUAL("/level1/..", dirName("//level1////..//level2"));
+    BOOST_CHECK_EQUAL("/level1/..", dirName("////level1//..////level2///"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-same-thread-guard.cpp b/tests/unit_tests/utils/ut-same-thread-guard.cpp
new file mode 100644 (file)
index 0000000..c482004
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Unit tests of same thread guard
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/same-thread-guard.hpp"
+#include <thread>
+
+#ifdef ENABLE_SAME_THREAD_GUARD
+
+BOOST_AUTO_TEST_SUITE(SameThreadGuardSuite)
+
+using namespace utils;
+
+BOOST_AUTO_TEST_CASE(Simple)
+{
+    SameThreadGuard guard;
+    BOOST_CHECK(guard.check());
+    BOOST_CHECK(guard.check());
+    guard.reset();
+    BOOST_CHECK(guard.check());
+    BOOST_CHECK(guard.check());
+}
+
+BOOST_AUTO_TEST_CASE(Thread)
+{
+    SameThreadGuard guard;
+
+    std::thread([&] {
+        BOOST_CHECK(guard.check());
+    }).join();
+
+    BOOST_CHECK(!guard.check());
+    BOOST_CHECK(!guard.check());
+
+    guard.reset();
+    BOOST_CHECK(guard.check());
+
+    std::thread([&] {
+        BOOST_CHECK(!guard.check());
+    }).join();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+#endif // ENABLE_SAME_THREAD_GUARD
diff --git a/tests/unit_tests/utils/ut-signalfd.cpp b/tests/unit_tests/utils/ut-signalfd.cpp
new file mode 100644 (file)
index 0000000..75a8686
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak <j.olszak@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Unit tests of SignalFD
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/signalfd.hpp"
+#include "cargo-ipc/epoll/event-poll.hpp"
+#include <atomic>
+#include <chrono>
+#include <thread>
+#include <csignal>
+
+
+using namespace utils;
+
+namespace {
+
+volatile sig_atomic_t gAsyncSignal;
+
+struct Fixture {
+    Fixture()
+    {
+        gAsyncSignal = 0;
+        ::signal(SIGINT, &Fixture::signalHandler);
+    }
+    ~Fixture()
+    {
+        ::signal(SIGINT, SIG_DFL);
+        gAsyncSignal = 0;
+    }
+
+    static void signalHandler(int s)
+    {
+        gAsyncSignal = s;
+    }
+
+    static bool isAsyncHandlerCalled() {
+        return gAsyncSignal != 0;
+    }
+};
+
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(SignalFDSuite, Fixture)
+
+const int TIMEOUT = 100;
+
+BOOST_AUTO_TEST_CASE(ConstructorDesctructor)
+{
+    cargo::ipc::epoll::EventPoll poll;
+    SignalFD s(poll);
+}
+
+BOOST_AUTO_TEST_CASE(BlockingSignalHandler)
+{
+    cargo::ipc::epoll::EventPoll poll;
+    SignalFD s(poll);
+    s.setHandler(SIGUSR1, [](struct ::signalfd_siginfo&) {});
+    s.setHandler(SIGINT, [](struct ::signalfd_siginfo&) {});
+
+    ::raise(SIGINT);
+    std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT));
+    BOOST_REQUIRE(!isAsyncHandlerCalled());
+}
+
+BOOST_AUTO_TEST_CASE(SignalHandler)
+{
+    cargo::ipc::epoll::EventPoll poll;
+    SignalFD s(poll);
+
+    bool isSignalCalled = false;
+    s.setHandler(SIGINT, [&](struct ::signalfd_siginfo&) {
+        isSignalCalled = true;
+    });
+
+    ::raise(SIGINT);
+    poll.dispatchIteration(TIMEOUT);
+    BOOST_REQUIRE(isSignalCalled);
+    BOOST_REQUIRE(!isAsyncHandlerCalled());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-text.cpp b/tests/unit_tests/utils/ut-text.cpp
new file mode 100644 (file)
index 0000000..bbfd349
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Krzysztof Dynowski (k.dynowski@samsumg.com)
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Krzysztof Dynowski (k.dynowski@samsung.com)
+ * @brief   Unit tests of text helpers
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/text.hpp"
+
+BOOST_AUTO_TEST_SUITE(TextUtilsSuite)
+
+BOOST_AUTO_TEST_CASE(SplitText)
+{
+    std::vector<std::string> v;
+    v = utils::split("", ",");
+    BOOST_CHECK(v.size() == 0);
+    v = utils::split("a", ",");
+    BOOST_CHECK(v.size() == 1);
+    v = utils::split(",", ",");
+    BOOST_CHECK(v.size() == 2);
+    v = utils::split("1,2", ",");
+    BOOST_CHECK(v.size() == 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-value-latch.cpp b/tests/unit_tests/utils/ut-value-latch.cpp
new file mode 100644 (file)
index 0000000..c3d81c5
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Unit tests of ValueLatch interface
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/value-latch.hpp"
+
+#include <thread>
+#include <string>
+
+BOOST_AUTO_TEST_SUITE(ValueLatchSuite)
+
+using namespace utils;
+
+namespace
+{
+    const int TIMEOUT = 1000; // ms
+    const int EXPECTED_TIMEOUT = 200; // ms
+    const std::string TEST_STRING = "some_random text";
+
+    struct ComplexType
+    {
+        float value;
+        std::string str;
+    };
+
+    struct ComplexMovableType
+    {
+        explicit ComplexMovableType(const ComplexType& val)
+            : value(val) {};
+        ComplexMovableType(const ComplexMovableType&) = delete;
+        ComplexMovableType(ComplexMovableType&&) = default;
+
+        ComplexType value;
+    };
+} // namespace
+
+BOOST_AUTO_TEST_CASE(SimpleValue)
+{
+    ValueLatch<int> testLatch;
+
+    std::thread testThread([&testLatch]() {
+        testLatch.set(3);
+    });
+
+    testThread.join();
+
+    BOOST_REQUIRE_EQUAL(testLatch.get(TIMEOUT), 3);
+}
+
+BOOST_AUTO_TEST_CASE(ComplexValue)
+{
+    ValueLatch<ComplexType> testLatch;
+
+    std::thread testThread([&testLatch]() {
+        testLatch.set({ 2.5f, TEST_STRING });
+    });
+
+    testThread.join();
+
+    ComplexType test(testLatch.get(TIMEOUT));
+    BOOST_REQUIRE_EQUAL(test.value, 2.5f);
+    BOOST_REQUIRE_EQUAL(test.str, TEST_STRING);
+}
+
+BOOST_AUTO_TEST_CASE(ComplexMovableValue)
+{
+    ValueLatch<ComplexMovableType> testLatch;
+
+    std::thread testThread([&testLatch]() {
+        testLatch.set( ComplexMovableType({ 2.5f, TEST_STRING }) );
+    });
+
+    testThread.join();
+
+    ComplexMovableType test(testLatch.get(TIMEOUT));
+    BOOST_REQUIRE_EQUAL(test.value.value, 2.5f);
+    BOOST_REQUIRE_EQUAL(test.value.str, TEST_STRING);
+}
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+    ValueLatch<int> testLatch;
+
+    BOOST_REQUIRE_EXCEPTION(testLatch.get(EXPECTED_TIMEOUT),
+                            UtilsException,
+                            WhatEquals("Timeout occured"));
+}
+
+BOOST_AUTO_TEST_CASE(MultipleSet)
+{
+    ValueLatch<int> testLatch;
+
+    testLatch.set(3);
+    BOOST_REQUIRE_EXCEPTION(testLatch.set(2),
+                            UtilsException,
+                            WhatEquals("Cannot set value multiple times"));
+}
+
+BOOST_AUTO_TEST_CASE(MultipleGet)
+{
+    ValueLatch<int> testLatch;
+
+    testLatch.set(3);
+    testLatch.get(TIMEOUT);
+    BOOST_REQUIRE_EXCEPTION(testLatch.get(EXPECTED_TIMEOUT),
+                            UtilsException,
+                            WhatEquals("Timeout occured"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit_tests/utils/ut-worker.cpp b/tests/unit_tests/utils/ut-worker.cpp
new file mode 100644 (file)
index 0000000..67a4ef8
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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
+ */
+
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Unit tests of worker thread
+ */
+
+#include "config.hpp"
+
+#include "ut.hpp"
+
+#include "utils/worker.hpp"
+#include "utils/latch.hpp"
+
+#include <chrono>
+#include <thread>
+#include <atomic>
+
+using namespace utils;
+
+BOOST_AUTO_TEST_SUITE(WorkerSuite)
+
+const int unsigned TIMEOUT = 1000;
+
+BOOST_AUTO_TEST_CASE(NoTasks)
+{
+    Worker::Pointer worker = Worker::create();
+}
+
+BOOST_AUTO_TEST_CASE(NoTasksWithSubWorkers)
+{
+    Worker::Pointer worker = Worker::create();
+    Worker::Pointer sub1 = worker->createSubWorker();
+    Worker::Pointer sub2 = worker->createSubWorker();
+    Worker::Pointer sub3 = sub1->createSubWorker();
+
+    sub1.reset();
+    worker.reset();
+}
+
+BOOST_AUTO_TEST_CASE(Simple)
+{
+    Latch done;
+
+    Worker::Pointer worker = Worker::create();
+    worker->addTask([&] {
+        done.set();
+    });
+
+    BOOST_CHECK(done.wait(TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(Queue)
+{
+    std::mutex mutex;
+    std::string result;
+
+    Worker::Pointer worker = Worker::create();
+
+    for (int n=0; n<10; ++n) {
+        worker->addTask([&, n]{
+            std::lock_guard<std::mutex> lock(mutex);
+            result += std::to_string(n);
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        });
+    }
+
+    worker.reset();
+
+    std::lock_guard<std::mutex> lock(mutex);
+    BOOST_CHECK_EQUAL("0123456789", result);
+}
+
+BOOST_AUTO_TEST_CASE(ThreadResume)
+{
+    Latch done;
+
+    const auto task = [&] {
+        done.set();
+    };
+
+    Worker::Pointer worker = Worker::create();
+
+    worker->addTask(task);
+
+    BOOST_CHECK(done.wait(TIMEOUT));
+
+    // make sure worker thread is in waiting state
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+    worker->addTask(task);
+
+    worker.reset();
+
+    BOOST_CHECK(done.wait(TIMEOUT));
+}
+
+BOOST_AUTO_TEST_CASE(SubWorker)
+{
+    std::mutex mutex;
+    std::string result;
+
+    Worker::Pointer worker = Worker::create();
+    Worker::Pointer sub1 = worker->createSubWorker();
+    Worker::Pointer sub2 = worker->createSubWorker();
+
+    auto addTask = [&](Worker::Pointer w, const std::string& id) {
+        w->addTask([&, id]{
+            std::lock_guard<std::mutex> lock(mutex);
+            result += id;
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        });
+    };
+
+    for (int n=0; n<4; ++n) {
+        addTask(worker, "_w" + std::to_string(n));
+        addTask(sub1, "_a" + std::to_string(n));
+    }
+
+    worker.reset();
+    sub1.reset();
+
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        BOOST_CHECK_EQUAL("_w0_a0_w1_a1_w2_a2_w3_a3", result);
+        result.clear();
+    }
+
+    addTask(sub2, "_b0");
+    addTask(sub2, "_b1");
+
+    sub2.reset();
+
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        BOOST_CHECK_EQUAL("_b0_b1", result);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(NoCopy)
+{
+    typedef std::atomic_int Counter;
+
+    struct Task {
+        Counter& count;
+
+        Task(Counter& c) : count(c) {}
+        Task(const Task& t) : count(t.count) {++count;}
+        Task(Task&& r) : count(r.count) {}
+        Task& operator=(const Task&) = delete;
+        Task& operator=(Task&&) = delete;
+        void operator() () const {}
+
+    };
+
+    Counter copyCount(0);
+
+    Worker::Pointer worker = Worker::create();
+    worker->addTask(Task(copyCount));
+    worker.reset();
+
+    BOOST_CHECK_EQUAL(1, copyCount); // one copy for creating std::function
+}
+
+BOOST_AUTO_TEST_SUITE_END()