--- /dev/null
+# 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
--- /dev/null
+#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})
--- /dev/null
+
+ 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.
--- /dev/null
+# 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)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+* 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
+
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+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
--- /dev/null
+/* 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;
+ }
+}
+
--- /dev/null
+# 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
--- /dev/null
+<!-- 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  <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>
--- /dev/null
+#!/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
--- /dev/null
+<!-- 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 -->
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+# 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)
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+# 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@
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+* 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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)
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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@
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
+/*@}*/
--- /dev/null
+%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
--- /dev/null
+#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)
--- /dev/null
+#!/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
--- /dev/null
+// 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
--- /dev/null
+# 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})
--- /dev/null
+#!/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:])
--- /dev/null
+#!/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()
--- /dev/null
+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
--- /dev/null
+# 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)
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+# 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)
--- /dev/null
+[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
--- /dev/null
+[Socket]
+ListenStream=/run/cargo-socket-test.socket
+SocketMode=0755
+SmackLabelIPIn=*
+SmackLabelIPOut=@
+Service=cargo-socket-test.service
+
+[Install]
+WantedBy=sockets.target
--- /dev/null
+File content
+Line 1
+Line 2
--- /dev/null
+/*
+ * 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()
+
--- /dev/null
+/*
+ * 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()
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()
--- /dev/null
+/*
+ * 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()