From f3330a0e728c9421629d082121f3e3123550e2a9 Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Mon, 13 Oct 2014 14:49:29 +0200 Subject: [PATCH 01/16] Adjust configuration files for Tizen:Common profile [Feature] Configuration files modified for Tizen:Common profile. [Cause] Tizen:Common devices use different configuration than M0 devices. [Solution] Change configuration files. [Verification] Build, install on Tizen:Common device, run tests. Change-Id: Ib1db585f99ba4e8feecd1defa76de277e3fead0b --- packaging/security-containers.spec | 2 +- server/configs/daemon.conf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/security-containers.spec b/packaging/security-containers.spec index fc1c806..3905904 100644 --- a/packaging/security-containers.spec +++ b/packaging/security-containers.spec @@ -4,7 +4,7 @@ %define libvirt_group libvirt # The group that has read and write access to /dev/input/event* devices. # It may vary between platforms. -%define input_event_group video +%define input_event_group input # The group has access to /dev/loop* devices. %define disk_group disk # The group that has write access to /dev/tty* devices. diff --git a/server/configs/daemon.conf b/server/configs/daemon.conf index db85284..dbfac95 100644 --- a/server/configs/daemon.conf +++ b/server/configs/daemon.conf @@ -8,9 +8,9 @@ "foregroundId" : "private", "defaultId" : "private", "inputConfig" : {"enabled" : true, - "device" : "gpio-keys", + "device" : "gpio_keys.6", "code" : 139, - "numberOfEvents" : 3, + "numberOfEvents" : 1, "timeWindowMs" : 500}, "proxyCallRules" : [] } -- 2.7.4 From e243eeb1fb3358b321dce9903865257ddaed8e23 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Thu, 9 Oct 2014 19:10:11 +0200 Subject: [PATCH 02/16] Command line interface to SCS [Feature] Command line interface to SCS [Cause] Need to manage SCS from shell [Solution] Binary that use libsecurity-containers [Verification] Build, install, execute security-containers-cli (switch container), check SCS logs. Change-Id: Ia6cc1cc00295e19befd2e0987900b69e2d4e7bd3 --- CMakeLists.txt | 2 + cli/CMakeLists.txt | 37 ++++++++++++ cli/command-line-interface.cpp | 115 +++++++++++++++++++++++++++++++++++++ cli/command-line-interface.hpp | 109 +++++++++++++++++++++++++++++++++++ cli/main.cpp | 74 ++++++++++++++++++++++++ doc/doxygen.cfg | 2 +- packaging/security-containers.spec | 14 +++++ 7 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 cli/CMakeLists.txt create mode 100644 cli/command-line-interface.cpp create mode 100644 cli/command-line-interface.hpp create mode 100644 cli/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c8e74c6..f824f8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ SET(CONTAINER_SUPPORT_FOLDER ${PROJECT_SOURCE_DIR}/container-support) SET(CONTAINER_DAEMON_FOLDER ${PROJECT_SOURCE_DIR}/container-daemon) SET(TESTS_FOLDER ${PROJECT_SOURCE_DIR}/tests) SET(UNIT_TESTS_FOLDER ${TESTS_FOLDER}/unit_tests) +SET(CLI_FOLDER ${PROJECT_SOURCE_DIR}/cli) IF(NOT DEFINED SYSCONF_INSTALL_DIR) SET(SYSCONF_INSTALL_DIR "/etc") @@ -132,4 +133,5 @@ ADD_SUBDIRECTORY(${SERVER_FOLDER}) ADD_SUBDIRECTORY(${CONTAINER_SUPPORT_FOLDER}) ADD_SUBDIRECTORY(${CONTAINER_DAEMON_FOLDER}) ADD_SUBDIRECTORY(${TESTS_FOLDER}) +ADD_SUBDIRECTORY(${CLI_FOLDER}) diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000..076dd94 --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,37 @@ +# 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 Mateusz Malicki (m.malicki2@samsung.com) +# + +MESSAGE(STATUS "Generating makefile for the command line interface...") +FILE(GLOB cli_SRCS *.cpp *.hpp) + +## Setup target ################################################################ +SET(CLI_CODENAME "${PROJECT_NAME}-cli") +ADD_EXECUTABLE(${CLI_CODENAME} ${cli_SRCS}) + + +## Link libraries ############################################################## +PKG_CHECK_MODULES(LIB_DEPS REQUIRED security-containers) + +INCLUDE_DIRECTORIES(${CLIENT_FOLDER}) +INCLUDE_DIRECTORIES(${COMMON_FOLDER}) +TARGET_LINK_LIBRARIES(${CLI_CODENAME} ${LIB_DEPS_LIBRARIES} ${PROJECT_NAME}) + + +## Install ##################################################################### +INSTALL(TARGETS ${CLI_CODENAME} DESTINATION bin) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp new file mode 100644 index 0000000..02c867f --- /dev/null +++ b/cli/command-line-interface.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR 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 Definition of CommandLineInterface class + */ + +#include "config.hpp" +#include "command-line-interface.hpp" +#include + +#include +#include +#include +#include + +using namespace std; + +namespace security_containers { +namespace cli { + +namespace { + +/** + * Invoke specific function on ScClient + * + * @param fun Function to be called. It must not throw any exception. + */ +void one_shot(const function& fun) +{ + string msg; + ScStatus status; + ScClient client; + + status = sc_start_glib_loop(); + if (SCCLIENT_SUCCESS != status) { + throw runtime_error("Can't start glib loop"); + } + + client = sc_client_create(); + if (NULL == client) { + msg = "Can't create client"; + goto finish; + } + + status = sc_connect(client); + if (SCCLIENT_SUCCESS != status) { + msg = sc_get_status_message(client); + goto finish; + } + + status = fun(client); + if (SCCLIENT_SUCCESS != status) { + msg = sc_get_status_message(client); + goto finish; + } + +finish: + sc_client_free(client); + sc_stop_glib_loop(); + if (! msg.empty()) { + throw runtime_error(msg); + } +} + +} // namespace + +void CommandLineInterface::printUsage(std::ostream& out) const +{ + out << mUsage << "\n\n" + << "\tDescription\n" + << "\t\t" << mUsageInfo << "\n\n" + << "\tOptions\n"; + for (const auto& args : mArgsSpec) { + out << "\t\t" << args.first << " -- " << args.second << "\n"; + } + out << "\n"; +} + +void CommandLineInterface::execute(int pos, int argc, const char** argv) +{ + mExecutorCallback(pos, argc, argv); +} + + +void set_active_container(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(sc_set_active_container, _1, argv[pos + 1])); +} + +} // namespace cli +} // namespace security_containers diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp new file mode 100644 index 0000000..c193068 --- /dev/null +++ b/cli/command-line-interface.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR 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 Declaration of CommandLineInterface class + */ +#ifndef CLI_COMMAND_LINE_INTERFACE_HPP +#define CLI_COMMAND_LINE_INTERFACE_HPP + +#include +#include +#include +#include + +namespace security_containers { +namespace cli { + +/** + * Class that implements command pattern. + */ +class CommandLineInterface { + +public: + /** + * @see CommandLineInterface::execute + */ + typedef std::function ExecutorCallback; + + /** + * @see CommandLineInterface::CommandLineInterface + */ + typedef std::map ArgsSpec; + + /** + * Dummy constructor (for stl usage) + */ + CommandLineInterface() {} + + /** + * Construct command + * + * @param executorCallback Callback function that will do the job + * @param usage Description of use + * @param usageInfo Description of the command + * @param argsSpec Description of arguments + */ + CommandLineInterface(const ExecutorCallback& executorCallback, + const std::string& usage, + const std::string& usageInfo, + const ArgsSpec& argsSpec) + : mExecutorCallback(executorCallback), + mUsage(usage), + mUsageInfo(usageInfo), + mArgsSpec(argsSpec) {} + + /** + * Print usage to stream + * + * @param out Output stream + */ + void printUsage(std::ostream& out) const; + + /** + * Do the work + * + * It calls the callback passed in constructor + * + * @param pos Points to element in argv where command was recognized (i.e. command name) + * @param argc Number of elements in argv + * @param argv Command line arguments + */ + void execute(int pos, int argc, const char** argv); + + +private: + const ExecutorCallback mExecutorCallback; + const std::string mUsage; + const std::string mUsageInfo; + const ArgsSpec mArgsSpec; +}; + +/** + * Parses command line arguments and call sc_set_active_container + * + * @see sc_set_active_container + */ +void set_active_container(int pos, int argc, const char** argv); + +} // namespace cli +} // namespace security_containers + +#endif /* CLI_COMMAND_LINE_INTERFACE_HPP */ diff --git a/cli/main.cpp b/cli/main.cpp new file mode 100644 index 0000000..e74f620 --- /dev/null +++ b/cli/main.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR 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 Declaration of CommandLineInterface class + */ + +#include "command-line-interface.hpp" + +#include +#include +#include +#include +#include + +using namespace security_containers::cli; + +std::map commands = { + {"set_active_container", { + set_active_container, + "set_active_container container_id", + "Set active (foreground) container", + {{"container_id", "id container name"}}} + } +}; + +void printUsage(std::ostream& out, const std::string& name) +{ + out << "Usage: " << name << " [command [args]]\n\n" + << "command can be one of the following:\n"; + + for (const auto& command : commands) { + command.second.printUsage(out); + } +} + +int main(const int argc, const char** argv) +{ + if (argc < 2) { + printUsage(std::cout, argv[0]); + return EXIT_FAILURE; + } + if (commands.count(argv[1]) == 0) { + printUsage(std::cout, argv[0]); + return EXIT_FAILURE; + } + + CommandLineInterface& command = commands[argv[1]]; + try { + command.execute(1, argc, argv); + } catch (const std::runtime_error& ex) { + std::cerr << ex.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg index 7e304e5..e997623 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg @@ -647,7 +647,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../common ../client ../server +INPUT = ../common ../client ../server ../cli # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/packaging/security-containers.spec b/packaging/security-containers.spec index 3905904..137f02b 100644 --- a/packaging/security-containers.spec +++ b/packaging/security-containers.spec @@ -186,6 +186,20 @@ Daemon running inside every container. /etc/dbus-1/system.d/org.tizen.containers.domain.daemon.conf +## Command Line Interface ###################################################### +%package cli +Summary: Security Containers Command Line Interface +Group: Security/Other +Requires: security-containers-client = %{version}-%{release} + +%description cli +Command Line Interface for security-containers. + +%files cli +%defattr(644,root,root,755) +%attr(755,root,root) %{_bindir}/security-containers-cli + + ## Test Package ################################################################ %package tests Summary: Security Containers Tests -- 2.7.4 From 2a642703696fa9d3390c18ccd58325cae56e62fb Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 21 Oct 2014 15:32:06 +0200 Subject: [PATCH 03/16] Fix missing package dependency [Bug/Feature] Test package require python-xml for working. [Cause] N/A [Solution] N/A [Verification] Build, install, run tests. Change-Id: I08774f20115dfd4d4ab18a65dd4ac395fb6322ce --- packaging/security-containers.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/security-containers.spec b/packaging/security-containers.spec index 137f02b..00a05a6 100644 --- a/packaging/security-containers.spec +++ b/packaging/security-containers.spec @@ -207,6 +207,7 @@ Group: Development/Libraries Requires: security-containers = %{version}-%{release} Requires: security-containers-client = %{version}-%{release} Requires: python +Requires: python-xml Requires: boost-test %description tests -- 2.7.4 From 0533ba4677d802d3821e04eb6756d76180994843 Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Tue, 14 Oct 2014 10:00:49 +0200 Subject: [PATCH 04/16] Fix issue with permissions when copying dir contents [Bug] Permission denied error when trying to copy read-only directories recursively. [Cause] boost::filesystem::copy applied permissions immediately, which in some cases caused error when trying to write something inside processed directory. [Solution] Instead of using boost::filesystem::copy on directories, split action into three sub-actions: * Create new directory with boost::filesystem::create_directory * Call copyDirContentsRec() to copy contents of processed directory * Apply source directory permissions and ownership [Verification] Build, install, run tests. Change-Id: Ifdec110a595dcecd113abf4065dd1cdc03f2d3cb --- common/utils/fs.cpp | 52 +++++++++++++++++++++++++++------------- server/server.cpp | 5 +++- tests/unit_tests/utils/ut-fs.cpp | 35 ++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/common/utils/fs.cpp b/common/utils/fs.cpp index a03c139..8a6441c 100644 --- a/common/utils/fs.cpp +++ b/common/utils/fs.cpp @@ -208,38 +208,56 @@ bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesys namespace fs = boost::filesystem; // TODO: Right now this function skips files which produce error when copying. Errors show up - // when: - // a) fs::directory_iterator file(src) is created - // b) fs::copy(...) is called - // In both cases lack of permissions is the issue. + // when fs::directory_iterator file(src) is created - lack of permissions is the issue. // - // In a) case we can't do much - SCS won't be able to read the directory and its contents. Such - // directories are not common in the filesystem, so they *probably* can be skipped. - // - // In b) case multiple directories have too strict permissions to be directly copied. This - // is a problem for some files crucial to container launch (ex. we cannot copy - // /usr/lib/systemd/systemd because /usr/lib has 555 permissions). - // To fix b) issue, copying must be done in two steps: - // 1. Copy file contents without permissions (this probably can be achieved by opening two - // files in-code with fstream and programatically copying data from one file to another). - // 2. Apply all available file attributes from source (permissions, owner UID/GID, xattrs...) + // To fix lack of permissions, copying must be done as root. The easiest way would be to launch + // copyDirContents after fork() and setuid(0). 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; - fs::copy(current, dst / current.filename(), ec); - if(ec.value() != boost::system::errc::success) { + + 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, dst / current.filename())) { + 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() << ": " << strerror(errno)); + } + } else { + if (::chown(destination.string().c_str(), info.st_uid, info.st_gid) < 0) { + LOGW("Failed to change owner of file " << destination.string() << ": " << strerror(errno)); + } } } } catch (fs::filesystem_error& e) { diff --git a/server/server.cpp b/server/server.cpp index a9ad444..7b03f4c 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -202,9 +202,12 @@ bool Server::prepareEnvironment(const std::string& configPath, bool runAsRoot) // is introduced. The capability is needed to allow modify SMACK labels of // "/var/run/containers//run" mount point. // CAP_SYS_TTY_CONFIG is needed to activate virtual terminals through ioctl calls + // CAP_CHOWN is needed when creating new container from image to set owner/group for each file, + // directory or symlink return (runAsRoot || utils::dropRoot(uid, gid, {CAP_SYS_ADMIN, CAP_MAC_OVERRIDE, - CAP_SYS_TTY_CONFIG})); + CAP_SYS_TTY_CONFIG, + CAP_CHOWN})); } diff --git a/tests/unit_tests/utils/ut-fs.cpp b/tests/unit_tests/utils/ut-fs.cpp index 43be58a..a1eea40 100644 --- a/tests/unit_tests/utils/ut-fs.cpp +++ b/tests/unit_tests/utils/ut-fs.cpp @@ -64,6 +64,8 @@ const std::string FILE_DIR_RANDOM_2 = boost::filesystem::unique_path("testDir-%%%%").string(); const std::string FILE_DIR_RANDOM_3 = boost::filesystem::unique_path("testDir-%%%%").string(); +const std::string FILE_DIR_RANDOM_4 = + boost::filesystem::unique_path("testDir-%%%%").string(); const std::string FILE_NAME_RANDOM_1 = boost::filesystem::unique_path("testFile-%%%%").string(); const std::string FILE_NAME_RANDOM_2 = @@ -146,25 +148,47 @@ BOOST_AUTO_TEST_CASE(MoveFileTest) BOOST_AUTO_TEST_CASE(CopyDirContentsTest) { namespace fs = boost::filesystem; - std::string src, src_inner, dst, dst_inner; + std::string src, src_inner, src_inner2, dst, dst_inner, dst_inner2; boost::system::error_code ec; src = TMP_PATH + "/" + FILE_DIR_RANDOM_1; src_inner = src + "/" + FILE_DIR_RANDOM_3; + src_inner2 = src + "/" + FILE_DIR_RANDOM_4; dst = TMP_PATH + "/" + FILE_DIR_RANDOM_2; dst_inner = dst + "/" + FILE_DIR_RANDOM_3; + dst_inner2 = dst + "/" + FILE_DIR_RANDOM_4; + + // template dir structure: + // |-src + // |-FILE_NAME_RANDOM_1 + // |-FILE_NAME_RANDOM_2 + // |-src_inner (rw directory) + // | |-FILE_NAME_RANDOM_1 + // | + // |-src_inner2 (ro directory) + // |-FILE_NAME_RANDOM_1 + // |-FILE_NAME_RANDOM_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_RANDOM_1, FILE_CONTENT)); BOOST_REQUIRE(saveFileContent(src + "/" + FILE_NAME_RANDOM_2, FILE_CONTENT_2)); BOOST_REQUIRE(saveFileContent(src_inner + "/" + FILE_NAME_RANDOM_1, FILE_CONTENT_3)); + BOOST_REQUIRE(saveFileContent(src_inner2 + "/" + FILE_NAME_RANDOM_1, FILE_CONTENT_3)); + BOOST_REQUIRE(saveFileContent(src_inner2 + "/" + FILE_NAME_RANDOM_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); @@ -176,10 +200,19 @@ BOOST_AUTO_TEST_CASE(CopyDirContentsTest) BOOST_CHECK(fs::exists(dst + "/" + FILE_NAME_RANDOM_2)); BOOST_CHECK(fs::exists(dst_inner)); BOOST_CHECK(fs::exists(dst_inner + "/" + FILE_NAME_RANDOM_1)); + BOOST_CHECK(fs::exists(dst_inner2)); + BOOST_CHECK(fs::exists(dst_inner2 + "/" + FILE_NAME_RANDOM_1)); + BOOST_CHECK(fs::exists(dst_inner2 + "/" + FILE_NAME_RANDOM_2)); BOOST_CHECK_EQUAL(readFileContent(dst + "/" + FILE_NAME_RANDOM_1), FILE_CONTENT); BOOST_CHECK_EQUAL(readFileContent(dst + "/" + FILE_NAME_RANDOM_2), FILE_CONTENT_2); BOOST_CHECK_EQUAL(readFileContent(dst_inner + "/" + FILE_NAME_RANDOM_1), FILE_CONTENT_3); + BOOST_CHECK_EQUAL(readFileContent(dst_inner2 + "/" + FILE_NAME_RANDOM_1), FILE_CONTENT_3); + BOOST_CHECK_EQUAL(readFileContent(dst_inner2 + "/" + FILE_NAME_RANDOM_2), FILE_CONTENT_2); + + fs::file_status st; + BOOST_REQUIRE_NO_THROW(st = fs::status(fs::path(dst_inner2))); + BOOST_CHECK(fs::owner_read == st.permissions()); } BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 46bf0c683aad2818f119c275042d39e8d7dcb19b Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Thu, 16 Oct 2014 14:44:52 +0200 Subject: [PATCH 05/16] Add launchAsRoot and use it when adding new container [Feature] Function launchAsRoot. [Cause] Some functions need to be launched as root. [Solution] Add launchAsRoot which forks, sets UID to 0 and then calls a function. [Verification] Build, install, run tests. Add new container - no copying errors should occur. Change-Id: Iaf917108ea4c7c699d9f2d69c8100430daa4f9c4 --- common/utils/environment.cpp | 39 +++++++++++++++++++++++++++++++++++++++ common/utils/environment.hpp | 8 ++++++++ common/utils/fs.cpp | 6 ------ server/containers-manager.cpp | 13 +++++++++---- server/server.cpp | 4 +++- 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/common/utils/environment.cpp b/common/utils/environment.cpp index 70ef27c..eaaef12 100644 --- a/common/utils/environment.cpp +++ b/common/utils/environment.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include @@ -82,6 +84,43 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector& caps) return true; } +bool launchAsRoot(const std::function& func) +{ + pid_t pid = fork(); + if (pid < 0) { + LOGE("Fork failed: " << strerror(errno)); + return false; + } + + if (pid == 0) { + if (::setuid(0) < 0) { + LOGW("Failed to become root: " << strerror(errno)); + ::exit(EXIT_FAILURE); + } + + try { + func(); + } catch (std::exception& e) { + LOGE("Failed to successfully execute func: " << e.what()); + ::exit(EXIT_FAILURE); + } + + ::exit(EXIT_SUCCESS); + } + + int result; + if (::waitpid(pid, &result, 0) < 0) { + LOGE("waitpid failed: " << strerror(errno)); + return false; + } + if (result != 0) { + LOGE("Function launched as root failed with result " << result); + return false; + } + + return true; +} + } // namespace utils } // namespace security_containers diff --git a/common/utils/environment.hpp b/common/utils/environment.hpp index b62189b..120b6ac 100644 --- a/common/utils/environment.hpp +++ b/common/utils/environment.hpp @@ -27,6 +27,7 @@ #include #include +#include #include @@ -44,6 +45,13 @@ bool setSuppGroups(const std::vector& groups); */ bool dropRoot(uid_t uid, gid_t gid, const std::vector& caps); +/** + * Launch func as root user. + * + * This function forks, sets UID 0 to child process and calls func. + */ +bool launchAsRoot(const std::function& func); + } // namespace utils } // namespace security_containers diff --git a/common/utils/fs.cpp b/common/utils/fs.cpp index 8a6441c..660db9f 100644 --- a/common/utils/fs.cpp +++ b/common/utils/fs.cpp @@ -207,12 +207,6 @@ bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesys { namespace fs = boost::filesystem; - // TODO: Right now this function skips files which produce error when copying. Errors show up - // when fs::directory_iterator file(src) is created - lack of permissions is the issue. - // - // To fix lack of permissions, copying must be done as root. The easiest way would be to launch - // copyDirContents after fork() and setuid(0). - try { for (fs::directory_iterator file(src); file != fs::directory_iterator(); diff --git a/server/containers-manager.cpp b/server/containers-manager.cpp index c9c7ff8..82be28d 100644 --- a/server/containers-manager.cpp +++ b/server/containers-manager.cpp @@ -37,6 +37,7 @@ #include "dbus/exception.hpp" #include "utils/fs.hpp" #include "utils/img.hpp" +#include "utils/environment.hpp" #include #include @@ -591,7 +592,11 @@ void ContainersManager::handleAddContainerCall(const std::string& id, // copy container image if config contains path to image LOGT("image path: " << mConfig.containerImagePath); if (!mConfig.containerImagePath.empty()) { - if (!utils::copyImageContents(mConfig.containerImagePath, containerPathStr)) { + auto copyImageContentsWrapper = std::bind(&utils::copyImageContents, + mConfig.containerImagePath, + containerPathStr); + + if (!utils::launchAsRoot(copyImageContentsWrapper)) { LOGE("Failed to copy container image."); result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, "Failed to copy container image."); @@ -636,7 +641,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, generateNewConfig(id, libvirtNetworkFilterPath, newLibvirtNetworkFilterPath); } catch (SecurityContainersException& e) { LOGE(e.what()); - removeAllWrapper(containerPathStr); + utils::launchAsRoot(std::bind(removeAllWrapper, containerPathStr)); result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what()); return; } @@ -646,7 +651,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, addContainer(newConfigPath); } catch (SecurityContainersException& e) { LOGE(e.what()); - removeAllWrapper(containerPathStr); + utils::launchAsRoot(std::bind(removeAllWrapper, containerPathStr)); result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what()); return; } @@ -656,7 +661,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, result->setVoid(); } else { LOGE("Failed to start container."); - removeAllWrapper(containerPathStr); + utils::launchAsRoot(std::bind(removeAllWrapper, containerPathStr)); result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, "Failed to start container."); } diff --git a/server/server.cpp b/server/server.cpp index 7b03f4c..f477f0a 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -204,10 +204,12 @@ bool Server::prepareEnvironment(const std::string& configPath, bool runAsRoot) // CAP_SYS_TTY_CONFIG is needed to activate virtual terminals through ioctl calls // CAP_CHOWN is needed when creating new container from image to set owner/group for each file, // directory or symlink + // CAP_SETUID is needed to launch specific funtions as root (see environment.cpp) return (runAsRoot || utils::dropRoot(uid, gid, {CAP_SYS_ADMIN, CAP_MAC_OVERRIDE, CAP_SYS_TTY_CONFIG, - CAP_CHOWN})); + CAP_CHOWN, + CAP_SETUID})); } -- 2.7.4 From e34356ee58cea135d0396e4b1218f11a44dc0216 Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Mon, 20 Oct 2014 14:00:32 +0200 Subject: [PATCH 06/16] Improvements to SCS API [Bug] * removeAllWrapper caught wrong type of exception * AddContainer did not focus container after adding * getActiveContainerId segfaulted when no container was present [Cause] N/A [Solution] N/A [Verification] Build, install, run tests. Change-Id: I6bc665952c0f0c515c3aa548bdd6165b2ee7d55b --- server/containers-manager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/containers-manager.cpp b/server/containers-manager.cpp index 82be28d..a707fe2 100644 --- a/server/containers-manager.cpp +++ b/server/containers-manager.cpp @@ -490,7 +490,7 @@ void ContainersManager::handleGetContainerIdsCall(dbus::MethodResultBuilder::Poi void ContainersManager::handleGetActiveContainerIdCall(dbus::MethodResultBuilder::Pointer result) { LOGI("GetActiveContainerId call"); - if (mContainers[mConfig.foregroundId]->isRunning()){ + if (!mConfig.foregroundId.empty() && mContainers[mConfig.foregroundId]->isRunning()){ result->set(g_variant_new("(s)", mConfig.foregroundId.c_str())); } else { result->set(g_variant_new("(s)", "")); @@ -622,7 +622,7 @@ void ContainersManager::handleAddContainerCall(const std::string& id, try { LOGD("Removing copied data"); fs::remove_all(fs::path(path)); - } catch(const boost::exception& e) { + } catch(const std::exception& e) { LOGW("Failed to remove data: " << boost::diagnostic_information(e)); } }; @@ -656,8 +656,9 @@ void ContainersManager::handleAddContainerCall(const std::string& id, return; } - auto resultCallback = [result, containerPathStr, removeAllWrapper](bool succeeded) { + auto resultCallback = [this, id, result, containerPathStr, removeAllWrapper](bool succeeded) { if (succeeded) { + focus(id); result->setVoid(); } else { LOGE("Failed to start container."); -- 2.7.4 From 068abbdef3718ccd0b142eab2439134975cdd16e Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Thu, 23 Oct 2014 13:31:30 +0200 Subject: [PATCH 07/16] Handle empty container name in AddContainer API [Bug] AddContainer API did not handle the case when container id was empty. [Cause] N/A [Solution] N/A [Verification] Build, install, try adding a container with empty string as name. Change-Id: I9340e56c58070c5b4c7aa1a0e4d7c2f5ea9c3aa6 --- server/containers-manager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/containers-manager.cpp b/server/containers-manager.cpp index a707fe2..d0d0a36 100644 --- a/server/containers-manager.cpp +++ b/server/containers-manager.cpp @@ -572,6 +572,13 @@ void ContainersManager::generateNewConfig(const std::string& id, void ContainersManager::handleAddContainerCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { + if (id.empty()) { + LOGE("Failed to add container - invalid name."); + result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, + "Failed to add container - invalid name."); + return; + } + LOGI("Adding container " << id); // TODO: This solution is temporary. It utilizes direct access to config files when creating new -- 2.7.4 From 17ebae30eab7530460afee437b56c8ef59e8e0b5 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Mon, 20 Oct 2014 09:34:33 +0200 Subject: [PATCH 08/16] Added sc_add_container to security-container's client [Feature] Ability to add container through SCS client [Cause] Cli need this [Solution] Add sc_add_container client function [Verification] Build, install, run Client/AddContainerTest test Change-Id: Ie0179cb02bdf1946fb9f8d2fd2f3c303cda401fe --- client/security-containers-client-impl.cpp | 8 ++++++++ client/security-containers-client-impl.hpp | 5 +++++ client/security-containers-client.cpp | 5 +++++ client/security-containers-client.h | 9 +++++++++ tests/unit_tests/client/ut-client.cpp | 12 ++++++++++++ 5 files changed, 39 insertions(+) diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index a2c51ba..95418b9 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -353,6 +353,14 @@ ScStatus Client::sc_set_active_container(const char* id) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_SET_ACTIVE_CONTAINER, args_in); } +ScStatus Client::sc_add_container(const char* id) noexcept +{ + assert(id); + + GVariant* args_in = g_variant_new("(s)", id); + return callMethod(HOST_INTERFACE, api::host::METHOD_ADD_CONTAINER, args_in); +} + ScStatus Client::sc_container_dbus_state(ScContainerDbusStateCallback containerDbusStateCallback, void* data) noexcept { diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index f1bfacb..3f5a556 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -127,6 +127,11 @@ public: ScStatus sc_set_active_container(const char* id) noexcept; /** + * @see ::sc_add_container + */ + ScStatus sc_add_container(const char* id) noexcept; + + /** * @see ::sc_container_dbus_state */ ScStatus sc_container_dbus_state(ScContainerDbusStateCallback containerDbusStateCallback, diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index cb22544..95c2758 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -131,6 +131,11 @@ API ScStatus sc_set_active_container(ScClient client, const char* id) return getClient(client).sc_set_active_container(id); } +API ScStatus sc_add_container(ScClient client, const char* id) +{ + return getClient(client).sc_add_container(id); +} + API ScStatus sc_container_dbus_state(ScClient client, ScContainerDbusStateCallback containerDbusStateCallback, void* data) diff --git a/client/security-containers-client.h b/client/security-containers-client.h index 4d37c3c..1307cb5 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -267,6 +267,15 @@ ScStatus sc_get_container_id_by_pid(ScClient client, int pid, ScString* id); ScStatus sc_set_active_container(ScClient client, const char* id); /** + * Create and add container + * + * @param[in] client security-containers-server's client + * @param[in] id container id + * @return status of this function call + */ +ScStatus sc_add_container(ScClient client, const char* id); + +/** * Register dbus state change callback function. * * @note The callback function will be invoked on a different thread. diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 574be39..5db6962 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -188,6 +188,18 @@ BOOST_AUTO_TEST_CASE(SetActiveContainerTest) sc_client_free(client); } +BOOST_AUTO_TEST_CASE(AddContainerTest) +{ + const std::string newActiveContainerId = ""; + + ScClient client = sc_client_create(); + ScStatus status = sc_connect(client); + BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + status = sc_add_container(client, newActiveContainerId.c_str()); + BOOST_REQUIRE_EQUAL(SCCLIENT_CUSTOM_ERROR, status); + sc_client_free(client); +} + BOOST_AUTO_TEST_CASE(FileMoveRequestTest) { const std::string path = "/tmp/fake_path"; -- 2.7.4 From 69db805a5ca63b93cb41a4773bdc894444ec52df Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Mon, 20 Oct 2014 10:14:42 +0200 Subject: [PATCH 09/16] Added add_container to cli [Feature] Ability to add container through cli [Cause] The need for the ability to add containers [Solution] Add add_container cli function [Verification] Build, install, run add container Change-Id: I020bddaa3707f0e84227a35a85d0905fbb81d6be --- cli/command-line-interface.cpp | 11 +++++++++++ cli/command-line-interface.hpp | 7 +++++++ cli/main.cpp | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 02c867f..a7b7aed 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -111,5 +111,16 @@ void set_active_container(int pos, int argc, const char** argv) one_shot(bind(sc_set_active_container, _1, argv[pos + 1])); } +void add_container(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(sc_add_container, _1, argv[pos + 1])); +} + } // namespace cli } // namespace security_containers diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index c193068..d9fc39f 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -103,6 +103,13 @@ private: */ void set_active_container(int pos, int argc, const char** argv); +/** + * Parses command line arguments and call sc_add_container + * + * @see sc_add_container + */ +void add_container(int pos, int argc, const char** argv); + } // namespace cli } // namespace security_containers diff --git a/cli/main.cpp b/cli/main.cpp index e74f620..d84d5ac 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -38,6 +38,12 @@ std::map commands = { "set_active_container container_id", "Set active (foreground) container", {{"container_id", "id container name"}}} + }, + {"add_container", { + add_container, + "add_container container_id", + "Create and add container", + {{"container_id", "id container name"}}} } }; -- 2.7.4 From 0ecba0804c632022b45655a3f168834327685a1d Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Thu, 6 Nov 2014 18:31:58 +0100 Subject: [PATCH 10/16] Rename cli, client library and client test functions [Feature] Brave New Name [Cause] New name of SCS [Solution] Rename: * sc_ -> vsm_ * Sc -> Vsm, * SC -> VSM, * container_dbus_state -> add_state_callback, * get_container_id_by_pid -> lookup_domain_by_pid, * get_container_ids -> get_domain_ids [Verification] Compile Change-Id: Icaffebaa5e7a0e9d5869a9d2ec701bf385e99529 --- cli/command-line-interface.cpp | 34 ++--- cli/command-line-interface.hpp | 16 +-- cli/main.cpp | 26 ++-- client/security-containers-client-impl.cpp | 161 ++++++++++++------------ client/security-containers-client-impl.hpp | 80 ++++++------ client/security-containers-client.cpp | 84 ++++++------- client/security-containers-client.h | 120 +++++++++--------- tests/unit_tests/client/ut-client.cpp | 194 +++++++++++++++-------------- 8 files changed, 366 insertions(+), 349 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index a7b7aed..8af17a7 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -39,42 +39,42 @@ namespace cli { namespace { /** - * Invoke specific function on ScClient + * Invoke specific function on VsmClient * * @param fun Function to be called. It must not throw any exception. */ -void one_shot(const function& fun) +void one_shot(const function& fun) { string msg; - ScStatus status; - ScClient client; + VsmStatus status; + VsmClient client; - status = sc_start_glib_loop(); - if (SCCLIENT_SUCCESS != status) { + status = vsm_start_glib_loop(); + if (VSMCLIENT_SUCCESS != status) { throw runtime_error("Can't start glib loop"); } - client = sc_client_create(); + client = vsm_client_create(); if (NULL == client) { msg = "Can't create client"; goto finish; } - status = sc_connect(client); - if (SCCLIENT_SUCCESS != status) { - msg = sc_get_status_message(client); + status = vsm_connect(client); + if (VSMCLIENT_SUCCESS != status) { + msg = vsm_get_status_message(client); goto finish; } status = fun(client); - if (SCCLIENT_SUCCESS != status) { - msg = sc_get_status_message(client); + if (VSMCLIENT_SUCCESS != status) { + msg = vsm_get_status_message(client); goto finish; } finish: - sc_client_free(client); - sc_stop_glib_loop(); + vsm_client_free(client); + vsm_stop_glib_loop(); if (! msg.empty()) { throw runtime_error(msg); } @@ -108,10 +108,10 @@ void set_active_container(int pos, int argc, const char** argv) throw runtime_error("Not enough parameters"); } - one_shot(bind(sc_set_active_container, _1, argv[pos + 1])); + one_shot(bind(vsm_set_active_container, _1, argv[pos + 1])); } -void add_container(int pos, int argc, const char** argv) +void create_domain(int pos, int argc, const char** argv) { using namespace std::placeholders; @@ -119,7 +119,7 @@ void add_container(int pos, int argc, const char** argv) throw runtime_error("Not enough parameters"); } - one_shot(bind(sc_add_container, _1, argv[pos + 1])); + one_shot(bind(vsm_create_domain, _1, argv[pos + 1])); } } // namespace cli diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index d9fc39f..27415c1 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -62,9 +62,9 @@ public: * @param argsSpec Description of arguments */ CommandLineInterface(const ExecutorCallback& executorCallback, - const std::string& usage, - const std::string& usageInfo, - const ArgsSpec& argsSpec) + const std::string& usage, + const std::string& usageInfo, + const ArgsSpec& argsSpec) : mExecutorCallback(executorCallback), mUsage(usage), mUsageInfo(usageInfo), @@ -97,18 +97,18 @@ private: }; /** - * Parses command line arguments and call sc_set_active_container + * Parses command line arguments and call vsm_set_active_container * - * @see sc_set_active_container + * @see vsm_set_active_container */ void set_active_container(int pos, int argc, const char** argv); /** - * Parses command line arguments and call sc_add_container + * Parses command line arguments and call vsm_create_domain * - * @see sc_add_container + * @see vsm_create_domain */ -void add_container(int pos, int argc, const char** argv); +void create_domain(int pos, int argc, const char** argv); } // namespace cli } // namespace security_containers diff --git a/cli/main.cpp b/cli/main.cpp index d84d5ac..135385d 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -33,17 +33,21 @@ using namespace security_containers::cli; std::map commands = { - {"set_active_container", { - set_active_container, - "set_active_container container_id", - "Set active (foreground) container", - {{"container_id", "id container name"}}} + { + "set_active_container", { + set_active_container, + "set_active_container container_id", + "Set active (foreground) container", + {{"container_id", "id container name"}} + } }, - {"add_container", { - add_container, - "add_container container_id", - "Create and add container", - {{"container_id", "id container name"}}} + { + "create_domain", { + create_domain, + "create_domain container_id", + "Create and add container", + {{"container_id", "id container name"}} + } } }; @@ -70,7 +74,7 @@ int main(const int argc, const char** argv) CommandLineInterface& command = commands[argv[1]]; try { - command.execute(1, argc, argv); + command.execute(1, argc, argv); } catch (const std::runtime_error& ex) { std::cerr << ex.what() << std::endl; return EXIT_FAILURE; diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index 95418b9..39abbfd 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -52,7 +52,7 @@ const DbusInterfaceInfo CONTAINER_INTERFACE(api::container::BUS_NAME, unique_ptr gGlibLoop; -void toDict(GVariant* in, ScArrayString* keys, ScArrayString* values) +void toDict(GVariant* in, VsmArrayString* keys, VsmArrayString* values) { assert(in); assert(keys); @@ -108,20 +108,20 @@ void toArray(GVariant* in, T** scArray) *scArray = ids; } -ScStatus toStatus(const std::exception& ex) +VsmStatus toStatus(const std::exception& ex) { if (typeid(DbusCustomException) == typeid(ex)) { - return SCCLIENT_CUSTOM_ERROR; + return VSMCLIENT_CUSTOM_ERROR; } else if (typeid(DbusIOException) == typeid(ex)) { - return SCCLIENT_IO_ERROR; + return VSMCLIENT_IO_ERROR; } else if (typeid(DbusOperationException) == typeid(ex)) { - return SCCLIENT_OPERATION_FAILED; + return VSMCLIENT_OPERATION_FAILED; } else if (typeid(DbusInvalidArgumentException) == typeid(ex)) { - return SCCLIENT_INVALID_ARGUMENT; + return VSMCLIENT_INVALID_ARGUMENT; } else if (typeid(DbusException) == typeid(ex)) { - return SCCLIENT_OTHER_ERROR; + return VSMCLIENT_OTHER_ERROR; } - return SCCLIENT_OTHER_ERROR; + return VSMCLIENT_OTHER_ERROR; } bool readFirstLineOfFile(const std::string& path, std::string& ret) @@ -137,36 +137,36 @@ bool readFirstLineOfFile(const std::string& path, std::string& ret) } //namespace -ScStatus Client::sc_start_glib_loop() noexcept +VsmStatus Client::vsm_start_glib_loop() noexcept { try { if (!gGlibLoop) { gGlibLoop.reset(new ScopedGlibLoop()); } } catch (const exception&) { - return SCCLIENT_OTHER_ERROR; + return VSMCLIENT_OTHER_ERROR; } - return SCCLIENT_SUCCESS; + return VSMCLIENT_SUCCESS; } -ScStatus Client::sc_stop_glib_loop() noexcept +VsmStatus Client::vsm_stop_glib_loop() noexcept { try { gGlibLoop.reset(); } catch (const exception&) { - return SCCLIENT_OTHER_ERROR; + return VSMCLIENT_OTHER_ERROR; } - return SCCLIENT_SUCCESS; + return VSMCLIENT_SUCCESS; } Client::Status::Status() - : mScStatus(SCCLIENT_SUCCESS), mMsg() + : mVsmStatus(VSMCLIENT_SUCCESS), mMsg() { } -Client::Status::Status(ScStatus status, const std::string& msg) - : mScStatus(status), mMsg(msg) +Client::Status::Status(VsmStatus status, const std::string& msg) + : mVsmStatus(status), mMsg(msg) { } @@ -178,33 +178,33 @@ Client::~Client() noexcept { } -ScStatus Client::createSystem() noexcept +VsmStatus Client::createSystem() noexcept { try { mConnection = DbusConnection::createSystem(); mStatus = Status(); - } catch (const exception& ex) { + } catch (const exception& ex) { mStatus = Status(toStatus(ex), ex.what()); } - return sc_get_status(); + return vsm_get_status(); } -ScStatus Client::create(const string& address) noexcept +VsmStatus Client::create(const string& address) noexcept { try { mConnection = DbusConnection::create(address); mStatus = Status(); - } catch (const exception& ex) { + } catch (const exception& ex) { mStatus = Status(toStatus(ex), ex.what()); } - return sc_get_status(); + return vsm_get_status(); } -ScStatus Client::callMethod(const DbusInterfaceInfo& info, - const string& method, - GVariant* args_in, - const string& args_spec_out, - GVariant** args_out) +VsmStatus Client::callMethod(const DbusInterfaceInfo& info, + const string& method, + GVariant* args_in, + const string& args_spec_out, + GVariant** args_out) { try { GVariantPtr ret = mConnection->callMethod(info.busName, @@ -220,12 +220,12 @@ ScStatus Client::callMethod(const DbusInterfaceInfo& info, } catch (const exception& ex) { mStatus = Status(toStatus(ex), ex.what()); } - return sc_get_status(); + return vsm_get_status(); } -ScStatus Client::signalSubscribe(const DbusInterfaceInfo& info, - const string& name, - SignalCallback signalCallback) +VsmStatus Client::signalSubscribe(const DbusInterfaceInfo& info, + const string& name, + SignalCallback signalCallback) { auto onSignal = [=](const std::string& /*senderBusName*/, const std::string & objectPath, @@ -233,8 +233,8 @@ ScStatus Client::signalSubscribe(const DbusInterfaceInfo& info, const std::string & signalName, GVariant * parameters) { if (objectPath == info.objectPath && - interface == info.interface && - signalName == name) { + interface == info.interface && + signalName == name) { signalCallback(parameters); } @@ -245,31 +245,31 @@ ScStatus Client::signalSubscribe(const DbusInterfaceInfo& info, } catch (const std::exception& ex) { mStatus = Status(toStatus(ex), ex.what()); } - return sc_get_status(); + return vsm_get_status(); } -const char* Client::sc_get_status_message() noexcept +const char* Client::vsm_get_status_message() noexcept { return mStatus.mMsg.c_str(); } -ScStatus Client::sc_get_status() noexcept +VsmStatus Client::vsm_get_status() noexcept { - return mStatus.mScStatus; + return mStatus.mVsmStatus; } -ScStatus Client::sc_get_container_dbuses(ScArrayString* keys, ScArrayString* values) noexcept +VsmStatus Client::vsm_get_container_dbuses(VsmArrayString* keys, VsmArrayString* values) noexcept { assert(keys); assert(values); GVariant* out; - ScStatus ret = callMethod(HOST_INTERFACE, - api::host::METHOD_GET_CONTAINER_DBUSES, - NULL, - "(a{ss})", - &out); - if (ret != SCCLIENT_SUCCESS) { + VsmStatus ret = callMethod(HOST_INTERFACE, + api::host::METHOD_GET_CONTAINER_DBUSES, + NULL, + "(a{ss})", + &out); + if (ret != VSMCLIENT_SUCCESS) { return ret; } GVariant* unpacked; @@ -280,17 +280,17 @@ ScStatus Client::sc_get_container_dbuses(ScArrayString* keys, ScArrayString* val return ret; } -ScStatus Client::sc_get_container_ids(ScArrayString* array) noexcept +VsmStatus Client::vsm_get_domain_ids(VsmArrayString* array) noexcept { assert(array); GVariant* out; - ScStatus ret = callMethod(HOST_INTERFACE, - api::host::METHOD_GET_CONTAINER_ID_LIST, - NULL, - "(as)", - &out); - if (ret != SCCLIENT_SUCCESS) { + VsmStatus ret = callMethod(HOST_INTERFACE, + api::host::METHOD_GET_CONTAINER_ID_LIST, + NULL, + "(as)", + &out); + if (ret != VSMCLIENT_SUCCESS) { return ret; } GVariant* unpacked; @@ -301,17 +301,17 @@ ScStatus Client::sc_get_container_ids(ScArrayString* array) noexcept return ret; } -ScStatus Client::sc_get_active_container_id(ScString* id) noexcept +VsmStatus Client::vsm_get_active_container_id(VsmString* id) noexcept { assert(id); GVariant* out; - ScStatus ret = callMethod(HOST_INTERFACE, - api::host::METHOD_GET_ACTIVE_CONTAINER_ID, - NULL, - "(s)", - &out); - if (ret != SCCLIENT_SUCCESS) { + VsmStatus ret = callMethod(HOST_INTERFACE, + api::host::METHOD_GET_ACTIVE_CONTAINER_ID, + NULL, + "(s)", + &out); + if (ret != VSMCLIENT_SUCCESS) { return ret; } GVariant* unpacked; @@ -322,7 +322,7 @@ ScStatus Client::sc_get_active_container_id(ScString* id) noexcept return ret; } -ScStatus Client::sc_get_container_id_by_pid(int pid, ScString* id) noexcept +VsmStatus Client::vsm_lookup_domain_by_pid(int pid, VsmString* id) noexcept { assert(id); @@ -330,22 +330,22 @@ ScStatus Client::sc_get_container_id_by_pid(int pid, ScString* id) noexcept std::string cpuset; if (!readFirstLineOfFile(path, cpuset)) { - mStatus = Status(SCCLIENT_INVALID_ARGUMENT, "Process not found"); - return sc_get_status(); + mStatus = Status(VSMCLIENT_INVALID_ARGUMENT, "Process not found"); + return vsm_get_status(); } std::string containerId; if (!parseContainerIdFromCpuSet(cpuset, containerId)) { - mStatus = Status(SCCLIENT_OTHER_ERROR, "unknown format of cpuset"); - return sc_get_status(); + mStatus = Status(VSMCLIENT_OTHER_ERROR, "unknown format of cpuset"); + return vsm_get_status(); } *id = strdup(containerId.c_str()); mStatus = Status(); - return sc_get_status();; + return vsm_get_status();; } -ScStatus Client::sc_set_active_container(const char* id) noexcept +VsmStatus Client::vsm_set_active_container(const char* id) noexcept { assert(id); @@ -353,7 +353,7 @@ ScStatus Client::sc_set_active_container(const char* id) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_SET_ACTIVE_CONTAINER, args_in); } -ScStatus Client::sc_add_container(const char* id) noexcept +VsmStatus Client::vsm_create_domain(const char* id) noexcept { assert(id); @@ -361,12 +361,13 @@ ScStatus Client::sc_add_container(const char* id) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_ADD_CONTAINER, args_in); } -ScStatus Client::sc_container_dbus_state(ScContainerDbusStateCallback containerDbusStateCallback, +VsmStatus Client::vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, void* data) noexcept { assert(containerDbusStateCallback); - auto onSigal = [=](GVariant * parameters) { + auto onSigal = [=](GVariant * parameters) + { const char* container; const char* dbusAddress; g_variant_get(parameters, "(&s&s)", &container, &dbusAddress); @@ -378,7 +379,7 @@ ScStatus Client::sc_container_dbus_state(ScContainerDbusStateCallback containerD onSigal); } -ScStatus Client::sc_notify_active_container(const char* application, const char* message) noexcept +VsmStatus Client::vsm_notify_active_container(const char* application, const char* message) noexcept { assert(application); assert(message); @@ -389,34 +390,34 @@ ScStatus Client::sc_notify_active_container(const char* application, const char* args_in); } -ScStatus Client::sc_file_move_request(const char* destContainer, const char* path) noexcept +VsmStatus Client::vsm_file_move_request(const char* destContainer, const char* path) noexcept { assert(destContainer); assert(path); GVariant* out; GVariant* args_in = g_variant_new("(ss)", destContainer, path); - ScStatus ret = callMethod(CONTAINER_INTERFACE, - api::container::METHOD_FILE_MOVE_REQUEST, - args_in, - "(s)", - &out); + VsmStatus ret = callMethod(CONTAINER_INTERFACE, + api::container::METHOD_FILE_MOVE_REQUEST, + args_in, + "(s)", + &out); - if (ret != SCCLIENT_SUCCESS) { + if (ret != VSMCLIENT_SUCCESS) { return ret; } const gchar* retcode = NULL;; g_variant_get(out, "(&s)", &retcode); if (strcmp(retcode, api::container::FILE_MOVE_SUCCEEDED.c_str()) != 0) { - mStatus = Status(SCCLIENT_CUSTOM_ERROR, retcode); + mStatus = Status(VSMCLIENT_CUSTOM_ERROR, retcode); g_variant_unref(out); - return sc_get_status(); + return vsm_get_status(); } g_variant_unref(out); return ret; } -ScStatus Client::sc_notification(ScNotificationCallback notificationCallback, void* data) noexcept +VsmStatus Client::vsm_notification(VsmNotificationCallback notificationCallback, void* data) noexcept { assert(notificationCallback); diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index 3f5a556..9a233e5 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -55,22 +55,22 @@ private: typedef std::function SignalCallback; struct Status { Status(); - Status(ScStatus status, const std::string& msg); - ScStatus mScStatus; + Status(VsmStatus status, const std::string& msg); + VsmStatus mVsmStatus; std::string mMsg; }; dbus::DbusConnection::Pointer mConnection; Status mStatus; - ScStatus callMethod(const DbusInterfaceInfo& info, - const std::string& method, - GVariant* args_in, - const std::string& args_spec_out = std::string(), - GVariant** args_out = NULL); - ScStatus signalSubscribe(const DbusInterfaceInfo& info, - const std::string& name, - SignalCallback signalCallback); + VsmStatus callMethod(const DbusInterfaceInfo& info, + const std::string& method, + GVariant* args_in, + const std::string& args_spec_out = std::string(), + GVariant** args_out = NULL); + VsmStatus signalSubscribe(const DbusInterfaceInfo& info, + const std::string& name, + SignalCallback signalCallback); public: Client() noexcept; @@ -81,7 +81,7 @@ public: * * @return status of this function call */ - ScStatus createSystem() noexcept; + VsmStatus createSystem() noexcept; /** * Create client. @@ -89,76 +89,76 @@ public: * @param address Dbus socket address * @return status of this function call */ - ScStatus create(const std::string& address) noexcept; + VsmStatus create(const std::string& address) noexcept; /** - * @see ::sc_get_status_message + * @see ::vsm_get_status_message */ - const char* sc_get_status_message() noexcept; + const char* vsm_get_status_message() noexcept; /** - * @see ::sc_get_status + * @see ::vsm_get_status */ - ScStatus sc_get_status() noexcept; + VsmStatus vsm_get_status() noexcept; /** - * @see ::sc_get_container_dbuses + * @see ::vsm_get_container_dbuses */ - ScStatus sc_get_container_dbuses(ScArrayString* keys, ScArrayString* values) noexcept; + VsmStatus vsm_get_container_dbuses(VsmArrayString* keys, VsmArrayString* values) noexcept; /** - * @see ::sc_get_container_ids + * @see ::vsm_get_domain_ids */ - ScStatus sc_get_container_ids(ScArrayString* array) noexcept; + VsmStatus vsm_get_domain_ids(VsmArrayString* array) noexcept; /** - * @see ::sc_get_active_container_id + * @see ::vsm_get_active_container_id */ - ScStatus sc_get_active_container_id(ScString* id) noexcept; + VsmStatus vsm_get_active_container_id(VsmString* id) noexcept; /** - * @see ::sc_get_container_id_by_pid + * @see ::vsm_lookup_domain_by_pid */ - ScStatus sc_get_container_id_by_pid(int pid, ScString* id) noexcept; + VsmStatus vsm_lookup_domain_by_pid(int pid, VsmString* id) noexcept; /** - * @see ::sc_set_active_container + * @see ::vsm_set_active_container */ - ScStatus sc_set_active_container(const char* id) noexcept; + VsmStatus vsm_set_active_container(const char* id) noexcept; /** - * @see ::sc_add_container + * @see ::vsm_create_domain */ - ScStatus sc_add_container(const char* id) noexcept; + VsmStatus vsm_create_domain(const char* id) noexcept; /** - * @see ::sc_container_dbus_state + * @see ::vsm_add_state_callback */ - ScStatus sc_container_dbus_state(ScContainerDbusStateCallback containerDbusStateCallback, + VsmStatus vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, void* data) noexcept; /** - * @see ::sc_notify_active_container + * @see ::vsm_notify_active_container */ - ScStatus sc_notify_active_container(const char* application, const char* message) noexcept; + VsmStatus vsm_notify_active_container(const char* application, const char* message) noexcept; /** - * @see ::sc_file_move_request + * @see ::vsm_file_move_request */ - ScStatus sc_file_move_request(const char* destContainer, const char* path) noexcept; + VsmStatus vsm_file_move_request(const char* destContainer, const char* path) noexcept; /** - * @see ::sc_notification + * @see ::vsm_notification */ - ScStatus sc_notification(ScNotificationCallback notificationCallback, void* data) noexcept; + VsmStatus vsm_notification(VsmNotificationCallback notificationCallback, void* data) noexcept; /** - * @see ::sc_start_glib_loop + * @see ::vsm_start_glib_loop */ - static ScStatus sc_start_glib_loop() noexcept; + static VsmStatus vsm_start_glib_loop() noexcept; /** - * @see ::sc_stop_glib_loop + * @see ::vsm_stop_glib_loop */ - static ScStatus sc_stop_glib_loop() noexcept; + static VsmStatus vsm_stop_glib_loop() noexcept; }; #endif /* SECURITY_CONTAINERS_CLIENT_IMPL_HPP */ diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index 95c2758..ad8fb38 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -37,7 +37,7 @@ using namespace std; namespace { -Client& getClient(ScClient client) +Client& getClient(VsmClient client) { assert(client); return *reinterpret_cast(client); @@ -46,118 +46,118 @@ Client& getClient(ScClient client) } // namespace /* external */ -API ScStatus sc_start_glib_loop() +API VsmStatus vsm_start_glib_loop() { - return Client::sc_start_glib_loop(); + return Client::vsm_start_glib_loop(); } -API ScStatus sc_stop_glib_loop() +API VsmStatus vsm_stop_glib_loop() { - return Client::sc_stop_glib_loop(); + return Client::vsm_stop_glib_loop(); } -API ScClient sc_client_create() +API VsmClient vsm_client_create() { Client* clientPtr = new(nothrow) Client(); - return reinterpret_cast(clientPtr); + return reinterpret_cast(clientPtr); } -API ScStatus sc_connect(ScClient client) +API VsmStatus vsm_connect(VsmClient client) { return getClient(client).createSystem(); } -API ScStatus sc_connect_custom(ScClient client, const char* address) +API VsmStatus vsm_connect_custom(VsmClient client, const char* address) { return getClient(client).create(address); } -API void sc_array_string_free(ScArrayString astring) +API void vsm_array_string_free(VsmArrayString astring) { if (!astring) { return; } for (char** ptr = astring; *ptr; ++ptr) { - sc_string_free(*ptr); + vsm_string_free(*ptr); } free(astring); } -API void sc_string_free(ScString string) +API void vsm_string_free(VsmString string) { free(string); } -API void sc_client_free(ScClient client) +API void vsm_client_free(VsmClient client) { if (client != NULL) { delete &getClient(client); } } -API const char* sc_get_status_message(ScClient client) +API const char* vsm_get_status_message(VsmClient client) { - return getClient(client).sc_get_status_message(); + return getClient(client).vsm_get_status_message(); } -API ScStatus sc_get_status(ScClient client) +API VsmStatus vsm_get_status(VsmClient client) { - return getClient(client).sc_get_status(); + return getClient(client).vsm_get_status(); } -API ScStatus sc_get_container_dbuses(ScClient client, ScArrayString* keys, ScArrayString* values) +API VsmStatus vsm_get_container_dbuses(VsmClient client, VsmArrayString* keys, VsmArrayString* values) { - return getClient(client).sc_get_container_dbuses(keys, values); + return getClient(client).vsm_get_container_dbuses(keys, values); } -API ScStatus sc_get_container_ids(ScClient client, ScArrayString* array) +API VsmStatus vsm_get_domain_ids(VsmClient client, VsmArrayString* array) { - return getClient(client).sc_get_container_ids(array); + return getClient(client).vsm_get_domain_ids(array); } -API ScStatus sc_get_active_container_id(ScClient client, ScString* id) +API VsmStatus vsm_get_active_container_id(VsmClient client, VsmString* id) { - return getClient(client).sc_get_active_container_id(id); + return getClient(client).vsm_get_active_container_id(id); } -API ScStatus sc_get_container_id_by_pid(ScClient client, int pid, ScString* id) +API VsmStatus vsm_lookup_domain_by_pid(VsmClient client, int pid, VsmString* id) { - return getClient(client).sc_get_container_id_by_pid(pid, id); + return getClient(client).vsm_lookup_domain_by_pid(pid, id); } -API ScStatus sc_set_active_container(ScClient client, const char* id) +API VsmStatus vsm_set_active_container(VsmClient client, const char* id) { - return getClient(client).sc_set_active_container(id); + return getClient(client).vsm_set_active_container(id); } -API ScStatus sc_add_container(ScClient client, const char* id) +API VsmStatus vsm_create_domain(VsmClient client, const char* id) { - return getClient(client).sc_add_container(id); + return getClient(client).vsm_create_domain(id); } -API ScStatus sc_container_dbus_state(ScClient client, - ScContainerDbusStateCallback containerDbusStateCallback, +API VsmStatus vsm_add_state_callback(VsmClient client, + VsmContainerDbusStateCallback containerDbusStateCallback, void* data) { - return getClient(client).sc_container_dbus_state(containerDbusStateCallback, data); + return getClient(client).vsm_add_state_callback(containerDbusStateCallback, data); } -API ScStatus sc_notify_active_container(ScClient client, - const char* application, - const char* message) +API VsmStatus vsm_notify_active_container(VsmClient client, + const char* application, + const char* message) { - return getClient(client).sc_notify_active_container(application, message); + return getClient(client).vsm_notify_active_container(application, message); } -API ScStatus sc_file_move_request(ScClient client, const char* destContainer, const char* path) +API VsmStatus vsm_file_move_request(VsmClient client, const char* destContainer, const char* path) { - return getClient(client).sc_file_move_request(destContainer, path); + return getClient(client).vsm_file_move_request(destContainer, path); } -API ScStatus sc_notification(ScClient client, - ScNotificationCallback notificationCallback, - void* data) +API VsmStatus vsm_notification(VsmClient client, + VsmNotificationCallback notificationCallback, + void* data) { - return getClient(client).sc_notification(notificationCallback, data); + return getClient(client).vsm_notification(notificationCallback, data); } diff --git a/client/security-containers-client.h b/client/security-containers-client.h index 1307cb5..710af99 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -29,47 +29,47 @@ int main(int argc, char** argv) { - ScStatus status; - ScClient client; - ScArrayString values = NULL; + VsmStatus status; + VsmClient client; + VsmArrayString values = NULL; int ret = 0; - status = sc_start_glib_loop(); // start glib loop (if not started any yet) - if (SCCLIENT_SUCCESS != status) { + status = vsm_start_glib_loop(); // start glib loop (if not started any yet) + if (VSMCLIENT_SUCCESS != status) { // error! return 1; } - client = sc_client_create(); // create client handle + client = vsm_client_create(); // create client handle if (NULL == client) { // error! ret = 1; goto finish; } - status = sc_connect(client); // connect to dbus - if (SCCLIENT_SUCCESS != status) { + status = vsm_connect(client); // connect to dbus + if (VSMCLIENT_SUCCESS != status) { // error! ret = 1; goto finish; } - status = sc_get_container_ids(client, &values); - if (SCCLIENT_SUCCESS != status) { + status = vsm_get_domain_ids(client, &values); + if (VSMCLIENT_SUCCESS != status) { // error! ret = 1; goto finish; } // print array - for (ScArrayString iValues = values; *iValues; iValues++) { + for (VsmArrayString iValues = values; *iValues; iValues++) { printf("%s\n", *iValues); } finish: - sc_array_string_free(values); // free memory - sc_client_free(client); // destroy client handle - sc_stop_glib_loop(); // stop the glib loop (use only with sc_start_glib_loop) + vsm_array_string_free(values); // free memory + vsm_client_free(client); // destroy client handle + vsm_stop_glib_loop(); // stop the glib loop (use only with vsm_start_glib_loop) return ret; } @endcode @@ -86,33 +86,33 @@ extern "C" /** * security-containers-server's client pointer. */ -typedef void* ScClient; +typedef void* VsmClient; /** * NULL-terminated string type. * - * @sa sc_array_string_free + * @sa vsm_array_string_free */ -typedef char* ScString; +typedef char* VsmString; /** * NULL-terminated array of strings type. * - * @sa sc_string_free + * @sa vsm_string_free */ -typedef ScString* ScArrayString; +typedef VsmString* VsmArrayString; /** * Completion status of communication function. */ typedef enum { - SCCLIENT_CUSTOM_ERROR, ///< User specified error - SCCLIENT_IO_ERROR, ///< Input/Output error - SCCLIENT_OPERATION_FAILED, ///< Operation failed - SCCLIENT_INVALID_ARGUMENT, ///< Invalid argument - SCCLIENT_OTHER_ERROR, ///< Other error - SCCLIENT_SUCCESS ///< Success -} ScStatus; + VSMCLIENT_CUSTOM_ERROR, ///< User specified error + VSMCLIENT_IO_ERROR, ///< Input/Output error + VSMCLIENT_OPERATION_FAILED, ///< Operation failed + VSMCLIENT_INVALID_ARGUMENT, ///< Invalid argument + VSMCLIENT_OTHER_ERROR, ///< Other error + VSMCLIENT_SUCCESS ///< Success +} VsmStatus; /** * Start glib loop. @@ -122,30 +122,30 @@ typedef enum { * * @return status of this function call */ -ScStatus sc_start_glib_loop(); +VsmStatus vsm_start_glib_loop(); /** * Stop glib loop. * - * Call only if sc_start_glib_loop() was called. + * Call only if vsm_start_glib_loop() was called. * * @return status of this function call */ -ScStatus sc_stop_glib_loop(); +VsmStatus vsm_stop_glib_loop(); /** * Create a new security-containers-server's client. * * @return Created client pointer or NULL on failure. */ -ScClient sc_client_create(); +VsmClient vsm_client_create(); /** * Release client resources. * * @param[in] client security-containers-server's client */ -void sc_client_free(ScClient client); +void vsm_client_free(VsmClient client); /** * Get status code of last security-containers-server communication. @@ -153,7 +153,7 @@ void sc_client_free(ScClient client); * @param[in] client security-containers-server's client * @return status of this function call */ -ScStatus sc_get_status(ScClient client); +VsmStatus vsm_get_status(VsmClient client); /** * Get status message of the last security-containers-server communication. @@ -161,7 +161,7 @@ ScStatus sc_get_status(ScClient client); * @param[in] client security-containers-server's client * @return last status message from security-containers-server communication */ -const char* sc_get_status_message(ScClient client); +const char* vsm_get_status_message(VsmClient client); /** * Connect client to the security-containers-server. @@ -169,7 +169,7 @@ const char* sc_get_status_message(ScClient client); * @param[in] client security-containers-server's client * @return status of this function call */ -ScStatus sc_connect(ScClient client); +VsmStatus vsm_connect(VsmClient client); /** * Connect client to the security-containers-server via custom address. @@ -178,21 +178,21 @@ ScStatus sc_connect(ScClient client); * @param[in] address dbus address * @return status of this function call */ -ScStatus sc_connect_custom(ScClient client, const char* address); +VsmStatus vsm_connect_custom(VsmClient client, const char* address); /** - * Release ScArrayString. + * Release VsmArrayString. * - * @param[in] astring ScArrayString + * @param[in] astring VsmArrayString */ -void sc_array_string_free(ScArrayString astring); +void vsm_array_string_free(VsmArrayString astring); /** - * Release ScString. + * Release VsmString. * - * @param string ScString + * @param string VsmString */ -void sc_string_free(ScString string); +void vsm_string_free(VsmString string); /** @@ -208,9 +208,9 @@ void sc_string_free(ScString string); * * @param[in] containerId affected container id * @param[in] dbusAddress new D-Bus address - * @param data custom user's data pointer passed to sc_container_dbus_state() function + * @param data custom user's data pointer passed to vsm_add_state_callback() function */ -typedef void (*ScContainerDbusStateCallback)(const char* containerId, +typedef void (*VsmContainerDbusStateCallback)(const char* containerId, const char* dbusAddress, void* data); @@ -222,9 +222,9 @@ typedef void (*ScContainerDbusStateCallback)(const char* containerId, * @param[out] values array of containers dbus address * @return status of this function call * @post keys[i] corresponds to values[i] - * @remark Use sc_array_string_free() to free memory occupied by @p keys and @p values. + * @remark Use vsm_array_string_free() to free memory occupied by @p keys and @p values. */ -ScStatus sc_get_container_dbuses(ScClient client, ScArrayString* keys, ScArrayString* values); +VsmStatus vsm_get_container_dbuses(VsmClient client, VsmArrayString* keys, VsmArrayString* values); /** * Get containers name. @@ -232,9 +232,9 @@ ScStatus sc_get_container_dbuses(ScClient client, ScArrayString* keys, ScArraySt * @param[in] client security-containers-server's client * @param[out] array array of containers name * @return status of this function call - * @remark Use sc_array_string_free() to free memory occupied by @p array. + * @remark Use vsm_array_string_free() to free memory occupied by @p array. */ -ScStatus sc_get_container_ids(ScClient client, ScArrayString* array); +VsmStatus vsm_get_domain_ids(VsmClient client, VsmArrayString* array); /** * Get active (foreground) container name. @@ -242,9 +242,9 @@ ScStatus sc_get_container_ids(ScClient client, ScArrayString* array); * @param[in] client security-containers-server's client * @param[out] id active container name * @return status of this function call - * @remark Use @p sc_string_free() to free memory occupied by @p id. + * @remark Use @p vsm_string_free() to free memory occupied by @p id. */ -ScStatus sc_get_active_container_id(ScClient client, ScString* id); +VsmStatus vsm_get_active_container_id(VsmClient client, VsmString* id); /** * Get container name of process with given pid. @@ -253,9 +253,9 @@ ScStatus sc_get_active_container_id(ScClient client, ScString* id); * @param[in] pid process id * @param[out] id active container name * @return status of this function call - * @remark Use @p sc_string_free() to free memory occupied by @p id. + * @remark Use @p vsm_string_free() to free memory occupied by @p id. */ -ScStatus sc_get_container_id_by_pid(ScClient client, int pid, ScString* id); +VsmStatus vsm_lookup_domain_by_pid(VsmClient client, int pid, VsmString* id); /** * Set active (foreground) container. @@ -264,7 +264,7 @@ ScStatus sc_get_container_id_by_pid(ScClient client, int pid, ScString* id); * @param[in] id container name * @return status of this function call */ -ScStatus sc_set_active_container(ScClient client, const char* id); +VsmStatus vsm_set_active_container(VsmClient client, const char* id); /** * Create and add container @@ -273,7 +273,7 @@ ScStatus sc_set_active_container(ScClient client, const char* id); * @param[in] id container id * @return status of this function call */ -ScStatus sc_add_container(ScClient client, const char* id); +VsmStatus vsm_create_domain(VsmClient client, const char* id); /** * Register dbus state change callback function. @@ -285,8 +285,8 @@ ScStatus sc_add_container(ScClient client, const char* id); * @param[in] data some extra data that will be passed to callback function * @return status of this function call */ -ScStatus sc_container_dbus_state(ScClient client, - ScContainerDbusStateCallback containerDbusStateCallback, +VsmStatus vsm_add_state_callback(VsmClient client, + VsmContainerDbusStateCallback containerDbusStateCallback, void* data); /** @} */ // Host API @@ -306,9 +306,9 @@ ScStatus sc_container_dbus_state(ScClient client, * @param[in] container source container * @param[in] application sending application name * @param[in] message notification message - * @param data custom user's data pointer passed to sc_notification() + * @param data custom user's data pointer passed to vsm_notification() */ -typedef void (*ScNotificationCallback)(const char* container, +typedef void (*VsmNotificationCallback)(const char* container, const char* application, const char* message, void* data); @@ -320,7 +320,7 @@ typedef void (*ScNotificationCallback)(const char* container, * @param[in] message message * @return status of this function call */ -ScStatus sc_notify_active_container(ScClient client, const char* application, const char* message); +VsmStatus vsm_notify_active_container(VsmClient client, const char* application, const char* message); /** * Move file between containers. @@ -330,7 +330,7 @@ ScStatus sc_notify_active_container(ScClient client, const char* application, co * @param[in] path path to moved file * @return status of this function call */ -ScStatus sc_file_move_request(ScClient client, const char* destContainer, const char* path); +VsmStatus vsm_file_move_request(VsmClient client, const char* destContainer, const char* path); /** * Register notification callback function. @@ -342,7 +342,7 @@ ScStatus sc_file_move_request(ScClient client, const char* destContainer, const * @param[in] data some extra data that will be passed to callback function * @return status of this function call */ -ScStatus sc_notification(ScClient client, ScNotificationCallback notificationCallback, void* data); +VsmStatus vsm_notification(VsmClient client, VsmNotificationCallback notificationCallback, void* data); /** @} */ // Domain API diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 5db6962..184659e 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -48,8 +48,14 @@ const std::string TEST_DBUS_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/client/ut-client/test-dbus-daemon.conf"; struct Loop { - Loop() { sc_start_glib_loop(); } - ~Loop() { sc_stop_glib_loop(); } + Loop() + { + vsm_start_glib_loop(); + } + ~Loop() + { + vsm_stop_glib_loop(); + } }; struct Fixture { @@ -64,32 +70,39 @@ struct Fixture { const int EVENT_TIMEOUT = 5000; ///< ms const std::map EXPECTED_DBUSES_STARTED = { - {"ut-containers-manager-console1-dbus", - "unix:path=/tmp/ut-containers-manager/console1-dbus/dbus/system_bus_socket"}, - {"ut-containers-manager-console2-dbus", - "unix:path=/tmp/ut-containers-manager/console2-dbus/dbus/system_bus_socket"}, - {"ut-containers-manager-console3-dbus", - "unix:path=/tmp/ut-containers-manager/console3-dbus/dbus/system_bus_socket"}}; - -void convertDictToMap(ScArrayString keys, - ScArrayString values, + { + "ut-containers-manager-console1-dbus", + "unix:path=/tmp/ut-containers-manager/console1-dbus/dbus/system_bus_socket" + }, + { + "ut-containers-manager-console2-dbus", + "unix:path=/tmp/ut-containers-manager/console2-dbus/dbus/system_bus_socket" + }, + { + "ut-containers-manager-console3-dbus", + "unix:path=/tmp/ut-containers-manager/console3-dbus/dbus/system_bus_socket" + } +}; + +void convertDictToMap(VsmArrayString keys, + VsmArrayString values, std::map& ret) { - ScArrayString iKeys; - ScArrayString iValues; + VsmArrayString iKeys; + VsmArrayString iValues; for (iKeys = keys, iValues = values; *iKeys && *iValues; iKeys++, iValues++) { ret.insert(std::make_pair(*iKeys, *iValues)); } } -void convertArrayToSet(ScArrayString values, std::set& ret) +void convertArrayToSet(VsmArrayString values, std::set& ret) { - for (ScArrayString iValues = values; *iValues; iValues++) { + for (VsmArrayString iValues = values; *iValues; iValues++) { ret.insert(*iValues); } } -int getArrayStringLength(ScArrayString astring, int max_len = -1) +int getArrayStringLength(VsmArrayString astring, int max_len = -1) { int i = 0; for (i = 0; astring[i]; i++) { @@ -108,21 +121,21 @@ BOOST_AUTO_TEST_CASE(NotRunningServerTest) { cm.stopAll(); - ScClient client = sc_client_create(); - ScStatus status = sc_connect_custom(client, - EXPECTED_DBUSES_STARTED.begin()->second.c_str()); - BOOST_CHECK_EQUAL(SCCLIENT_IO_ERROR, status); - sc_client_free(client); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect_custom(client, + EXPECTED_DBUSES_STARTED.begin()->second.c_str()); + BOOST_CHECK_EQUAL(VSMCLIENT_IO_ERROR, status); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(GetContainerDbusesTest) { - ScClient client = sc_client_create(); - ScStatus status = sc_connect(client); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); - ScArrayString keys, values; - status = sc_get_container_dbuses(client, &keys, &values); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect(client); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + VsmArrayString keys, values; + status = vsm_get_container_dbuses(client, &keys, &values); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); BOOST_CHECK_EQUAL(getArrayStringLength(keys, EXPECTED_DBUSES_STARTED.size() + 1), EXPECTED_DBUSES_STARTED.size()); @@ -132,19 +145,19 @@ BOOST_AUTO_TEST_CASE(GetContainerDbusesTest) std::map containers; convertDictToMap(keys, values, containers); BOOST_CHECK(containers == EXPECTED_DBUSES_STARTED); - sc_array_string_free(keys); - sc_array_string_free(values); - sc_client_free(client); + vsm_array_string_free(keys); + vsm_array_string_free(values); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(GetContainerIdsTest) { - ScClient client = sc_client_create(); - ScStatus status = sc_connect(client); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); - ScArrayString values; - status = sc_get_container_ids(client, &values); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect(client); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + VsmArrayString values; + status = vsm_get_domain_ids(client, &values); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); BOOST_CHECK_EQUAL(getArrayStringLength(values, EXPECTED_DBUSES_STARTED.size() + 1), EXPECTED_DBUSES_STARTED.size()); @@ -154,23 +167,23 @@ BOOST_AUTO_TEST_CASE(GetContainerIdsTest) for (const auto& container : containers) { BOOST_CHECK(EXPECTED_DBUSES_STARTED.find(container) != EXPECTED_DBUSES_STARTED.cend()); } - sc_array_string_free(values); - sc_client_free(client); + vsm_array_string_free(values); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(GetActiveContainerIdTest) { - ScClient client = sc_client_create(); - ScStatus status = sc_connect(client); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); - ScString container; - status = sc_get_active_container_id(client, &container); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect(client); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + VsmString container; + status = vsm_get_active_container_id(client, &container); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); BOOST_CHECK_EQUAL(container, cm.getRunningForegroundContainerId()); - sc_string_free(container); - sc_client_free(client); + vsm_string_free(container); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(SetActiveContainerTest) @@ -179,25 +192,25 @@ BOOST_AUTO_TEST_CASE(SetActiveContainerTest) BOOST_REQUIRE_NE(newActiveContainerId, cm.getRunningForegroundContainerId()); - ScClient client = sc_client_create(); - ScStatus status = sc_connect(client); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); - status = sc_set_active_container(client, newActiveContainerId.c_str()); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect(client); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + status = vsm_set_active_container(client, newActiveContainerId.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); BOOST_CHECK_EQUAL(newActiveContainerId, cm.getRunningForegroundContainerId()); - sc_client_free(client); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(AddContainerTest) { const std::string newActiveContainerId = ""; - ScClient client = sc_client_create(); - ScStatus status = sc_connect(client); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); - status = sc_add_container(client, newActiveContainerId.c_str()); - BOOST_REQUIRE_EQUAL(SCCLIENT_CUSTOM_ERROR, status); - sc_client_free(client); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect(client); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + status = vsm_create_domain(client, newActiveContainerId.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_CUSTOM_ERROR, status); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(FileMoveRequestTest) @@ -205,14 +218,14 @@ BOOST_AUTO_TEST_CASE(FileMoveRequestTest) const std::string path = "/tmp/fake_path"; const std::string secondContainer = "fake_container"; - ScClient client = sc_client_create(); - ScStatus status = sc_connect_custom(client, EXPECTED_DBUSES_STARTED.begin()->second.c_str()); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); - status = sc_file_move_request(client, secondContainer.c_str(), path.c_str()); - BOOST_REQUIRE_EQUAL(SCCLIENT_CUSTOM_ERROR, status); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect_custom(client, EXPECTED_DBUSES_STARTED.begin()->second.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + status = vsm_file_move_request(client, secondContainer.c_str(), path.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_CUSTOM_ERROR, status); BOOST_REQUIRE_EQUAL(api::container::FILE_MOVE_DESTINATION_NOT_FOUND, - sc_get_status_message(client)); - sc_client_free(client); + vsm_get_status_message(client)); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(NotificationTest) @@ -228,30 +241,29 @@ BOOST_AUTO_TEST_CASE(NotificationTest) auto callback = [](const char* container, const char* application, const char* message, - void* data) - { + void* data) { CallbackData& callbackData = *reinterpret_cast(data); callbackData.receivedSignalMsg.push_back(std::make_tuple(container, application, message)); callbackData.signalReceivedLatch.set(); }; CallbackData callbackData; - std::map clients; + std::map clients; for (const auto& it : EXPECTED_DBUSES_STARTED) { - ScClient client = sc_client_create(); - ScStatus status = sc_connect_custom(client, it.second.c_str()); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect_custom(client, it.second.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); clients[it.first] = client; } for (auto& client : clients) { - ScStatus status = sc_notification(client.second, callback, &callbackData); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmStatus status = vsm_notification(client.second, callback, &callbackData); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); } for (auto& client : clients) { - ScStatus status = sc_notify_active_container(client.second, - MSG_APP.c_str(), - MSG_CONTENT.c_str()); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmStatus status = vsm_notify_active_container(client.second, + MSG_APP.c_str(), + MSG_CONTENT.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); } BOOST_CHECK(callbackData.signalReceivedLatch.waitForN(clients.size() - 1, EVENT_TIMEOUT)); @@ -264,39 +276,39 @@ BOOST_AUTO_TEST_CASE(NotificationTest) } for (auto& client : clients) { - sc_client_free(client.second); + vsm_client_free(client.second); } } BOOST_AUTO_TEST_CASE(GetContainerIdByPidTest1) { - ScClient client = sc_client_create(); - ScString container; - ScStatus status = sc_get_container_id_by_pid(client, 1, &container); - BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + VsmClient client = vsm_client_create(); + VsmString container; + VsmStatus status = vsm_lookup_domain_by_pid(client, 1, &container); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); BOOST_CHECK_EQUAL(container, std::string("host")); - sc_string_free(container); - sc_client_free(client); + vsm_string_free(container); + vsm_client_free(client); } BOOST_AUTO_TEST_CASE(GetContainerIdByPidTest2) { std::set ids; - ScClient client = sc_client_create(); + VsmClient client = vsm_client_create(); for (int n = 0; n < 100000; ++n) { - ScString container; - ScStatus status = sc_get_container_id_by_pid(client, n, &container); - if (status == SCCLIENT_SUCCESS) { + VsmString container; + VsmStatus status = vsm_lookup_domain_by_pid(client, n, &container); + if (status == VSMCLIENT_SUCCESS) { ids.insert(container); - sc_string_free(container); + vsm_string_free(container); } else { - BOOST_WARN_MESSAGE(status == SCCLIENT_INVALID_ARGUMENT, sc_get_status_message(client)); + BOOST_WARN_MESSAGE(status == VSMCLIENT_INVALID_ARGUMENT, vsm_get_status_message(client)); } } - sc_client_free(client); + vsm_client_free(client); BOOST_CHECK(ids.count("host") == 1); -- 2.7.4 From 92f6ab7c3bc9d0fb0e07def9cb552c60309cd008 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Fri, 7 Nov 2014 09:25:00 +0100 Subject: [PATCH 11/16] Add vsm_del_state_callback and vsm_del_notification_callback functions [Feature] Add possibility to unsubscribe from signal (unregister state callback and notification callback) [Cause] N/A [Solution] API Change: * add vsm_del_state_callback and vsm_del_notification_callback, * change vsm_add_notification_callback and vsm_add_state_callback signatures [Verification] Compile Change-Id: Ia390a41175c6b3bd9334018c28f61533c248fc05 --- client/security-containers-client-impl.cpp | 44 ++++++++++++++++++++++++++---- client/security-containers-client-impl.hpp | 25 ++++++++++++++--- client/security-containers-client.cpp | 25 +++++++++++++---- client/security-containers-client.h | 38 ++++++++++++++++++++++++-- tests/unit_tests/client/ut-client.cpp | 5 +++- 5 files changed, 117 insertions(+), 20 deletions(-) diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index 39abbfd..fd85d2b 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -225,7 +225,8 @@ VsmStatus Client::callMethod(const DbusInterfaceInfo& info, VsmStatus Client::signalSubscribe(const DbusInterfaceInfo& info, const string& name, - SignalCallback signalCallback) + SignalCallback signalCallback, + VsmSubscriptionId* subscriptionId) { auto onSignal = [=](const std::string& /*senderBusName*/, const std::string & objectPath, @@ -240,7 +241,21 @@ VsmStatus Client::signalSubscribe(const DbusInterfaceInfo& info, } }; try { - mConnection->signalSubscribe(onSignal, info.busName); + guint id = mConnection->signalSubscribe(onSignal, info.busName); + if (subscriptionId) { + *subscriptionId = id; + } + mStatus = Status(); + } catch (const std::exception& ex) { + mStatus = Status(toStatus(ex), ex.what()); + } + return vsm_get_status(); +} + +VsmStatus Client::signalUnsubscribe(VsmSubscriptionId id) +{ + try { + mConnection->signalUnsubscribe(id); mStatus = Status(); } catch (const std::exception& ex) { mStatus = Status(toStatus(ex), ex.what()); @@ -362,7 +377,8 @@ VsmStatus Client::vsm_create_domain(const char* id) noexcept } VsmStatus Client::vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, - void* data) noexcept + void* data, + VsmSubscriptionId* subscriptionId) noexcept { assert(containerDbusStateCallback); @@ -376,7 +392,13 @@ VsmStatus Client::vsm_add_state_callback(VsmContainerDbusStateCallback container return signalSubscribe(HOST_INTERFACE, api::host::SIGNAL_CONTAINER_DBUS_STATE, - onSigal); + onSigal, + subscriptionId); +} + +VsmStatus Client::vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexcept +{ + return signalUnsubscribe(subscriptionId); } VsmStatus Client::vsm_notify_active_container(const char* application, const char* message) noexcept @@ -417,7 +439,9 @@ VsmStatus Client::vsm_file_move_request(const char* destContainer, const char* p return ret; } -VsmStatus Client::vsm_notification(VsmNotificationCallback notificationCallback, void* data) noexcept +VsmStatus Client::vsm_add_notification_callback(VsmNotificationCallback notificationCallback, + void* data, + VsmSubscriptionId* subscriptionId) noexcept { assert(notificationCallback); @@ -429,5 +453,13 @@ VsmStatus Client::vsm_notification(VsmNotificationCallback notificationCallback, notificationCallback(container, application, message, data); }; - return signalSubscribe(CONTAINER_INTERFACE, api::container::SIGNAL_NOTIFICATION, onSigal); + return signalSubscribe(CONTAINER_INTERFACE, + api::container::SIGNAL_NOTIFICATION, + onSigal, + subscriptionId); +} + +VsmStatus Client::vsm_del_notification_callback(VsmSubscriptionId subscriptionId) noexcept +{ + return signalUnsubscribe(subscriptionId); } diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index 9a233e5..e88da49 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -70,7 +70,9 @@ private: GVariant** args_out = NULL); VsmStatus signalSubscribe(const DbusInterfaceInfo& info, const std::string& name, - SignalCallback signalCallback); + SignalCallback signalCallback, + VsmSubscriptionId* subscriptionId); + VsmStatus signalUnsubscribe(VsmSubscriptionId id); public: Client() noexcept; @@ -135,7 +137,13 @@ public: * @see ::vsm_add_state_callback */ VsmStatus vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, - void* data) noexcept; + void* data, + VsmSubscriptionId* subscriptionId) noexcept; + + /** + * @see ::vsm_del_state_callback + */ + VsmStatus vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexcept; /** * @see ::vsm_notify_active_container @@ -146,10 +154,19 @@ public: * @see ::vsm_file_move_request */ VsmStatus vsm_file_move_request(const char* destContainer, const char* path) noexcept; + /** - * @see ::vsm_notification + * @see ::vsm_add_notification_callback */ - VsmStatus vsm_notification(VsmNotificationCallback notificationCallback, void* data) noexcept; + VsmStatus vsm_add_notification_callback(VsmNotificationCallback notificationCallback, + void* data, + VsmSubscriptionId* subscriptionId) noexcept; + + /** + * @see ::vsm_del_notification_callback + */ + VsmStatus vsm_del_notification_callback(VsmSubscriptionId subscriptionId) noexcept; + /** * @see ::vsm_start_glib_loop */ diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index ad8fb38..2adc702 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -138,9 +138,15 @@ API VsmStatus vsm_create_domain(VsmClient client, const char* id) API VsmStatus vsm_add_state_callback(VsmClient client, VsmContainerDbusStateCallback containerDbusStateCallback, - void* data) + void* data, + VsmSubscriptionId* subscriptionId) { - return getClient(client).vsm_add_state_callback(containerDbusStateCallback, data); + return getClient(client).vsm_add_state_callback(containerDbusStateCallback, data, subscriptionId); +} + +API VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscriptionId) +{ + return getClient(client).vsm_del_state_callback(subscriptionId); } API VsmStatus vsm_notify_active_container(VsmClient client, @@ -155,9 +161,16 @@ API VsmStatus vsm_file_move_request(VsmClient client, const char* destContainer, return getClient(client).vsm_file_move_request(destContainer, path); } -API VsmStatus vsm_notification(VsmClient client, - VsmNotificationCallback notificationCallback, - void* data) +API VsmStatus vsm_add_notification_callback(VsmClient client, + VsmNotificationCallback notificationCallback, + void* data, + VsmSubscriptionId* subscriptionId) +{ + return getClient(client).vsm_add_notification_callback(notificationCallback, data, subscriptionId); +} + +API VsmStatus vsm_del_notification_callback(VsmClient client, + VsmSubscriptionId subscriptionId) { - return getClient(client).vsm_notification(notificationCallback, data); + return getClient(client).vsm_del_notification_callback(subscriptionId); } diff --git a/client/security-containers-client.h b/client/security-containers-client.h index 710af99..22a2a4e 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -115,6 +115,11 @@ typedef enum { } VsmStatus; /** + * Subscription id + */ +typedef unsigned int VsmSubscriptionId; + +/** * Start glib loop. * * Do not call this function if an application creates a glib loop itself. @@ -283,11 +288,24 @@ VsmStatus vsm_create_domain(VsmClient client, const char* id); * @param[in] client security-containers-server's client * @param[in] containerDbusStateCallback callback function * @param[in] data some extra data that will be passed to callback function + * @param[out] subscriptionId subscription identifier that can be used to unsubscribe signal, + * pointer can be NULL. * @return status of this function call */ VsmStatus vsm_add_state_callback(VsmClient client, VsmContainerDbusStateCallback containerDbusStateCallback, - void* data); + void* data, + VsmSubscriptionId* subscriptionId); + +/** + * Unregister dbus state change callback function. + * + * @param[in] client security-containers-server's client + * @param[in] subscriptionId subscription identifier returned by vsm_add_state_callback + * @return status of this function call + */ +VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscriptionId); + /** @} */ // Host API @@ -306,7 +324,7 @@ VsmStatus vsm_add_state_callback(VsmClient client, * @param[in] container source container * @param[in] application sending application name * @param[in] message notification message - * @param data custom user's data pointer passed to vsm_notification() + * @param data custom user's data pointer passed to vsm_add_notification_callback() */ typedef void (*VsmNotificationCallback)(const char* container, const char* application, @@ -340,9 +358,23 @@ VsmStatus vsm_file_move_request(VsmClient client, const char* destContainer, con * @param[in] client security-containers-server's client * @param[in] notificationCallback callback function * @param[in] data some extra data that will be passed to callback function + * @param[out] subscriptionId subscription identifier that can be used to unsubscribe signal, + * pointer can be NULL. + * @return status of this function call + */ +VsmStatus vsm_add_notification_callback(VsmClient client, + VsmNotificationCallback notificationCallback, + void* data, + VsmSubscriptionId* subscriptionId); + +/** + * Unregister notification callback function. + * + * @param[in] client security-containers-server's client + * @param[in] subscriptionId subscription identifier returned by vsm_add_notification_callback * @return status of this function call */ -VsmStatus vsm_notification(VsmClient client, VsmNotificationCallback notificationCallback, void* data); +VsmStatus vsm_del_notification_callback(VsmClient client, VsmSubscriptionId subscriptionId); /** @} */ // Domain API diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 184659e..f26dbf3 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -256,7 +256,10 @@ BOOST_AUTO_TEST_CASE(NotificationTest) clients[it.first] = client; } for (auto& client : clients) { - VsmStatus status = vsm_notification(client.second, callback, &callbackData); + VsmStatus status = vsm_add_notification_callback(client.second, + callback, + &callbackData, + NULL); BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); } for (auto& client : clients) { -- 2.7.4 From b25dcc4c14d972123e109517585ff36989d05d1a Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 3 Nov 2014 08:52:00 +0200 Subject: [PATCH 12/16] IPC via UX sockets [Bug/Feature] IPC for communication between the library and daemon [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I9880c7b4f3104b93f38d0e6ad86762fb17013d28 --- common/ipc/client.cpp | 80 +++++++ common/ipc/client.hpp | 156 +++++++++++++ common/ipc/exception.hpp | 45 ++++ common/ipc/internals/acceptor.cpp | 131 +++++++++++ common/ipc/internals/acceptor.hpp | 87 +++++++ common/ipc/internals/event-queue.hpp | 112 +++++++++ common/ipc/internals/eventfd.cpp | 77 +++++++ common/ipc/internals/eventfd.hpp | 62 +++++ common/ipc/internals/processor.cpp | 434 +++++++++++++++++++++++++++++++++++ common/ipc/internals/processor.hpp | 418 +++++++++++++++++++++++++++++++++ common/ipc/internals/socket.cpp | 200 ++++++++++++++++ common/ipc/internals/socket.hpp | 116 ++++++++++ common/ipc/internals/utils.cpp | 134 +++++++++++ common/ipc/internals/utils.hpp | 77 +++++++ common/ipc/service.cpp | 86 +++++++ common/ipc/service.hpp | 168 ++++++++++++++ common/ipc/types.hpp | 48 ++++ packaging/security-containers.spec | 1 + server/CMakeLists.txt | 2 +- tests/unit_tests/CMakeLists.txt | 3 +- tests/unit_tests/ipc/ut-ipc.cpp | 391 +++++++++++++++++++++++++++++++ 21 files changed, 2826 insertions(+), 2 deletions(-) create mode 100644 common/ipc/client.cpp create mode 100644 common/ipc/client.hpp create mode 100644 common/ipc/exception.hpp create mode 100644 common/ipc/internals/acceptor.cpp create mode 100644 common/ipc/internals/acceptor.hpp create mode 100644 common/ipc/internals/event-queue.hpp create mode 100644 common/ipc/internals/eventfd.cpp create mode 100644 common/ipc/internals/eventfd.hpp create mode 100644 common/ipc/internals/processor.cpp create mode 100644 common/ipc/internals/processor.hpp create mode 100644 common/ipc/internals/socket.cpp create mode 100644 common/ipc/internals/socket.hpp create mode 100644 common/ipc/internals/utils.cpp create mode 100644 common/ipc/internals/utils.hpp create mode 100644 common/ipc/service.cpp create mode 100644 common/ipc/service.hpp create mode 100644 common/ipc/types.hpp create mode 100644 tests/unit_tests/ipc/ut-ipc.cpp diff --git a/common/ipc/client.cpp b/common/ipc/client.cpp new file mode 100644 index 0000000..c1651f3 --- /dev/null +++ b/common/ipc/client.cpp @@ -0,0 +1,80 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 "ipc/client.hpp" +#include "ipc/internals/socket.hpp" +#include "ipc/exception.hpp" + +namespace security_containers { +namespace ipc { + +Client::Client(const std::string& socketPath) + : mSocketPath(socketPath) +{ + LOGD("Creating client"); +} + +Client::~Client() +{ + LOGD("Destroying client..."); + try { + stop(); + } catch (IPCException& e) { + LOGE("Error in Client's destructor: " << e.what()); + } + LOGD("Destroyed client"); +} + +void Client::start() +{ + LOGD("Starting client..."); + + // Initialize the connection with the server + LOGD("Connecting to " + mSocketPath); + auto socketPtr = std::make_shared(Socket::connectSocket(mSocketPath)); + mServiceID = mProcessor.addPeer(socketPtr); + + // Start listening + mProcessor.start(); + + LOGD("Started client"); +} + +void Client::stop() +{ + LOGD("Stopping client..."); + mProcessor.stop(); + LOGD("Stopped"); +} + +void Client::removeMethod(const MethodID methodID) +{ + LOGD("Removing method id: " << methodID); + mProcessor.removeMethod(methodID); +} + +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/client.hpp b/common/ipc/client.hpp new file mode 100644 index 0000000..7429b24 --- /dev/null +++ b/common/ipc/client.hpp @@ -0,0 +1,156 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 + */ + +#ifndef COMMON_IPC_CLIENT_HPP +#define COMMON_IPC_CLIENT_HPP + +#include "ipc/internals/processor.hpp" +#include "ipc/types.hpp" +#include "logger/logger.hpp" + +#include + +namespace security_containers { +namespace ipc { + +/** + * This class wraps communication via UX sockets for client applications. + * It uses serialization mechanism from libConfig. + * + * There is one additional thread: + * - PROCESSOR is responsible for the communication and calling the callbacks + * + * For message format @see ipc::Processor + */ +class Client { +public: + typedef Processor::MethodID MethodID; + + /** + * @param serverPath path to the server's socket + */ + Client(const std::string& serverPath); + ~Client(); + + Client(const Client&) = delete; + Client& operator=(const Client&) = delete; + + /** + * Starts the worker thread + */ + void start(); + + /** + * Stops all worker thread + */ + void stop(); + + /** + * 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 methodCallback method handling implementation + */ + template + void addMethodHandler(const MethodID methodID, + const typename MethodHandler::type& method); + + /** + * Removes the callback + * + * @param methodID API dependent id of the method + */ + void removeMethod(const MethodID methodID); + + /** + * Synchronous method call. + * + * @param methodID API dependent id of the method + * @param data data to send + * @param timeoutMS how long to wait for the return value before throw + * @return result data + */ + template + std::shared_ptr callSync(const MethodID methodID, + const std::shared_ptr& data, + unsigned int timeoutMS = 500); + + /** + * 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 sendCallback callback for data serialization + * @param resultCallback callback for result serialization and handling + */ + template + void callAsync(const MethodID methodID, + const std::shared_ptr& data, + const typename ResultHandler::type& resultCallback); + +private: + Processor::PeerID mServiceID; + Processor mProcessor; + std::string mSocketPath; +}; + +template +void Client::addMethodHandler(const MethodID methodID, + const typename MethodHandler::type& method) +{ + LOGD("Adding method with id " << methodID); + mProcessor.addMethodHandler(methodID, method); + LOGD("Added method with id " << methodID); +} + +template +std::shared_ptr Client::callSync(const MethodID methodID, + const std::shared_ptr& data, + unsigned int timeoutMS) +{ + LOGD("Sync calling method: " << methodID); + return mProcessor.callSync(methodID, mServiceID, data, timeoutMS); +} + +template +void Client::callAsync(const MethodID methodID, + const std::shared_ptr& data, + const typename ResultHandler::type& resultCallback) +{ + LOGD("Async calling method: " << methodID); + mProcessor.callAsync(methodID, + mServiceID, + data, + resultCallback); + LOGD("Async called method: " << methodID); +} + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_CLIENT_HPP diff --git a/common/ipc/exception.hpp b/common/ipc/exception.hpp new file mode 100644 index 0000000..67d9c86 --- /dev/null +++ b/common/ipc/exception.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR 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 COMMON_IPC_EXCEPTION_HPP +#define COMMON_IPC_EXCEPTION_HPP + +#include "base-exception.hpp" + +namespace security_containers { + + +/** + * Base class for exceptions in IPC + */ +struct IPCException: public SecurityContainersException { + IPCException(const std::string& error) : SecurityContainersException(error) {} +}; + + +} + + +#endif // COMMON_IPC_EXCEPTION_HPP diff --git a/common/ipc/internals/acceptor.cpp b/common/ipc/internals/acceptor.cpp new file mode 100644 index 0000000..9738546 --- /dev/null +++ b/common/ipc/internals/acceptor.cpp @@ -0,0 +1,131 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 "ipc/exception.hpp" +#include "ipc/internals/utils.hpp" +#include "ipc/internals/acceptor.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include +#include + +namespace security_containers { +namespace ipc { + +Acceptor::Acceptor(const std::string& socketPath, const NewConnectionCallback& newConnectionCallback) + : mNewConnectionCallback(newConnectionCallback), + mSocket(Socket::createSocket(socketPath)) +{ + LOGT("Creating Acceptor for socket " << socketPath); +} + +Acceptor::~Acceptor() +{ + LOGT("Destroying Acceptor"); + try { + stop(); + } catch (IPCException& e) { + LOGE("Error in destructor: " << e.what()); + } + LOGT("Destroyed Acceptor"); +} + +void Acceptor::start() +{ + LOGT("Starting Acceptor"); + if (!mThread.joinable()) { + mThread = std::thread(&Acceptor::run, this); + } + LOGT("Started Acceptor"); +} + +void Acceptor::stop() +{ + LOGT("Stopping Acceptor"); + if (mThread.joinable()) { + LOGT("Event::FINISH -> Acceptor"); + mEventQueue.send(Event::FINISH); + LOGT("Waiting for Acceptor to finish"); + mThread.join(); + } + LOGT("Stopped Acceptor"); +} + +void Acceptor::run() +{ + // Setup polling structure + std::vector fds(2); + + fds[0].fd = mEventQueue.getFD(); + fds[0].events = POLLIN; + + fds[1].fd = mSocket.getFD(); + fds[1].events = POLLIN; + + // Main loop + bool isRunning = true; + while (isRunning) { + LOGT("Waiting for new connections..."); + + int ret = ::poll(fds.data(), fds.size(), -1 /*blocking call*/); + + LOGT("...Incoming connection!"); + + if (ret == -1 || ret == 0) { + if (errno == EINTR) { + continue; + } + LOGE("Error in poll: " << std::string(strerror(errno))); + throw IPCException("Error in poll: " + std::string(strerror(errno))); + break; + } + + // Check for incoming connections + if (fds[1].revents & POLLIN) { + fds[1].revents = 0; + std::shared_ptr tmpSocket = mSocket.accept(); + mNewConnectionCallback(tmpSocket); + } + + // Check for incoming events + if (fds[0].revents & POLLIN) { + fds[0].revents = 0; + + if (mEventQueue.receive() == Event::FINISH) { + LOGD("Event FINISH"); + isRunning = false; + break; + } + } + } + LOGT("Exiting run"); +} + +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/internals/acceptor.hpp b/common/ipc/internals/acceptor.hpp new file mode 100644 index 0000000..b863400 --- /dev/null +++ b/common/ipc/internals/acceptor.hpp @@ -0,0 +1,87 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 COMMON_IPC_INTERNALS_ACCEPTOR_HPP +#define COMMON_IPC_INTERNALS_ACCEPTOR_HPP + +#include "config.hpp" + +#include "ipc/internals/socket.hpp" +#include "ipc/internals/event-queue.hpp" + +#include +#include + +namespace security_containers { +namespace ipc { + +/** + * Accepts new connections and passes the new socket to a callback. + */ +class Acceptor { +public: + + typedef std::function& socketPtr)> NewConnectionCallback; + + /** + * Class for accepting new connections. + * + * @param socketPath path to the socket + * @param newConnectionCallback called on new connections + */ + Acceptor(const std::string& socketPath, + const NewConnectionCallback& newConnectionCallback); + ~Acceptor(); + + Acceptor(const Acceptor& acceptor) = delete; + Acceptor& operator=(const Acceptor&) = delete; + + /** + * Starts the thread accepting the new connections. + */ + void start(); + + /** + * Stops the accepting thread. + */ + void stop(); + +private: + enum class Event : int { + FINISH // Shutdown request + }; + + NewConnectionCallback mNewConnectionCallback; + Socket mSocket; + + EventQueue mEventQueue; + std::thread mThread; + + void run(); +}; + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_INTERNALS_ACCEPTOR_HPP diff --git a/common/ipc/internals/event-queue.hpp b/common/ipc/internals/event-queue.hpp new file mode 100644 index 0000000..82cb2ff --- /dev/null +++ b/common/ipc/internals/event-queue.hpp @@ -0,0 +1,112 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 COMMON_IPC_INTERNALS_EVENT_QUEUE_HPP +#define COMMON_IPC_INTERNALS_EVENT_QUEUE_HPP + +#include "ipc/exception.hpp" +#include "ipc/internals/eventfd.hpp" +#include "logger/logger.hpp" + +#include +#include +#include + +namespace security_containers { +namespace ipc { + + +/** + * 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 +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 of a given value + * + * @param value size of the buffer + */ + void send(const MessageType& mess); + + /** + * Receives the signal. + * Blocks if there is no event. + * + * @return event's value + */ + MessageType receive(); + +private: + typedef std::lock_guard Lock; + + std::mutex mCommunicationMutex; + std::queue mMessages; + + EventFD mEventFD; +}; + +template +int EventQueue::getFD() const +{ + return mEventFD.getFD(); +} + +template +void EventQueue::send(const MessageType& mess) +{ + Lock lock(mCommunicationMutex); + LOGT("Sending event"); + mMessages.push(mess); + mEventFD.send(); +} + +template +MessageType EventQueue::receive() +{ + Lock lock(mCommunicationMutex); + mEventFD.receive(); + LOGT("Received event"); + MessageType mess = mMessages.front(); + mMessages.pop(); + return mess; +} + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_INTERNALS_EVENT_QUEUE_HPP diff --git a/common/ipc/internals/eventfd.cpp b/common/ipc/internals/eventfd.cpp new file mode 100644 index 0000000..c8a17b6 --- /dev/null +++ b/common/ipc/internals/eventfd.cpp @@ -0,0 +1,77 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 "ipc/internals/eventfd.hpp" +#include "ipc/internals/utils.hpp" +#include "ipc/exception.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include + +namespace security_containers { +namespace ipc { + +EventFD::EventFD() +{ + mFD = ::eventfd(0, EFD_SEMAPHORE); + if (mFD == -1) { + LOGE("Error in eventfd: " << std::string(strerror(errno))); + throw IPCException("Error in eventfd: " + std::string(strerror(errno))); + } +} + +EventFD::~EventFD() +{ + try { + ipc::close(mFD); + } catch (IPCException& e) { + LOGE("Error in Event's destructor: " << e.what()); + } +} + +int EventFD::getFD() const +{ + return mFD; +} + +void EventFD::send() +{ + const std::uint64_t toSend = 1; + ipc::write(mFD, &toSend, sizeof(toSend)); +} + +void EventFD::receive() +{ + std::uint64_t readBuffer; + ipc::read(mFD, &readBuffer, sizeof(readBuffer)); +} + + +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/internals/eventfd.hpp b/common/ipc/internals/eventfd.hpp new file mode 100644 index 0000000..9de6f17 --- /dev/null +++ b/common/ipc/internals/eventfd.hpp @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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_IPC_INTERNALS_EVENTFD_HPP +#define COMMON_IPC_INTERNALS_EVENTFD_HPP + +namespace security_containers { +namespace ipc { + +class EventFD { +public: + + EventFD(); + ~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 ipc +} // namespace security_containers + +#endif // COMMON_IPC_INTERNALS_EVENTFD_HPP diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp new file mode 100644 index 0000000..0465574 --- /dev/null +++ b/common/ipc/internals/processor.cpp @@ -0,0 +1,434 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 "ipc/exception.hpp" +#include "ipc/internals/processor.hpp" +#include "ipc/internals/utils.hpp" + +#include +#include +#include +#include + +#include +#include + + +namespace security_containers { +namespace ipc { + +const Processor::MethodID Processor::RETURN_METHOD_ID = std::numeric_limits::max(); + +Processor::Processor(const PeerCallback& newPeerCallback, + const PeerCallback& removedPeerCallback, + const unsigned int maxNumberOfPeers) + : mNewPeerCallback(newPeerCallback), + mRemovedPeerCallback(removedPeerCallback), + mMaxNumberOfPeers(maxNumberOfPeers), + mMessageIDCounter(0), + mPeerIDCounter(0) +{ + LOGT("Creating Processor"); +} + +Processor::~Processor() +{ + LOGT("Destroying Processor"); + try { + stop(); + } catch (IPCException& e) { + LOGE("Error in Processor's destructor: " << e.what()); + } + LOGT("Destroyed Processor"); +} + +void Processor::start() +{ + LOGT("Starting Processor"); + if (!mThread.joinable()) { + mThread = std::thread(&Processor::run, this); + } + LOGT("Started Processor"); +} + +void Processor::stop() +{ + LOGT("Stopping Processor"); + if (mThread.joinable()) { + mEventQueue.send(Event::FINISH); + mThread.join(); + } + LOGT("Stopped Processor"); +} + +void Processor::removeMethod(const MethodID methodID) +{ + LOGT("Removing method " << methodID); + Lock lock(mCallsMutex); + mMethodsCallbacks.erase(methodID); +} + +Processor::PeerID Processor::addPeer(const std::shared_ptr& socketPtr) +{ + LOGT("Adding socket"); + PeerID peerID; + { + Lock lock(mSocketsMutex); + peerID = getNextPeerID(); + SocketInfo socketInfo; + socketInfo.peerID = peerID; + socketInfo.socketPtr = std::move(socketPtr); + mNewSockets.push(std::move(socketInfo)); + } + LOGI("New peer added. Id: " << peerID); + mEventQueue.send(Event::NEW_PEER); + + return peerID; +} + +void Processor::removePeer(PeerID peerID) +{ + LOGW("Removing naughty peer. ID: " << peerID); + { + Lock lock(mSocketsMutex); + mSockets.erase(peerID); + } + resetPolling(); +} + +void Processor::resetPolling() +{ + LOGI("Resetting polling"); + // Setup polling on eventfd and sockets + Lock lock(mSocketsMutex); + mFDs.resize(mSockets.size() + 1); + + mFDs[0].fd = mEventQueue.getFD(); + mFDs[0].events = POLLIN; + + auto socketIt = mSockets.begin(); + for (unsigned int i = 1; i < mFDs.size(); ++i) { + mFDs[i].fd = socketIt->second->getFD(); + mFDs[i].events = POLLIN | POLLHUP; // Listen for input events + ++socketIt; + // TODO: It's possible to block on writing to fd. Maybe listen for POLLOUT too? + } +} + +void Processor::run() +{ + resetPolling(); + + mIsRunning = true; + while (mIsRunning) { + LOGT("Waiting for communication..."); + int ret = poll(mFDs.data(), mFDs.size(), -1 /*blocking call*/); + LOGT("... incoming communication!"); + if (ret == -1 || ret == 0) { + if (errno == EINTR) { + continue; + } + LOGE("Error in poll: " << std::string(strerror(errno))); + throw IPCException("Error in poll: " + std::string(strerror(errno))); + } + + // Check for lost connections: + if (handleLostConnections()) { + // mFDs changed + continue; + } + + // Check for incoming data. + if (handleInputs()) { + // mFDs changed + continue; + } + + // Check for incoming events + if (handleEvent()) { + // mFDs changed + continue; + } + } +} + +bool Processor::handleLostConnections() +{ + std::list peersToRemove; + + { + Lock lock(mSocketsMutex); + auto socketIt = mSockets.begin(); + for (unsigned int i = 1; i < mFDs.size(); ++i, ++socketIt) { + if (mFDs[i].revents & POLLHUP) { + LOGI("Lost connection to peer: " << socketIt->first); + mFDs[i].revents &= ~(POLLHUP); + peersToRemove.push_back(socketIt->first); + } + } + + for (const auto peerID : peersToRemove) { + LOGT("Removing peer. ID: " << peerID); + mSockets.erase(peerID); + } + } + + if (!peersToRemove.empty()) { + resetPolling(); + } + + return !peersToRemove.empty(); +} + +bool Processor::handleInputs() +{ + std::list> > peersWithInput; + { + Lock lock(mSocketsMutex); + auto socketIt = mSockets.begin(); + for (unsigned int i = 1; i < mFDs.size(); ++i, ++socketIt) { + if (mFDs[i].revents & POLLIN) { + mFDs[i].revents &= ~(POLLIN); + peersWithInput.push_back(*socketIt); + } + } + } + + bool pollChanged = false; + // Handle input outside the critical section + for (const auto& peer : peersWithInput) { + pollChanged = pollChanged || handleInput(peer.first, *peer.second); + } + return pollChanged; +} + +bool Processor::handleInput(const PeerID peerID, const Socket& socket) +{ + LOGT("Handle incoming data"); + MethodID methodID; + MessageID messageID; + { + LOGI("Locking"); + Socket::Guard guard = socket.getGuard(); + socket.read(&methodID, sizeof(methodID)); + socket.read(&messageID, sizeof(messageID)); + LOGI("Locked"); + + if (methodID == RETURN_METHOD_ID) { + LOGI("Return value for messageID: " << messageID); + ReturnCallbacks returnCallbacks; + try { + Lock lock(mReturnCallbacksMutex); + LOGT("Getting the return callback"); + returnCallbacks = std::move(mReturnCallbacks.at(messageID)); + mReturnCallbacks.erase(messageID); + } catch (const std::out_of_range&) { + LOGW("No return callback for messageID: " << messageID); + return false; + } + + std::shared_ptr data; + try { + LOGT("Parsing incoming return data"); + data = returnCallbacks.parse(socket.getFD()); + } catch (const IPCException&) { + removePeer(peerID); + return true; + } + + guard.unlock(); + + LOGT("Process callback for methodID: " << methodID << "; messageID: " << messageID); + returnCallbacks.process(data); + + } else { + LOGI("Remote call; methodID: " << methodID << " messageID: " << messageID); + std::shared_ptr methodCallbacks; + try { + Lock lock(mCallsMutex); + methodCallbacks = mMethodsCallbacks.at(methodID); + } catch (const std::out_of_range&) { + LOGW("No method callback for methodID: " << methodID); + removePeer(peerID); + return true; + } + + std::shared_ptr data; + try { + LOGT("Parsing incoming data"); + data = methodCallbacks->parse(socket.getFD()); + } catch (const IPCException&) { + removePeer(peerID); + return true; + } + + guard.unlock(); + + LOGT("Process callback for methodID: " << methodID << "; messageID: " << messageID); + std::shared_ptr returnData = methodCallbacks->method(data); + + LOGT("Sending return data; methodID: " << methodID << "; messageID: " << messageID); + try { + // Send the call with the socket + Socket::Guard guard = socket.getGuard(); + socket.write(&RETURN_METHOD_ID, sizeof(RETURN_METHOD_ID)); + socket.write(&messageID, sizeof(messageID)); + methodCallbacks->serialize(socket.getFD(), returnData); + } catch (const IPCException&) { + removePeer(peerID); + return true; + } + } + } + + return false; +} + +bool Processor::handleEvent() +{ + if (!(mFDs[0].revents & POLLIN)) { + // No event to serve + return false; + } + + mFDs[0].revents &= ~(POLLIN); + + switch (mEventQueue.receive()) { + case Event::FINISH: { + LOGD("Event FINISH"); + mIsRunning = false; + return false; + } + + case Event::CALL: { + LOGD("Event CALL"); + handleCall(); + return false; + } + + case Event::NEW_PEER: { + LOGD("Event NEW_PEER"); + SocketInfo socketInfo; + { + Lock lock(mSocketsMutex); + + socketInfo = std::move(mNewSockets.front()); + mNewSockets.pop(); + + if (mSockets.size() > mMaxNumberOfPeers) { + LOGE("There are too many peers. I don't accept the connection with " << socketInfo.peerID); + + } + if (mSockets.count(socketInfo.peerID) != 0) { + LOGE("There already was a socket for peerID: " << socketInfo.peerID); + } + mSockets[socketInfo.peerID] = socketInfo.socketPtr; + } + resetPolling(); + + if (mNewPeerCallback) { + // Notify about the new user. + mNewPeerCallback(socketInfo.peerID); + } + return true; + } + } + + return false; +} + +Processor::MessageID Processor::getNextMessageID() +{ + // TODO: This method of generating UIDs is buggy. To be changed. + return ++mMessageIDCounter; +} + +Processor::PeerID Processor::getNextPeerID() +{ + // TODO: This method of generating UIDs is buggy. To be changed. + return ++mPeerIDCounter; +} + +Processor::Call Processor::getCall() +{ + Lock lock(mCallsMutex); + if (mCalls.empty()) { + LOGE("Calls queue empty"); + throw IPCException("Calls queue empty"); + } + Call call = std::move(mCalls.front()); + mCalls.pop(); + return call; +} + +void Processor::handleCall() +{ + LOGT("Handle call from another thread"); + Call call = getCall(); + + ReturnCallbacks returnCallbacks; + returnCallbacks.parse = call.parse; + returnCallbacks.process = call.process; + + std::shared_ptr socketPtr; + try { + // Get the addressee's socket + Lock lock(mSocketsMutex); + socketPtr = mSockets.at(call.peerID); + } catch (const std::out_of_range&) { + LOGE("Peer disconnected. No socket with a peerID: " << call.peerID); + return; + } + + MessageID messageID = getNextMessageID(); + + { + // Set what to do with the return message + Lock lock(mReturnCallbacksMutex); + if (mReturnCallbacks.count(messageID) != 0) { + LOGE("There already was a return callback for messageID: " << messageID); + } + mReturnCallbacks[messageID] = std::move(returnCallbacks); + } + + try { + // Send the call with the socket + Socket::Guard guard = socketPtr->getGuard(); + socketPtr->write(&call.methodID, sizeof(call.methodID)); + socketPtr->write(&messageID, sizeof(messageID)); + call.serialize(socketPtr->getFD(), call.data); + } catch (const std::exception& e) { + LOGE("Error during sending a message: " << e.what()); + { + Lock lock(mReturnCallbacksMutex); + mReturnCallbacks.erase(messageID); + } + // TODO: User should get the error code. + } +} + +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp new file mode 100644 index 0000000..cb14a67 --- /dev/null +++ b/common/ipc/internals/processor.hpp @@ -0,0 +1,418 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 COMMON_IPC_INTERNALS_PROCESSOR_HPP +#define COMMON_IPC_INTERNALS_PROCESSOR_HPP + +#include "ipc/internals/socket.hpp" +#include "ipc/internals/event-queue.hpp" +#include "ipc/exception.hpp" +#include "ipc/types.hpp" +#include "config/manager.hpp" +#include "config/is-visitable.hpp" +#include "logger/logger.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace security_containers { +namespace ipc { +namespace { +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 libConfig. +* 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::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: +* - error codes passed to async callbacks +* - remove ReturnCallbacks on peer disconnect +* - on sync timeout erase the return callback +* - don't throw timeout if the message is already processed +* - naming convention or methods that just commissions the PROCESS thread to do something +* - removePeer API function +*/ +class Processor { +public: + typedef std::function PeerCallback; + typedef unsigned int PeerID; + typedef unsigned int MethodID; + + /** + * Method ID. Used to indicate a message with the return value. + */ + static const MethodID RETURN_METHOD_ID; + /** + * Constructs the Processor, but doesn't start it. + * The object is ready to add methods. + * + * @param newPeerCallback called when a new peer arrives + * @param removedPeerCallback called when the Processor stops listening for this peer + */ + Processor(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 the processing thread. + * Quits immediately after starting the thread. + */ + void start(); + + /** + * Stops the processing thread. + * No incoming data will be handled after. + */ + void stop(); + + /** + * 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 socket + */ + PeerID addPeer(const std::shared_ptr& 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 + void addMethodHandler(const MethodID methodID, + const typename MethodHandler::type& process); + + /** + * Removes the callback + * + * @param methodID API dependent id of the method + */ + void removeMethod(const MethodID methodID); + + /** + * Synchronous method call. + * + * @param methodID API dependent id of the method + * @param peerID id of the peer + * @param data data to sent + * @param timeoutMS how long to wait for the return value before throw + * @tparam SentDataType data type to send + * @tparam ReceivedDataType data type to receive + */ + template + std::shared_ptr callSync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + unsigned int timeoutMS = 500); + + /** + * 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 + void callAsync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process); + + +private: + typedef std::function& data)> SerializeCallback; + typedef std::function(int fd)> ParseCallback; + typedef std::lock_guard Lock; + typedef unsigned int MessageID; + + struct Call { + Call(const Call& other) = delete; + Call& operator=(const Call&) = delete; + Call() = default; + Call(Call&&) = default; + + PeerID peerID; + MethodID methodID; + std::shared_ptr data; + SerializeCallback serialize; + ParseCallback parse; + ResultHandler::type process; + }; + + 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::type method; + }; + + struct ReturnCallbacks { + ReturnCallbacks(const ReturnCallbacks& other) = delete; + ReturnCallbacks& operator=(const ReturnCallbacks&) = delete; + ReturnCallbacks() = default; + ReturnCallbacks(ReturnCallbacks&&) = default; + ReturnCallbacks& operator=(ReturnCallbacks &&) = default; + + ParseCallback parse; + ResultHandler::type process; + }; + + struct SocketInfo { + SocketInfo(const SocketInfo& other) = delete; + SocketInfo& operator=(const SocketInfo&) = delete; + SocketInfo() = default; + SocketInfo(SocketInfo&&) = default; + SocketInfo& operator=(SocketInfo &&) = default; + + std::shared_ptr socketPtr; + PeerID peerID; + }; + + enum class Event : int { + FINISH, // Shutdown request + CALL, // New method call in the queue + NEW_PEER // New peer in the queue + }; + EventQueue mEventQueue; + + + bool mIsRunning; + + // Mutex for the Calls queue and the map of methods. + std::mutex mCallsMutex; + std::queue mCalls; + std::unordered_map> mMethodsCallbacks; + + // Mutex for changing mSockets map. + // Shouldn't be locked on any read/write, that could block. Just copy the ptr. + std::mutex mSocketsMutex; + std::unordered_map > mSockets; + std::queue mNewSockets; + + // Mutex for modifying the map with return callbacks + std::mutex mReturnCallbacksMutex; + std::unordered_map mReturnCallbacks; + + + PeerCallback mNewPeerCallback; + PeerCallback mRemovedPeerCallback; + + unsigned int mMaxNumberOfPeers; + + std::thread mThread; + std::vector mFDs; + + std::atomic mMessageIDCounter; + std::atomic mPeerIDCounter; + + void run(); + bool handleEvent(); + void handleCall(); + bool handleLostConnections(); + bool handleInputs(); + bool handleInput(const PeerID peerID, const Socket& socket); + void resetPolling(); + MessageID getNextMessageID(); + PeerID getNextPeerID(); + Call getCall(); + void removePeer(PeerID peerID); + +}; + +template +void Processor::addMethodHandler(const MethodID methodID, + const typename MethodHandler::type& method) +{ + static_assert(config::isVisitable::value, + "Use the libConfig library"); + static_assert(config::isVisitable::value, + "Use the libConfig library"); + + if (methodID == RETURN_METHOD_ID) { + LOGE("Forbidden methodID: " << methodID); + throw IPCException("Forbidden methodID: " + std::to_string(methodID)); + } + + using namespace std::placeholders; + + MethodHandlers methodCall; + + methodCall.parse = [](const int fd)->std::shared_ptr { + std::shared_ptr data(new ReceivedDataType()); + config::loadFromFD(fd, *data); + return data; + }; + + methodCall.serialize = [](const int fd, std::shared_ptr& data)->void { + config::saveToFD(fd, *std::static_pointer_cast(data)); + }; + + methodCall.method = [method](std::shared_ptr& data)->std::shared_ptr { + std::shared_ptr tmpData = std::static_pointer_cast(data); + return method(tmpData); + }; + + { + Lock lock(mCallsMutex); + mMethodsCallbacks[methodID] = std::make_shared(std::move(methodCall)); + } +} + +template +void Processor::callAsync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& process) +{ + static_assert(config::isVisitable::value, + "Use the libConfig library"); + static_assert(config::isVisitable::value, + "Use the libConfig library"); + + if (!mThread.joinable()) { + LOGE("The Processor thread is not started. Can't send any data."); + throw IPCException("The Processor thread is not started. Can't send any data."); + } + + using namespace std::placeholders; + + Call call; + call.peerID = peerID; + call.methodID = methodID; + call.data = data; + + call.parse = [](const int fd)->std::shared_ptr { + std::shared_ptr data(new ReceivedDataType()); + config::loadFromFD(fd, *data); + return data; + }; + + call.serialize = [](const int fd, std::shared_ptr& data)->void { + config::saveToFD(fd, *std::static_pointer_cast(data)); + }; + + call.process = [process](std::shared_ptr& data)->void { + std::shared_ptr tmpData = std::static_pointer_cast(data); + return process(tmpData); + }; + + { + Lock lock(mCallsMutex); + mCalls.push(std::move(call)); + } + + mEventQueue.send(Event::CALL); +} + + +template +std::shared_ptr Processor::callSync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + unsigned int timeoutMS) +{ + static_assert(config::isVisitable::value, + "Use the libConfig library"); + static_assert(config::isVisitable::value, + "Use the libConfig library"); + + if (!mThread.joinable()) { + LOGE("The Processor thread is not started. Can't send any data."); + throw IPCException("The Processor thread is not started. Can't send any data."); + } + + std::shared_ptr result; + + std::mutex mtx; + std::unique_lock lck(mtx); + std::condition_variable cv; + + auto process = [&result, &cv](std::shared_ptr returnedData) { + result = returnedData; + cv.notify_one(); + }; + + callAsync(methodID, + peerID, + data, + process); + + auto isResultInitialized = [&result]() { + return static_cast(result); + }; + + if (!cv.wait_for(lck, std::chrono::milliseconds(timeoutMS), isResultInitialized)) { + LOGE("Function call timeout; methodID: " << methodID); + throw IPCException("Function call timeout; methodID: " + std::to_string(methodID)); + } + + return result; +} + + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_INTERNALS_PROCESSOR_HPP diff --git a/common/ipc/internals/socket.cpp b/common/ipc/internals/socket.cpp new file mode 100644 index 0000000..002b9cf --- /dev/null +++ b/common/ipc/internals/socket.cpp @@ -0,0 +1,200 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 "ipc/exception.hpp" +#include "ipc/internals/socket.hpp" +#include "ipc/internals/utils.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace security_containers { +namespace ipc { + +namespace { +const int MAX_QUEUE_LENGTH = 1000; +} + +Socket::Socket(int socketFD) + : mFD(socketFD) +{ +} + +Socket::Socket(Socket&& socket) + : mFD(socket.mFD) +{ + socket.mFD = -1; +} + +Socket::~Socket() +{ + try { + ipc::close(mFD); + } catch (IPCException& e) { + LOGE("Error in Socket's destructor: " << e.what()); + } +} + +Socket::Guard Socket::getGuard() const +{ + return Guard(mCommunicationMutex); +} + +int Socket::getFD() const +{ + return mFD; +} + +std::shared_ptr Socket::accept() +{ + int sockfd = ::accept(mFD, nullptr, nullptr); + if (sockfd == -1) { + LOGE("Error in accept: " << std::string(strerror(errno))); + IPCException("Error in accept: " + std::string(strerror(errno))); + } + return std::make_shared(sockfd); +} + +void Socket::write(const void* bufferPtr, const size_t size) const +{ + Guard guard(mCommunicationMutex); + ipc::write(mFD, bufferPtr, size); +} + +void Socket::read(void* bufferPtr, const size_t size) const +{ + Guard guard(mCommunicationMutex); + ipc::read(mFD, bufferPtr, size); +} + +int Socket::getSystemdSocket(const std::string& path) +{ + int n = ::sd_listen_fds(-1 /*Block further calls to sd_listen_fds*/); + if (n < 0) { + LOGE("sd_listen_fds fails with errno: " + n); + throw IPCException("sd_listen_fds fails with errno: " + n); + } + + for (int fd = SD_LISTEN_FDS_START; + fd < SD_LISTEN_FDS_START + n; + ++fd) { + if (0 < ::sd_is_socket_unix(SD_LISTEN_FDS_START, SOCK_STREAM, 1, path.c_str(), 0)) { + return fd; + } + } + LOGW("No usable sockets were passed by systemd."); + return -1; +} + +int Socket::createDomainSocket(const std::string& path) +{ + // Isn't the path too long? + if (path.size() >= sizeof(sockaddr_un::sun_path)) { + LOGE("Socket's path too long"); + throw IPCException("Socket's path too long"); + } + + int sockfd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd == -1) { + LOGE("Error in socket: " + std::string(strerror(errno))); + throw IPCException("Error in socket: " + std::string(strerror(errno))); + } + + ::sockaddr_un serverAddress; + serverAddress.sun_family = AF_UNIX; + ::strncpy(serverAddress.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path)); + unlink(serverAddress.sun_path); + + // Everybody can access the socket + // TODO: Use SMACK to guard the socket + if (-1 == ::bind(sockfd, + reinterpret_cast(&serverAddress), + sizeof(struct sockaddr_un))) { + std::string message = strerror(errno); + ::close(sockfd); + LOGE("Error in bind: " << message); + IPCException("Error in bind: " + message); + } + + if (-1 == ::listen(sockfd, + MAX_QUEUE_LENGTH)) { + std::string message = strerror(errno); + ::close(sockfd); + LOGE("Error in listen: " << message); + IPCException("Error in listen: " + message); + } + + return sockfd; +} + +Socket Socket::createSocket(const std::string& path) +{ + // Initialize a socket + int fd = getSystemdSocket(path); + fd = fd != -1 ? fd : createDomainSocket(path); + + return Socket(fd); +} + +Socket Socket::connectSocket(const std::string& path) +{ + // Isn't the path too long? + if (path.size() >= sizeof(sockaddr_un::sun_path)) { + LOGE("Socket's path too long"); + throw IPCException("Socket's path too long"); + } + + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) { + LOGE("Error in socket: " + std::string(strerror(errno))); + throw IPCException("Error in socket: " + std::string(strerror(errno))); + } + + sockaddr_un serverAddress; + serverAddress.sun_family = AF_UNIX; + strncpy(serverAddress.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path)); + + if (-1 == connect(fd, + reinterpret_cast(&serverAddress), + sizeof(struct sockaddr_un))) { + ::close(fd); + LOGE("Error in connect: " + std::string(strerror(errno))); + throw IPCException("Error in connect: " + std::string(strerror(errno))); + } + + return Socket(fd); +} + +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/internals/socket.hpp b/common/ipc/internals/socket.hpp new file mode 100644 index 0000000..b7a6d40 --- /dev/null +++ b/common/ipc/internals/socket.hpp @@ -0,0 +1,116 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 COMMON_IPC_INTERNALS_SOCKET_HPP +#define COMMON_IPC_INTERNALS_SOCKET_HPP + +#include +#include +#include + +namespace security_containers { +namespace ipc { + +/** + * This class wraps all operations possible to do with a socket. + * + * It has operations both for client and server application. + */ +class Socket { +public: + + typedef std::unique_lock Guard; + + /** + * Default constructor. + * If socketFD is passed then it's passed by the Socket + * + * @param socketFD socket obtained outside the class. + */ + Socket(int socketFD = -1); + Socket(Socket&& socket); + ~Socket(); + Socket& operator=(const 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 accept() ; + + /** + */ + Socket::Guard getGuard() const; + + + /** + * Prepares socket for accepting connections. + * Called by a server. + * + * @param path path to the socket + * @return created socket + */ + static Socket createSocket(const std::string& path); + + /** + * Connects to a socket. Called as a client. + * + * @param path path to the socket + * @return connected socket + */ + static Socket connectSocket(const std::string& path); + +private: + int mFD; + mutable std::recursive_mutex mCommunicationMutex; + + static int createDomainSocket(const std::string& path); + static int getSystemdSocket(const std::string& path); +}; + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_INTERNALS_SOCKET_HPP diff --git a/common/ipc/internals/utils.cpp b/common/ipc/internals/utils.cpp new file mode 100644 index 0000000..e98b60d --- /dev/null +++ b/common/ipc/internals/utils.cpp @@ -0,0 +1,134 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 Utility functions + */ + +#include "config.hpp" + +#include "ipc/exception.hpp" +#include "ipc/internals/utils.hpp" +#include "logger/logger.hpp" + +#include +#include +#include + +#include +#include + +namespace fs = boost::filesystem; + +namespace security_containers { +namespace ipc { + +void close(int fd) +{ + if (fd < 0) { + return; + } + + for (;;) { + if (-1 == ::close(fd)) { + if (errno == EINTR) { + LOGD("Close interrupted by a signal, retrying"); + continue; + } + LOGE("Error in close: " << std::string(strerror(errno))); + throw IPCException("Error in close: " + std::string(strerror(errno))); + } + break; + } +} + +void write(int fd, const void* bufferPtr, const size_t size) +{ + size_t nTotal = 0; + int n; + + do { + n = ::write(fd, + reinterpret_cast(bufferPtr) + nTotal, + size - nTotal); + if (n < 0) { + if (errno == EINTR) { + LOGD("Write interrupted by a signal, retrying"); + continue; + } + LOGE("Error during writing: " + std::string(strerror(errno))); + throw IPCException("Error during witting: " + std::string(strerror(errno))); + } + nTotal += n; + } while (nTotal < size); +} + +void read(int fd, void* bufferPtr, const size_t size) +{ + size_t nTotal = 0; + int n; + + do { + n = ::read(fd, + reinterpret_cast(bufferPtr) + nTotal, + size - nTotal); + if (n < 0) { + if (errno == EINTR) { + LOGD("Read interrupted by a signal, retrying"); + continue; + } + LOGE("Error during reading: " + std::string(strerror(errno))); + throw IPCException("Error during reading: " + std::string(strerror(errno))); + } + nTotal += n; + } while (nTotal < size); +} + +unsigned int getMaxFDNumber() +{ + struct rlimit rlim; + if (-1 == getrlimit(RLIMIT_NOFILE, &rlim)) { + LOGE("Error during getrlimit: " + std::string(strerror(errno))); + throw IPCException("Error during getrlimit: " + std::string(strerror(errno))); + } + 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)) { + LOGE("Error during setrlimit: " + std::string(strerror(errno))); + throw IPCException("Error during setrlimit: " + std::string(strerror(errno))); + } +} + +unsigned int getFDNumber() +{ + const std::string path = "/proc/self/fd/"; + return std::distance(fs::directory_iterator(path), + fs::directory_iterator()); +} + +} // namespace ipc +} // namespace security_containers + diff --git a/common/ipc/internals/utils.hpp b/common/ipc/internals/utils.hpp new file mode 100644 index 0000000..0b1815d --- /dev/null +++ b/common/ipc/internals/utils.hpp @@ -0,0 +1,77 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 Utility functions + */ + +#ifndef COMMON_IPC_INTERNALS_UTILS_HPP +#define COMMON_IPC_INTERNALS_UTILS_HPP + +#include + +namespace security_containers { +namespace ipc { + +/** + * Close the file descriptor. + * Repeat until + */ +void close(int fd); + +/** + * 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 + */ +void write(int fd, const void* bufferPtr, const size_t size); + +/** + * 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 + */ +void read(int fd, void* bufferPtr, const size_t size); + +/** + * @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(); + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_INTERNALS_UTILS_HPP diff --git a/common/ipc/service.cpp b/common/ipc/service.cpp new file mode 100644 index 0000000..d2590d6 --- /dev/null +++ b/common/ipc/service.cpp @@ -0,0 +1,86 @@ +// /* +// * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +// * +// * Contact: Jan Olszak +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR 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 IPC handling class +// */ + +#include "config.hpp" + +#include "ipc/service.hpp" +#include "ipc/exception.hpp" +#include "logger/logger.hpp" + +using namespace std::placeholders; + +namespace security_containers { +namespace ipc { + +Service::Service(const std::string& socketPath, + const PeerCallback& addPeerCallback, + const PeerCallback& removePeerCallback) + : mProcessor(addPeerCallback, removePeerCallback), + mAcceptor(socketPath, std::bind(&Processor::addPeer, &mProcessor, _1)) + +{ + LOGD("Creating server"); +} + +Service::~Service() +{ + LOGD("Destroying server..."); + try { + stop(); + } catch (IPCException& e) { + LOGE("Error in Service's destructor: " << e.what()); + } + LOGD("Destroyed"); +} + +void Service::start() +{ + LOGD("Starting server"); + mProcessor.start(); + + // There can be an incoming connection from mAcceptor before mProcessor is listening, + // but it's OK. It will handle the connection when ready. So no need to wait for mProcessor. + mAcceptor.start(); + + LOGD("Started server"); +} + +void Service::stop() +{ + LOGD("Stopping server.."); + mAcceptor.stop(); + mProcessor.stop(); + LOGD("Stopped"); +} + +void Service::removeMethod(const MethodID methodID) +{ + LOGD("Removing method " << methodID); + mProcessor.removeMethod(methodID); + LOGD("Removed " << methodID); +} + + +} // namespace ipc +} // namespace security_containers diff --git a/common/ipc/service.hpp b/common/ipc/service.hpp new file mode 100644 index 0000000..08f7ec9 --- /dev/null +++ b/common/ipc/service.hpp @@ -0,0 +1,168 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 IPC handling class + */ + +#ifndef COMMON_IPC_SERVICE_HPP +#define COMMON_IPC_SERVICE_HPP + +#include "ipc/internals/processor.hpp" +#include "ipc/internals/acceptor.hpp" +#include "ipc/types.hpp" +#include "logger/logger.hpp" + +#include + +namespace security_containers { +namespace ipc { + + +/** + * This class wraps communication via UX sockets. + * It uses serialization mechanism from libConfig. + * + * There are two working threads: + * - ACCEPTOR accepts incoming connections and passes them to PROCESSOR + * - PROCESSOR is responsible for the communication and calling the callbacks + * + * For message format @see ipc::Processor + */ +class Service { +public: + typedef Processor::PeerCallback PeerCallback; + typedef Processor::PeerID PeerID; + typedef Processor::MethodID MethodID; + + /** + * @param path path to the socket + */ + Service(const std::string& path, + const PeerCallback& addPeerCallback = nullptr, + const PeerCallback& removePeerCallback = nullptr); + ~Service(); + + Service(const Service&) = delete; + Service& operator=(const Service&) = delete; + + /** + * Starts the worker and acceptor threads + */ + void start(); + + /** + * Stops all working threads + */ + void stop(); + + /** + * 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 methodCallback method handling implementation + */ + template + void addMethodHandler(const MethodID methodID, + const typename MethodHandler::type& method); + + /** + * Removes the callback + * + * @param methodID API dependent id of the method + */ + void removeMethod(const MethodID methodID); + + /** + * Synchronous method call. + * + * @param methodID API dependent id of the method + * @param data data to send + * @return result data + */ + template + std::shared_ptr callSync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + unsigned int timeoutMS); + + /** + * 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 sendCallback callback for data serialization + * @param resultCallback callback for result serialization and handling + */ + template + void callAsync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& resultCallback); + +private: + typedef std::lock_guard Lock; + Processor mProcessor; + Acceptor mAcceptor; +}; + + +template +void Service::addMethodHandler(const MethodID methodID, + const typename MethodHandler::type& method) +{ + LOGD("Adding method with id " << methodID); + mProcessor.addMethodHandler(methodID, method); + LOGD("Added method with id " << methodID); +} + +template +std::shared_ptr Service::callSync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + unsigned int timeoutMS = 500) +{ + LOGD("Sync calling method: " << methodID << " for user: " << peerID); + return mProcessor.callSync(methodID, peerID, data, timeoutMS); +} + +template +void Service::callAsync(const MethodID methodID, + const PeerID peerID, + const std::shared_ptr& data, + const typename ResultHandler::type& resultCallback) +{ + LOGD("Async calling method: " << methodID << " for user: " << peerID); + mProcessor.callAsync(methodID, + peerID, + data, + resultCallback); + LOGD("Async called method: " << methodID << "for user: " << peerID); +} + + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_SERVICE_HPP diff --git a/common/ipc/types.hpp b/common/ipc/types.hpp new file mode 100644 index 0000000..4269664 --- /dev/null +++ b/common/ipc/types.hpp @@ -0,0 +1,48 @@ +/* +* Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR 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 Handler types definitions + */ + +#ifndef COMMON_IPC_HANDLERS_HPP +#define COMMON_IPC_HANDLERS_HPP + +#include +#include + +namespace security_containers { +namespace ipc { + +template +struct MethodHandler { + typedef std::function(std::shared_ptr&)> type; +}; + + +template +struct ResultHandler { + typedef std::function&)> type; +}; + +} // namespace ipc +} // namespace security_containers + +#endif // COMMON_IPC_HANDLERS_HPP diff --git a/packaging/security-containers.spec b/packaging/security-containers.spec index 00a05a6..4453648 100644 --- a/packaging/security-containers.spec +++ b/packaging/security-containers.spec @@ -27,6 +27,7 @@ BuildRequires: pkgconfig(libLogger) BuildRequires: pkgconfig(libSimpleDbus) BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(libsystemd-journal) +BuildRequires: pkgconfig(libsystemd-daemon) BuildRequires: pkgconfig(libvirt-glib-1.0) BuildRequires: pkgconfig(sqlite3) Requires: libvirt-daemon >= 1.2.4 diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 2237f52..333171a 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -30,7 +30,7 @@ ADD_EXECUTABLE(${SERVER_CODENAME} ${project_SRCS} ${common_SRCS}) ## Link libraries ############################################################## FIND_PACKAGE(Boost COMPONENTS program_options system filesystem regex) PKG_CHECK_MODULES(SERVER_DEPS REQUIRED libvirt libvirt-glib-1.0 json gio-2.0 libsystemd-journal - libcap-ng libLogger libSimpleDbus libConfig) + libcap-ng libLogger libSimpleDbus libConfig libsystemd-daemon) INCLUDE_DIRECTORIES(${COMMON_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 3bb265d..caea433 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -36,7 +36,8 @@ ADD_EXECUTABLE(${UT_SERVER_CODENAME} ${project_SRCS} ${common_SRCS} ${server_SRC FIND_PACKAGE (Boost COMPONENTS unit_test_framework system filesystem regex) PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED libvirt libvirt-glib-1.0 json gio-2.0 - libsystemd-journal libcap-ng libLogger libSimpleDbus libConfig) + libsystemd-journal libcap-ng libLogger libSimpleDbus libConfig + libsystemd-daemon) INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${SERVER_FOLDER} ${UNIT_TESTS_FOLDER} ${CLIENT_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${UT_SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) TARGET_LINK_LIBRARIES(${UT_SERVER_CODENAME} ${UT_SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES}) diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp new file mode 100644 index 0000000..48ad1e5 --- /dev/null +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR 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 + */ + +// TODO: Test connection limit +// TODO: Refactor tests - function for setting up env + + +#include "config.hpp" +#include "ut.hpp" + +#include "ipc/service.hpp" +#include "ipc/client.hpp" +#include "ipc/types.hpp" + +#include "config/fields.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include +#include + +using namespace security_containers; +using namespace security_containers::ipc; +namespace fs = boost::filesystem; + +namespace { +struct Fixture { + std::string socketPath; + + Fixture() + : socketPath(fs::unique_path("/tmp/ipc-%%%%.socket").string()) + { + } + ~Fixture() + { + fs::remove(socketPath); + } +}; + +struct SendData { + int intVal; + SendData(int i = 0): intVal(i) {} + + CONFIG_REGISTER + ( + intVal + ) +}; + +struct EmptyData { + CONFIG_REGISTER_EMPTY +}; + +std::shared_ptr returnEmptyCallback(std::shared_ptr&) +{ + return std::shared_ptr(new EmptyData()); +} + +std::shared_ptr returnDataCallback(std::shared_ptr&) +{ + return std::shared_ptr(new SendData(1)); +} + +std::shared_ptr echoCallback(std::shared_ptr& data) +{ + return data; +} + +void testEcho(Client& c, const Client::MethodID methodID) +{ + std::shared_ptr sentData(new SendData(34)); + std::shared_ptr recvData = c.callSync(methodID, sentData); + BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); +} + +void testEcho(Service& s, const Client::MethodID methodID, const Service::PeerID peerID) +{ + std::shared_ptr sentData(new SendData(56)); + std::shared_ptr recvData = s.callSync(methodID, peerID, sentData); + BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); +} + +} // namespace + + +BOOST_FIXTURE_TEST_SUITE(IPCSuite, Fixture) + +BOOST_AUTO_TEST_CASE(ConstructorDestructorTest) +{ + Service s(socketPath); + Client c(socketPath); +} + +BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethodTest) +{ + Service s(socketPath); + + s.addMethodHandler(1, returnEmptyCallback); + s.addMethodHandler(1, returnDataCallback); + + s.start(); + + s.addMethodHandler(1, echoCallback); + s.addMethodHandler(2, returnDataCallback); + + Client c(socketPath); + c.start(); + testEcho(c, 1); + + s.removeMethod(1); + s.removeMethod(2); + + BOOST_CHECK_THROW(testEcho(c, 2), IPCException); +} + +BOOST_AUTO_TEST_CASE(ClientAddRemoveMethodTest) +{ + std::mutex mtx; + std::unique_lock lck(mtx); + std::condition_variable cv; + unsigned int peerID = 0; + auto newPeerCallback = [&cv, &peerID](unsigned int newPeerID) { + peerID = newPeerID; + cv.notify_one(); + }; + Service s(socketPath, newPeerCallback); + s.start(); + Client c(socketPath); + + c.addMethodHandler(1, returnEmptyCallback); + c.addMethodHandler(1, returnDataCallback); + + c.start(); + + c.addMethodHandler(1, echoCallback); + c.addMethodHandler(2, returnDataCallback); + + BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(1000), [&peerID]() { + return peerID != 0; + })); + + testEcho(s, 1, peerID); + + c.removeMethod(1); + c.removeMethod(2); + + BOOST_CHECK_THROW(testEcho(s, 1, peerID), IPCException); +} + +BOOST_AUTO_TEST_CASE(ServiceStartStopTest) +{ + Service s(socketPath); + + s.addMethodHandler(1, returnDataCallback); + + s.start(); + s.stop(); + s.start(); + s.stop(); + + s.start(); + s.start(); +} + +BOOST_AUTO_TEST_CASE(ClientStartStopTest) +{ + Service s(socketPath); + Client c(socketPath); + c.addMethodHandler(1, returnDataCallback); + + c.start(); + c.stop(); + c.start(); + c.stop(); + + c.start(); + c.start(); + + c.stop(); + c.stop(); +} + +BOOST_AUTO_TEST_CASE(SyncClientToServiceEchoTest) +{ + Service s(socketPath); + s.addMethodHandler(1, echoCallback); + s.addMethodHandler(2, echoCallback); + + s.start(); + Client c(socketPath); + c.start(); + testEcho(c, 1); + testEcho(c, 2); +} + +BOOST_AUTO_TEST_CASE(RestartTest) +{ + Service s(socketPath); + s.addMethodHandler(1, echoCallback); + s.start(); + s.addMethodHandler(2, echoCallback); + + Client c(socketPath); + c.start(); + testEcho(c, 1); + testEcho(c, 2); + + c.stop(); + c.start(); + + testEcho(c, 1); + testEcho(c, 2); + + s.stop(); + s.start(); + + testEcho(c, 1); + testEcho(c, 2); +} + +BOOST_AUTO_TEST_CASE(SyncServiceToClientEchoTest) +{ + std::mutex mtx; + std::unique_lock lck(mtx); + std::condition_variable cv; + unsigned int peerID = 0; + auto newPeerCallback = [&cv, &peerID](unsigned int newPeerID) { + peerID = newPeerID; + cv.notify_one(); + }; + Service s(socketPath, newPeerCallback); + s.start(); + Client c(socketPath); + c.addMethodHandler(1, echoCallback); + c.start(); + + BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(1000), [&peerID]() { + return peerID != 0; + })); + + std::shared_ptr sentData(new SendData(56)); + std::shared_ptr recvData = s.callSync(1, peerID, sentData); + BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); +} + +BOOST_AUTO_TEST_CASE(AsyncClientToServiceEchoTest) +{ + // Setup Service and Client + Service s(socketPath); + s.addMethodHandler(1, echoCallback); + s.start(); + Client c(socketPath); + c.start(); + + std::mutex mtx; + std::unique_lock lck(mtx); + std::condition_variable cv; + + //Async call + std::shared_ptr sentData(new SendData(34)); + std::shared_ptr recvData; + auto dataBack = [&cv, &recvData](std::shared_ptr& data) { + recvData = data; + cv.notify_one(); + }; + c.callAsync(1, sentData, dataBack); + + // Wait for the response + BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(100), [&recvData]() { + return static_cast(recvData); + })); + + BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); +} + +BOOST_AUTO_TEST_CASE(AsyncServiceToClientEchoTest) +{ + std::mutex mtx; + std::unique_lock lck(mtx); + std::condition_variable cv; + + // Setup Service and Client + unsigned int peerID = 0; + auto newPeerCallback = [&cv, &peerID](unsigned int newPeerID) { + peerID = newPeerID; + cv.notify_one(); + }; + Service s(socketPath, newPeerCallback); + s.start(); + Client c(socketPath); + c.addMethodHandler(1, echoCallback); + c.start(); + + // Wait for the connection + BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(1000), [&peerID]() { + return peerID != 0; + })); + + // Async call + std::shared_ptr sentData(new SendData(56)); + std::shared_ptr recvData; + + auto dataBack = [&cv, &recvData](std::shared_ptr& data) { + recvData = data; + cv.notify_one(); + }; + + s.callAsync(1, peerID, sentData, dataBack); + + // Wait for the response + BOOST_CHECK(cv.wait_for(lck, std::chrono::milliseconds(1000), [&recvData]() { + return recvData.get() != nullptr; + })); + + BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); +} + + +BOOST_AUTO_TEST_CASE(SyncTimeoutTest) +{ + Service s(socketPath); + s.addMethodHandler(1, echoCallback); + + s.start(); + Client c(socketPath); + c.start(); + + std::shared_ptr sentData(new SendData(78)); + + BOOST_CHECK_THROW((c.callSync(1, sentData, 1)), IPCException); +} + + +// BOOST_AUTO_TEST_CASE(ConnectionLimitTest) +// { +// unsigned oldLimit = ipc::getMaxFDNumber(); +// ipc::setMaxFDNumber(50); + +// // Setup Service and many Clients +// Service s(socketPath); +// s.addMethodHandler(1, echoCallback); +// s.start(); + +// std::list clients; +// for (int i = 0; i < 100; ++i) { +// try { +// clients.push_back(Client(socketPath)); +// clients.back().start(); +// } catch (...) {} +// } + +// unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); +// std::mt19937 generator(seed); +// for (auto it = clients.begin(); it != clients.end(); ++it) { +// try { +// std::shared_ptr sentData(new SendData(generator())); +// std::shared_ptr recvData = it->callSync(1, sentData); +// BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); +// } catch (...) {} +// } + +// ipc::setMaxFDNumber(oldLimit); +// } + + + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 7f3a8c09fb8210c9480dd0b3d0137635fdd9cee8 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 12 Nov 2014 14:43:18 +0100 Subject: [PATCH 13/16] Add client stubs for getting domain informations [Bug/Feature] Client stubs for getting domain informations [Cause] N/A [Solution] N/A [Verification] Build Change-Id: If800ae1396f3ec2e1a39d144ab6e93ea583b8949 --- client/security-containers-client-impl.cpp | 14 ++++++- client/security-containers-client-impl.hpp | 10 +++++ client/security-containers-client.cpp | 16 ++++++++ client/security-containers-client.h | 60 ++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index fd85d2b..05636fe 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -357,7 +357,19 @@ VsmStatus Client::vsm_lookup_domain_by_pid(int pid, VsmString* id) noexcept *id = strdup(containerId.c_str()); mStatus = Status(); - return vsm_get_status();; + return vsm_get_status(); +} + +VsmStatus Client::vsm_lookup_domain_by_id(const char*, VsmDomain*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_lookup_domain_by_terminal_id(int, VsmString*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); } VsmStatus Client::vsm_set_active_container(const char* id) noexcept diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index e88da49..bf1c39a 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -124,6 +124,16 @@ public: VsmStatus vsm_lookup_domain_by_pid(int pid, VsmString* id) noexcept; /** + * @see ::vsm_lookup_domain_by_id + */ + VsmStatus vsm_lookup_domain_by_id(const char* id, VsmDomain* domain) noexcept; + + /** + * @see ::vsm_lookup_domain_by_terminal_id + */ + VsmStatus vsm_lookup_domain_by_terminal_id(int terminal, VsmString* id) noexcept; + + /** * @see ::vsm_set_active_container */ VsmStatus vsm_set_active_container(const char* id) noexcept; diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index 2adc702..d1d99e4 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -88,6 +88,12 @@ API void vsm_string_free(VsmString string) free(string); } +API void vsm_domain_free(VsmDomain domain) +{ + free(domain->rootfs_path); + free(domain->id); + free(domain); +} API void vsm_client_free(VsmClient client) { @@ -126,6 +132,16 @@ API VsmStatus vsm_lookup_domain_by_pid(VsmClient client, int pid, VsmString* id) return getClient(client).vsm_lookup_domain_by_pid(pid, id); } +API VsmStatus vsm_lookup_domain_by_id(VsmClient client, const char* id, VsmDomain* domain) +{ + return getClient(client).vsm_lookup_domain_by_id(id, domain); +} + +API VsmStatus vsm_lookup_domain_by_terminal_id(VsmClient client, int terminal, VsmString* id) +{ + return getClient(client).vsm_lookup_domain_by_terminal_id(terminal, id); +} + API VsmStatus vsm_set_active_container(VsmClient client, const char* id) { return getClient(client).vsm_set_active_container(id); diff --git a/client/security-containers-client.h b/client/security-containers-client.h index 22a2a4e..4fe18f0 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -120,6 +120,38 @@ typedef enum { typedef unsigned int VsmSubscriptionId; /** + * States of domain + */ +typedef enum { + STOPPED, + STARTING, + RUNNING, + STOPPING, + ABORTING, + FREEZING, + FROZEN, + THAWED, + LOCKED, + MAX_STATE, + ACTIVATING = 128 +} VsmDomainState; + +/** + * Domain information structure + */ +struct VsmDomainStructure { + char* id; + int terminal; + VsmDomainState state; + char *rootfs_path; +}; + +/** + * Domain information + */ +typedef VsmDomainStructure* VsmDomain; + +/** * Start glib loop. * * Do not call this function if an application creates a glib loop itself. @@ -199,6 +231,12 @@ void vsm_array_string_free(VsmArrayString astring); */ void vsm_string_free(VsmString string); +/** + * Release VsmDomain + * + * @param domain VsmDomain + */ +void vsm_domain_free(VsmDomain domain); /** * @name Host API @@ -263,6 +301,28 @@ VsmStatus vsm_get_active_container_id(VsmClient client, VsmString* id); VsmStatus vsm_lookup_domain_by_pid(VsmClient client, int pid, VsmString* id); /** + * Get domain informations of domain with given id. + * + * @param[in] client security-containers-server's client + * @param[in] id domain name + * @param[out] domain domain informations + * @return status of this function call + * @remark Use @p vsm_doamin_free() to free memory occupied by @p domain + */ +VsmStatus vsm_lookup_domain_by_id(VsmClient client, const char* id, VsmDomain* domain); + +/** + * Get domain name with given terminal. + * + * @param[in] client security-containers-server's client + * @param[in] terminal terminal id + * @param[out] id domain name with given terminal + * @return status of this function call + * @remark Use @p vsm_string_free() to free memory occupied by @p id. + */ +VsmStatus vsm_lookup_domain_by_terminal_id(VsmClient client, int terminal, VsmString* id); + +/** * Set active (foreground) container. * * @param[in] client security-containers-server's client -- 2.7.4 From c658c5050ff820757012f0d2f0cf29bee172ab85 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 12 Nov 2014 15:32:49 +0100 Subject: [PATCH 14/16] Add client stubs for domain lifetime management [Bug/Feature] Client stubs for domain lifetime management [Cause] N/A [Solution] N/A [Verification] Build Change-Id: Ibf6c8f769bf2bfab4e7960a6775fb1064990fc2d --- client/security-containers-client-impl.cpp | 12 ++++++++++++ client/security-containers-client-impl.hpp | 10 ++++++++++ client/security-containers-client.cpp | 10 ++++++++++ client/security-containers-client.h | 18 ++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index 05636fe..fc4699d 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -388,6 +388,18 @@ VsmStatus Client::vsm_create_domain(const char* id) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_ADD_CONTAINER, args_in); } +VsmStatus Client::vsm_shutdown_domain(const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_start_domain(const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + VsmStatus Client::vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, void* data, VsmSubscriptionId* subscriptionId) noexcept diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index bf1c39a..0d450a7 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -144,6 +144,16 @@ public: VsmStatus vsm_create_domain(const char* id) noexcept; /** + * @see ::vsm_shutdown_domain + */ + VsmStatus vsm_shutdown_domain(const char* id) noexcept; + + /** + * @see ::vsm_start_domain + */ + VsmStatus vsm_start_domain(const char* id) noexcept; + + /** * @see ::vsm_add_state_callback */ VsmStatus vsm_add_state_callback(VsmContainerDbusStateCallback containerDbusStateCallback, diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index d1d99e4..c1dafeb 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -152,6 +152,16 @@ API VsmStatus vsm_create_domain(VsmClient client, const char* id) return getClient(client).vsm_create_domain(id); } +API VsmStatus vsm_shutdown_domain(VsmClient client, const char* id) +{ + return getClient(client).vsm_shutdown_domain(id); +} + +API VsmStatus vsm_start_domain(VsmClient client, const char* id) +{ + return getClient(client).vsm_start_domain(id); +} + API VsmStatus vsm_add_state_callback(VsmClient client, VsmContainerDbusStateCallback containerDbusStateCallback, void* data, diff --git a/client/security-containers-client.h b/client/security-containers-client.h index 4fe18f0..f19d3b7 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -341,6 +341,24 @@ VsmStatus vsm_set_active_container(VsmClient client, const char* id); VsmStatus vsm_create_domain(VsmClient client, const char* id); /** + * Shutdown domain + * + * @param[in] client security-containers-server's client + * @param[in] id domain name + * @return status of this function call + */ +VsmStatus vsm_shutdown_domain(VsmClient client, const char* id); + +/** + * Start domain + * + * @param[in] client security-containers-server's client + * @param[in] id domain name + * @return status of this function call + */ +VsmStatus vsm_start_domain(VsmClient client, const char* id); + +/** * Register dbus state change callback function. * * @note The callback function will be invoked on a different thread. -- 2.7.4 From 6544fc5632496132f08c94f48a5f731d9cfba860 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 12 Nov 2014 16:10:30 +0100 Subject: [PATCH 15/16] Add client stubs for domain devices management [Bug/Feature] Client stubs for domain devices management [Cause] N/A [Solution] N/A [Verification] Build Change-Id: Ie34fff4507fa7277a2327983ae3bd6b7c732ae82 --- client/security-containers-client-impl.cpp | 60 ++++++++++ client/security-containers-client-impl.hpp | 67 +++++++++++ client/security-containers-client.cpp | 82 ++++++++++++++ client/security-containers-client.h | 171 ++++++++++++++++++++++++++++- 4 files changed, 378 insertions(+), 2 deletions(-) diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index fc4699d..a98349a 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -425,6 +425,66 @@ VsmStatus Client::vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexc return signalUnsubscribe(subscriptionId); } +VsmStatus Client::vsm_domain_grant_device(const char*, const char*, uint32_t) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_revoke_device(const char*, const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_domain_get_netdevs(const char*, VsmArrayString*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_netdev_get_ipv4_addr(const char*, const char*, struct in_addr*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_netdev_get_ipv6_addr(const char*, const char*, struct in6_addr*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_netdev_set_ipv4_addr(const char*, const char*, struct in_addr*, int) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_netdev_set_ipv6_addr(const char*, const char*, struct in6_addr*, int) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_create_netdev(const char*, VsmNetdevType, const char*, const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_destroy_netdev(const char*, const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + +VsmStatus Client::vsm_lookup_netdev_by_name(const char*, const char*, VsmNetdev*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + VsmStatus Client::vsm_notify_active_container(const char* application, const char* message) noexcept { assert(application); diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index 0d450a7..592771d 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -166,6 +166,73 @@ public: VsmStatus vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexcept; /** + * @see ::vsm_del_state_callback + */ + VsmStatus vsm_domain_grant_device(const char* id, + const char* device, + uint32_t flags) noexcept; + + /** + * @see ::vsm_revoke_device + */ + VsmStatus vsm_revoke_device(const char* id, const char* device) noexcept; + + /** + * @see ::vsm_domain_get_netdevs + */ + VsmStatus vsm_domain_get_netdevs(const char* domain, VsmArrayString* netdevIds) noexcept; + + /** + * @see ::vsm_netdev_get_ipv4_addr + */ + VsmStatus vsm_netdev_get_ipv4_addr(const char* domain, + const char* netdevId, + struct in_addr *addr) noexcept; + + /** + * @see ::vsm_netdev_get_ipv6_addr + */ + VsmStatus vsm_netdev_get_ipv6_addr(const char* domain, + const char* netdevId, + struct in6_addr *addr) noexcept; + + /** + * @see ::vsm_netdev_set_ipv4_addr + */ + VsmStatus vsm_netdev_set_ipv4_addr(const char* domain, + const char* netdevId, + struct in_addr *addr, + int prefix) noexcept; + + /** + * @see ::vsm_netdev_set_ipv6_addr + */ + VsmStatus vsm_netdev_set_ipv6_addr(const char* domain, + const char* netdevId, + struct in6_addr *addr, + int prefix) noexcept; + + /** + * @see ::vsm_create_netdev + */ + VsmStatus vsm_create_netdev(const char* domain, + VsmNetdevType netdevType, + const char* target, + const char* netdevId) noexcept; + + /** + * @see ::vsm_destroy_netdev + */ + VsmStatus vsm_destroy_netdev(const char* domain, const char* netdevId) noexcept; + + /** + * @see ::vsm_lookup_netdev_by_name + */ + VsmStatus vsm_lookup_netdev_by_name(const char* domain, + const char* netdevId, + VsmNetdev* netdev) noexcept; + + /** * @see ::vsm_notify_active_container */ VsmStatus vsm_notify_active_container(const char* application, const char* message) noexcept; diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index c1dafeb..6616c80 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -95,6 +95,12 @@ API void vsm_domain_free(VsmDomain domain) free(domain); } +API void vsm_netdev_free(VsmNetdev netdev) +{ + free(netdev->name); + free(netdev); +} + API void vsm_client_free(VsmClient client) { if (client != NULL) { @@ -175,6 +181,82 @@ API VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscri return getClient(client).vsm_del_state_callback(subscriptionId); } +API VsmStatus vsm_domain_grant_device(VsmClient client, + const char* id, + const char* device, + uint32_t flags) +{ + return getClient(client).vsm_domain_grant_device(id, device, flags); +} + +API VsmStatus vsm_revoke_device(VsmClient client, const char* id, const char* device) +{ + return getClient(client).vsm_revoke_device(id, device); +} + +API VsmStatus vsm_domain_get_netdevs(VsmClient client, + const char* domain, + VsmArrayString* netdevIds) +{ + return getClient(client).vsm_domain_get_netdevs(domain, netdevIds); +} + +API VsmStatus vsm_netdev_get_ipv4_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in_addr *addr) +{ + return getClient(client).vsm_netdev_get_ipv4_addr(domain, netdevId, addr); +} + +API VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in6_addr *addr) +{ + return getClient(client).vsm_netdev_get_ipv6_addr(domain, netdevId, addr); +} + +API VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in_addr *addr, + int prefix) +{ + return getClient(client).vsm_netdev_set_ipv4_addr(domain, netdevId, addr, prefix); +} + +API VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in6_addr *addr, + int prefix) +{ + return getClient(client).vsm_netdev_set_ipv6_addr(domain, netdevId, addr, prefix); +} + +API VsmStatus vsm_create_netdev(VsmClient client, + const char* domain, + VsmNetdevType netdevType, + const char* target, + const char* netdevId) +{ + return getClient(client).vsm_create_netdev(domain, netdevType, target, netdevId); +} + +API VsmStatus vsm_destroy_netdev(VsmClient client, const char* domain, const char* netdevId) +{ + return getClient(client).vsm_destroy_netdev(domain, netdevId); +} + +API VsmStatus vsm_lookup_netdev_by_name(VsmClient client, + const char* domain, + const char* netdevId, + VsmNetdev* netdev) +{ + return getClient(client).vsm_lookup_netdev_by_name(domain, netdevId, netdev); +} + API VsmStatus vsm_notify_active_container(VsmClient client, const char* application, const char* message) diff --git a/client/security-containers-client.h b/client/security-containers-client.h index f19d3b7..8afc67b 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -78,6 +78,8 @@ finish: #ifndef SECURITY_CONTAINERS_CLIENT_H #define SECURITY_CONTAINERS_CLIENT_H +#include + #ifdef __cplusplus extern "C" { @@ -139,12 +141,12 @@ typedef enum { /** * Domain information structure */ -struct VsmDomainStructure { +typedef struct { char* id; int terminal; VsmDomainState state; char *rootfs_path; -}; +} VsmDomainStructure; /** * Domain information @@ -152,6 +154,28 @@ struct VsmDomainStructure { typedef VsmDomainStructure* VsmDomain; /** + * Netowrk device type + */ +typedef enum { + VETH, + PHYS, + MACVLAN +} VsmNetdevType; + +/** + * Network device information structure + */ +typedef struct { + char* name; + VsmNetdevType type; +} VsmNetdevStructure; + +/** + * Network device information + */ +typedef VsmNetdevStructure* VsmNetdev; + +/** * Start glib loop. * * Do not call this function if an application creates a glib loop itself. @@ -239,6 +263,13 @@ void vsm_string_free(VsmString string); void vsm_domain_free(VsmDomain domain); /** + * Release VsmNetdev + * + * @param netdev VsmNetdev + */ +void vsm_netdev_free(VsmNetdev netdev); + +/** * @name Host API * * Functions using org.tizen.containers.host.manager D-Bus interface. @@ -384,6 +415,142 @@ VsmStatus vsm_add_state_callback(VsmClient client, */ VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscriptionId); +/** + * Grant access to device + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] device device path + * @param[in] flags access flags + * @return status of this function call + */ +VsmStatus vsm_domain_grant_device(VsmClient client, + const char* domain, + const char* device, + uint32_t flags); + +/** + * Revoke access to device + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] device device path + * @return status of this function call + */ +VsmStatus vsm_revoke_device(VsmClient client, const char* domain, const char* device); + +/** + * Get array of netdev from given domain + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[out] netdevIds array of netdev id + * @return status of this function call + * @remark Use vsm_array_string_free() to free memory occupied by @p netdevIds. + */ +VsmStatus vsm_domain_get_netdevs(VsmClient client, const char* domain, VsmArrayString* netdevIds); + +/** + * Get ipv4 address for given netdevId + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] netdevId netdev id + * @param[out] addr ipv4 address + * @return status of this function call + */ +VsmStatus vsm_netdev_get_ipv4_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in_addr *addr); + +/** + * Get ipv6 address for given netdevId + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] netdevId netdev id + * @param[out] addr ipv6 address + * @return status of this function call + */ +VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in6_addr *addr); + +/** + * Set ipv4 address for given netdevId + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] netdevId netdev id + * @param[in] addr ipv4 address + * @param[in] prefix bit-length of the network prefix + * @return status of this function call + */ +VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in_addr *addr, + int prefix); + +/** + * Set ipv6 address for given netdevId + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] netdevId netdev id + * @param[in] addr ipv6 address + * @param[in] prefix bit-length of the network prefix + * @return status of this function call + */ +VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, + const char* domain, + const char* netdevId, + struct in6_addr *addr, + int prefix); + +/** + * Create netdev in domain + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] netdevType netdev type + * @param[in] target TODO: this is taken form domain-control + * @param[in] netdevId network device id + * @return status of this function call + */ +VsmStatus vsm_create_netdev(VsmClient client, + const char* domain, + VsmNetdevType netdevType, + const char* target, + const char* netdevId); + +/** + * Remove netdev from domain + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] netdevId network device id + * @return status of this function call + */ +VsmStatus vsm_destroy_netdev(VsmClient client, const char* domain, const char* netdevId); + +/** + * Get netdev informations + * + * @param[in] client security-containers-server's client + * @param[in] domain domain name + * @param[in] netdevId network device id + * @param[out] netdev netdev informations + * @return status of this function call + * @remark Use vsm_netdev_free() to free memory occupied by @p netdev. + */ +VsmStatus vsm_lookup_netdev_by_name(VsmClient client, + const char* domain, + const char* netdevId, + VsmNetdev* netdev); + /** @} */ // Host API -- 2.7.4 From a95a15f6b39abffd117038949dda0a208ac8f6f9 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Fri, 14 Nov 2014 08:58:01 +0100 Subject: [PATCH 16/16] Add client stubs for domain image management [Bug/Feature] Client stubs for domain image management [Cause] N/A [Solution] N/A [Verification] Build Change-Id: I1e193a41b86d75333ed8dc0f165c42e52a1c16f9 --- cli/command-line-interface.cpp | 2 +- client/security-containers-client-impl.cpp | 12 +++++++++++- client/security-containers-client-impl.hpp | 7 ++++++- client/security-containers-client.cpp | 9 +++++++-- client/security-containers-client.h | 13 ++++++++++++- tests/unit_tests/client/ut-client.cpp | 2 +- 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 8af17a7..f3768d0 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -119,7 +119,7 @@ void create_domain(int pos, int argc, const char** argv) throw runtime_error("Not enough parameters"); } - one_shot(bind(vsm_create_domain, _1, argv[pos + 1])); + one_shot(bind(vsm_create_domain, _1, argv[pos + 1], nullptr)); } } // namespace cli diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index a98349a..205a8b2 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -380,14 +380,24 @@ VsmStatus Client::vsm_set_active_container(const char* id) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_SET_ACTIVE_CONTAINER, args_in); } -VsmStatus Client::vsm_create_domain(const char* id) noexcept +VsmStatus Client::vsm_create_domain(const char* id, const char* tname) noexcept { assert(id); + if (tname) { + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Named template isn't implemented"); + return vsm_get_status(); + } GVariant* args_in = g_variant_new("(s)", id); return callMethod(HOST_INTERFACE, api::host::METHOD_ADD_CONTAINER, args_in); } +VsmStatus Client::vsm_destroy_domain(const char*) noexcept +{ + mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); + return vsm_get_status(); +} + VsmStatus Client::vsm_shutdown_domain(const char*) noexcept { mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index 592771d..48e1c19 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -141,7 +141,12 @@ public: /** * @see ::vsm_create_domain */ - VsmStatus vsm_create_domain(const char* id) noexcept; + VsmStatus vsm_create_domain(const char* id, const char* tname) noexcept; + + /** + * @see ::vsm_destroy_domain + */ + VsmStatus vsm_destroy_domain(const char* id) noexcept; /** * @see ::vsm_shutdown_domain diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index 6616c80..e867a9e 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -153,9 +153,14 @@ API VsmStatus vsm_set_active_container(VsmClient client, const char* id) return getClient(client).vsm_set_active_container(id); } -API VsmStatus vsm_create_domain(VsmClient client, const char* id) +API VsmStatus vsm_create_domain(VsmClient client, const char* id, const char* tname) { - return getClient(client).vsm_create_domain(id); + return getClient(client).vsm_create_domain(id, tname); +} + +API VsmStatus vsm_destroy_domain(VsmClient client, const char* id) +{ + return getClient(client).vsm_destroy_domain(id); } API VsmStatus vsm_shutdown_domain(VsmClient client, const char* id) diff --git a/client/security-containers-client.h b/client/security-containers-client.h index 8afc67b..6419870 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -367,9 +367,20 @@ VsmStatus vsm_set_active_container(VsmClient client, const char* id); * * @param[in] client security-containers-server's client * @param[in] id container id + * @param[in] tname template name, NULL for default * @return status of this function call */ -VsmStatus vsm_create_domain(VsmClient client, const char* id); +VsmStatus vsm_create_domain(VsmClient client, const char* id, const char* tname); + +/** + * Remove domain + * + * @param[in] client security-containers-server's client + * @param[in] id container id + * @param[in] force if 0 data will be kept, otherwise data will be lost + * @return status of this function call + */ +VsmStatus vsm_destroy_domain(VsmStatus clent, const char* id, int force); /** * Shutdown domain diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index f26dbf3..3a5e0e6 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(AddContainerTest) VsmClient client = vsm_client_create(); VsmStatus status = vsm_connect(client); BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); - status = vsm_create_domain(client, newActiveContainerId.c_str()); + status = vsm_create_domain(client, newActiveContainerId.c_str(), NULL); BOOST_REQUIRE_EQUAL(VSMCLIENT_CUSTOM_ERROR, status); vsm_client_free(client); } -- 2.7.4