From 719daa453260d4cbcad85d0e6dbfd2434b779625 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Sat, 6 Dec 2014 13:05:46 +0100 Subject: [PATCH 01/16] config (dynamic): unit tests, test config extracted [Bug/Feature] test loading config defaults from json [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ia9ad29af5e343642d9ca7ae26b126c3efe45beab --- tests/unit_tests/config/testconfig-example.hpp | 151 +++++++++++++++++++++++++ tests/unit_tests/config/ut-configuration.cpp | 122 +------------------- tests/unit_tests/config/ut-dynvisit.cpp | 100 ++++++++++++++++ 3 files changed, 252 insertions(+), 121 deletions(-) create mode 100644 tests/unit_tests/config/testconfig-example.hpp create mode 100644 tests/unit_tests/config/ut-dynvisit.cpp diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp new file mode 100644 index 0000000..35e846e --- /dev/null +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Krzysztof Dynowski (k.dynowski@samsumg.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Test configuration struct to be used in unit tests + */ + +#ifndef TESTCONFIG_EXAMPLE_HPP +#define TESTCONFIG_EXAMPLE_HPP + +#include "config/fields.hpp" +#include "config/fields-union.hpp" + +struct TestConfig { + // subtree class + struct SubConfig { + + struct SubSubConfig { + int intVal; + bool moved; + + CONFIG_REGISTER + ( + intVal + ) + SubSubConfig() : intVal(), moved(false) {} + SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} + SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { + config.moved = true; + } + SubSubConfig& operator=(const SubSubConfig& config) { + intVal = config.intVal; + moved = false; + return *this; + } + SubSubConfig& operator=(SubSubConfig&& config) { + intVal = std::move(config.intVal); + moved = false; + config.moved = true; + return *this; + } + bool isMoved() const { + return moved; + } + }; + + int intVal; + std::vector intVector; + SubSubConfig subSubObj; + + CONFIG_REGISTER + ( + intVal, + intVector, + subSubObj + ) + }; + + struct SubConfigOption { + CONFIG_DECLARE_UNION + ( + SubConfig, + int + ) + }; + + int intVal; + std::int64_t int64Val; + std::string stringVal; + double doubleVal; + bool boolVal; + + std::vector emptyIntVector; + std::vector intVector; + std::vector stringVector; + std::vector doubleVector; + + SubConfig subObj; + std::vector subVector; + + SubConfigOption union1; + SubConfigOption union2; + std::vector unions; + + CONFIG_REGISTER + ( + intVal, + int64Val, + stringVal, + doubleVal, + boolVal, + + emptyIntVector, + intVector, + stringVector, + doubleVector, + + subObj, + subVector, + + union1, + union2, + unions + ) +}; + +/** + * JSON string used in ConfigSuite test cases + * For the purpose of these tests the order of this string + * has to be equal to the above REGISTER order + */ +const std::string jsonTestString = + "{ \"intVal\": 12345, " + "\"int64Val\": -1234567890123456789, " + "\"stringVal\": \"blah\", " + "\"doubleVal\": -1.234000, " + "\"boolVal\": true, " + "\"emptyIntVector\": [ ], " + "\"intVector\": [ 1, 2, 3 ], " + "\"stringVector\": [ \"a\", \"b\" ], " + "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], " + "\"subObj\": { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, " + "\"subVector\": [ { \"intVal\": 123, \"intVector\": [ 3, 4 ], \"subSubObj\": { \"intVal\": 345 } }, " + "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], " + "\"union1\": { \"type\": \"int\", \"value\": 2 }, " + "\"union2\": { \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " + "\"subSubObj\": { \"intVal\": 234 } } }, " + "\"unions\": [ " + "{ \"type\": \"int\", \"value\": 2 }, " + "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " + "\"subSubObj\": { \"intVal\": 234 } } } ] }"; + +#endif diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index 4e6bedc..8e82905 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -25,8 +25,7 @@ #include "config.hpp" #include "ut.hpp" -#include "config/fields.hpp" -#include "config/fields-union.hpp" +#include "testconfig-example.hpp" #include "config/manager.hpp" #include #include @@ -38,125 +37,6 @@ using namespace config; BOOST_AUTO_TEST_SUITE(ConfigurationSuite) -struct TestConfig { - // subtree class - struct SubConfig { - - struct SubSubConfig { - int intVal; - bool moved; - - CONFIG_REGISTER - ( - intVal - ) - SubSubConfig() : intVal(), moved(false) {} - SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} - SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { - config.moved = true; - } - SubSubConfig& operator=(const SubSubConfig& config) { - intVal = config.intVal; - moved = false; - return *this; - } - SubSubConfig& operator=(SubSubConfig&& config) { - intVal = std::move(config.intVal); - moved = false; - config.moved = true; - return *this; - } - bool isMoved() const { - return moved; - } - }; - - int intVal; - std::vector intVector; - SubSubConfig subSubObj; - - CONFIG_REGISTER - ( - intVal, - intVector, - subSubObj - ) - }; - - struct SubConfigOption { - CONFIG_DECLARE_UNION - ( - SubConfig, - int - ) - }; - - int intVal; - std::int64_t int64Val; - std::string stringVal; - double doubleVal; - bool boolVal; - - std::vector emptyIntVector; - std::vector intVector; - std::vector stringVector; - std::vector doubleVector; - - SubConfig subObj; - std::vector subVector; - - SubConfigOption union1; - SubConfigOption union2; - std::vector unions; - - CONFIG_REGISTER - ( - intVal, - int64Val, - stringVal, - doubleVal, - boolVal, - - emptyIntVector, - intVector, - stringVector, - doubleVector, - - subObj, - subVector, - - union1, - union2, - unions - ) -}; - -/** - * JSON string used in ConfigSuite test cases - * For the purpose of these tests the order of this string - * has to be equal to the above REGISTER order - */ -const std::string jsonTestString = - "{ \"intVal\": 12345, " - "\"int64Val\": -1234567890123456789, " - "\"stringVal\": \"blah\", " - "\"doubleVal\": -1.234000, " - "\"boolVal\": true, " - "\"emptyIntVector\": [ ], " - "\"intVector\": [ 1, 2, 3 ], " - "\"stringVector\": [ \"a\", \"b\" ], " - "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], " - "\"subObj\": { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, " - "\"subVector\": [ { \"intVal\": 123, \"intVector\": [ 3, 4 ], \"subSubObj\": { \"intVal\": 345 } }, " - "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], " - "\"union1\": { \"type\": \"int\", \"value\": 2 }, " - "\"union2\": { \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " - "\"subSubObj\": { \"intVal\": 234 } } }, " - "\"unions\": [ " - "{ \"type\": \"int\", \"value\": 2 }, " - "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " - "\"subSubObj\": { \"intVal\": 234 } } } ] }"; - // Floating point tolerance as a number of rounding errors const int TOLERANCE = 1; diff --git a/tests/unit_tests/config/ut-dynvisit.cpp b/tests/unit_tests/config/ut-dynvisit.cpp new file mode 100644 index 0000000..0a24d5c --- /dev/null +++ b/tests/unit_tests/config/ut-dynvisit.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Krzysztof Dynowski (k.dynowski@samsumg.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Unit test of combine kvstore with defaults from json + */ + +#include "config.hpp" +#include "ut.hpp" +#include "testconfig-example.hpp" +#include "config/manager.hpp" +#include + +#include "config/from-kvjson-visitor.hpp" + + +using namespace config; +namespace fs = boost::filesystem; + +struct Fixture { + std::string dbPath; + + Fixture() + : dbPath(fs::unique_path("/tmp/kvstore-%%%%.db3").string()) + { + fs::remove(dbPath); + } + ~Fixture() + { + fs::remove(dbPath); + } +}; + +BOOST_FIXTURE_TEST_SUITE(DynVisitSuite, Fixture) + +void checkJsonConfig(const TestConfig& cfg, const std::string& json) +{ + TestConfig cfg2; + loadFromString(json, cfg2); + BOOST_CHECK_EQUAL(cfg2.intVal, cfg.intVal); + BOOST_CHECK_EQUAL(cfg.int64Val, cfg.int64Val); + BOOST_CHECK_EQUAL(cfg2.boolVal, cfg.boolVal); + BOOST_CHECK_EQUAL(cfg2.stringVal, cfg.stringVal); + BOOST_CHECK_EQUAL(cfg2.intVector.size(), cfg.intVector.size()); + BOOST_CHECK_EQUAL(cfg2.subObj.intVal, cfg.subObj.intVal); +} + +void checkKVConfig(const TestConfig& cfg, const std::string& db) +{ + KVStore store(db); + BOOST_CHECK_EQUAL(store.get(".intVal"), cfg.intVal); + BOOST_CHECK_EQUAL(store.get(".int64Val"), cfg.int64Val); + BOOST_CHECK_EQUAL(store.get(".boolVal"), cfg.boolVal); + BOOST_CHECK_EQUAL(store.get(".stringVal"), cfg.stringVal); + BOOST_CHECK_EQUAL(store.get(".intVector"), cfg.intVector.size()); + BOOST_CHECK_EQUAL(store.get(".subObj.intVal"), cfg.subObj.intVal); +} + +BOOST_AUTO_TEST_CASE(ReadConfigDefaults) +{ + TestConfig cfg; + loadFromKVStoreWithJson(dbPath, jsonTestString, cfg); + checkJsonConfig(cfg, jsonTestString); +} + +BOOST_AUTO_TEST_CASE(ReadConfigNoDefaults) +{ + TestConfig cfg; + loadFromKVStoreWithJson(dbPath, jsonTestString, cfg); + // modify and save config + cfg.intVal += 5; + cfg.int64Val += 7777; + cfg.boolVal = !cfg.boolVal; + cfg.stringVal += "-changed"; + config::saveToKVStore(dbPath, cfg); + + TestConfig cfg2; + loadFromKVStoreWithJson(dbPath, jsonTestString, cfg2); + checkKVConfig(cfg2, dbPath); +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From fc5836a20906dc4a50f9639d03f84f6f704f1423 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Thu, 18 Dec 2014 09:56:02 +0100 Subject: [PATCH 02/16] Implement shutdown/start function in server, client and cli [Bug/Feature] Implement shutdown/start function in server, client and cli [Cause] N/A [Solution] N/A [Verification] Build, run appropriate function (through cli) Change-Id: Ib06feff13305db134cf3415c38e83d0e836f149a Signed-off-by: Dariusz Michaluk --- cli/command-line-interface.cpp | 22 ++++ cli/command-line-interface.hpp | 14 +++ cli/main.cpp | 16 +++ client/vasum-client-impl.cpp | 14 +-- server/host-connection.cpp | 38 ++++++- server/host-connection.hpp | 18 ++++ server/host-dbus-definitions.hpp | 20 ++-- server/zones-manager.cpp | 153 +++++++++++++++++++-------- server/zones-manager.hpp | 24 +++-- tests/unit_tests/client/ut-client.cpp | 14 +++ tests/unit_tests/server/ut-zones-manager.cpp | 96 +++++++++++++++-- 11 files changed, 347 insertions(+), 82 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index d7d8439..8120ca8 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -165,6 +165,28 @@ void destroy_zone(int pos, int argc, const char** argv) one_shot(bind(vsm_destroy_zone, _1, argv[pos + 1], 1)); } +void shutdown_zone(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(vsm_shutdown_zone, _1, argv[pos + 1])); +} + +void start_zone(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 1) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(vsm_start_zone, _1, argv[pos + 1])); +} + void lock_zone(int pos, int argc, const char** argv) { using namespace std::placeholders; diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index d2fe6be..789abd6 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -118,6 +118,20 @@ void create_zone(int pos, int argc, const char** argv); void destroy_zone(int pos, int argc, const char** argv); /** + * Parses command line arguments and call vsm_shutdown_zone + * + * @see vsm_shutdown_zone + */ +void shutdown_zone(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_start_zone + * + * @see vsm_start_zone + */ +void start_zone(int pos, int argc, const char** argv); + +/** * Parses command line arguments and call vsm_lock_zone * * @see vsm_lock_zone diff --git a/cli/main.cpp b/cli/main.cpp index 448d32d..b91d4e1 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -60,6 +60,22 @@ std::map commands = { } }, { + "shutdown_zone", { + shutdown_zone, + "shutdown_zone zone_id", + "Shutdown zone", + {{"zone_id", "id zone name"}} + } + }, + { + "start_zone", { + start_zone, + "start_zone zone_id", + "Start zone", + {{"zone_id", "id zone name"}} + } + }, + { "lock_zone", { lock_zone, "lock_zone zone_id", diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 2aa923d..749b155 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -460,16 +460,18 @@ VsmStatus Client::vsm_destroy_zone(const char* id) noexcept return callMethod(HOST_INTERFACE, api::host::METHOD_DESTROY_ZONE, args_in); } -VsmStatus Client::vsm_shutdown_zone(const char*) noexcept +VsmStatus Client::vsm_shutdown_zone(const char* id) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + GVariant* args_in = g_variant_new("(s)", id); + return callMethod(HOST_INTERFACE, api::host::METHOD_SHUTDOWN_ZONE, args_in); } -VsmStatus Client::vsm_start_zone(const char*) noexcept +VsmStatus Client::vsm_start_zone(const char* id) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + GVariant* args_in = g_variant_new("(s)", id); + return callMethod(HOST_INTERFACE, api::host::METHOD_START_ZONE, args_in); } VsmStatus Client::vsm_lock_zone(const char* id) noexcept diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 1eed929..621e3a2 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -160,6 +160,16 @@ void HostConnection::setDestroyZoneCallback(const DestroyZoneCallback& callback) mDestroyZoneCallback = callback; } +void HostConnection::setShutdownZoneCallback(const ShutdownZoneCallback& callback) +{ + mShutdownZoneCallback = callback; +} + +void HostConnection::setStartZoneCallback(const StartZoneCallback& callback) +{ + mStartZoneCallback = callback; +} + void HostConnection::setLockZoneCallback(const LockZoneCallback& callback) { mLockZoneCallback = callback; @@ -172,10 +182,10 @@ void HostConnection::setUnlockZoneCallback(const UnlockZoneCallback& callback) void HostConnection::onMessageCall(const std::string& objectPath, - const std::string& interface, - const std::string& methodName, - GVariant* parameters, - dbus::MethodResultBuilder::Pointer result) + const std::string& interface, + const std::string& methodName, + GVariant* parameters, + dbus::MethodResultBuilder::Pointer result) { if (objectPath != api::host::OBJECT_PATH || interface != api::host::INTERFACE) { return; @@ -317,6 +327,24 @@ void HostConnection::onMessageCall(const std::string& objectPath, } } + if (methodName == api::host::METHOD_SHUTDOWN_ZONE) { + const gchar* id = NULL; + g_variant_get(parameters, "(&s)", &id); + + if (mShutdownZoneCallback){ + mShutdownZoneCallback(id, result); + } + } + + if (methodName == api::host::METHOD_START_ZONE) { + const gchar* id = NULL; + g_variant_get(parameters, "(&s)", &id); + + if (mStartZoneCallback){ + mStartZoneCallback(id, result); + } + } + if (methodName == api::host::METHOD_LOCK_ZONE) { const gchar* id = NULL; g_variant_get(parameters, "(&s)", &id); @@ -353,7 +381,7 @@ void HostConnection::proxyCallAsync(const std::string& busName, } void HostConnection::signalZoneDbusState(const std::string& zoneId, - const std::string& dbusAddress) + const std::string& dbusAddress) { GVariant* parameters = g_variant_new("(ss)", zoneId.c_str(), dbusAddress.c_str()); mDbusConnection->emitSignal(api::host::OBJECT_PATH, diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 68e3a73..49e2a08 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -91,6 +91,12 @@ public: )> DestroyZoneCallback; typedef std::function ShutdownZoneCallback; + typedef std::function StartZoneCallback; + typedef std::function LockZoneCallback; typedef std::function" " " " " + " " + " " + " " + " " + " " + " " " " " " " " diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 1f9bef0..dcfa04c 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -90,17 +90,17 @@ ZonesManager::ZonesManager(const std::string& managerConfigPath) mHostConnection.setProxyCallCallback(bind(&ZonesManager::handleProxyCall, this, HOST_ID, _1, _2, _3, _4, _5, _6, _7)); - mHostConnection.setGetZoneDbusesCallback(bind( - &ZonesManager::handleGetZoneDbuses, this, _1)); + mHostConnection.setGetZoneDbusesCallback(bind(&ZonesManager::handleGetZoneDbuses, + this, _1)); mHostConnection.setGetZoneIdsCallback(bind(&ZonesManager::handleGetZoneIdsCall, - this, _1)); + this, _1)); mHostConnection.setGetActiveZoneIdCallback(bind(&ZonesManager::handleGetActiveZoneIdCall, - this, _1)); + this, _1)); mHostConnection.setGetZoneInfoCallback(bind(&ZonesManager::handleGetZoneInfoCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall, this, _1, _2, _3, _4, _5, _6)); @@ -112,19 +112,25 @@ ZonesManager::ZonesManager(const std::string& managerConfigPath) this, _1, _2, _3, _4)); mHostConnection.setSetActiveZoneCallback(bind(&ZonesManager::handleSetActiveZoneCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setCreateZoneCallback(bind(&ZonesManager::handleCreateZoneCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setDestroyZoneCallback(bind(&ZonesManager::handleDestroyZoneCall, - this, _1, _2)); + this, _1, _2)); + + mHostConnection.setShutdownZoneCallback(bind(&ZonesManager::handleShutdownZoneCall, + this, _1, _2)); + + mHostConnection.setStartZoneCallback(bind(&ZonesManager::handleStartZoneCall, + this, _1, _2)); mHostConnection.setLockZoneCallback(bind(&ZonesManager::handleLockZoneCall, - this, _1, _2)); + this, _1, _2)); mHostConnection.setUnlockZoneCallback(bind(&ZonesManager::handleUnlockZoneCall, - this, _1, _2)); + this, _1, _2)); for (auto& zoneConfig : mConfig.zoneConfigs) { createZone(zoneConfig); @@ -378,8 +384,8 @@ void ZonesManager::setZonesDetachOnExit() } void ZonesManager::notifyActiveZoneHandler(const std::string& caller, - const std::string& application, - const std::string& message) + const std::string& application, + const std::string& message) { LOGI("notifyActiveZoneHandler(" << caller << ", " << application << ", " << message << ") called"); @@ -412,9 +418,9 @@ void ZonesManager::displayOffHandler(const std::string& /*caller*/) } void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, - const std::string& dstZoneId, - const std::string& path, - dbus::MethodResultBuilder::Pointer result) + const std::string& dstZoneId, + const std::string& path, + dbus::MethodResultBuilder::Pointer result) { // TODO: this implementation is only a placeholder. // There are too many unanswered questions and security concerns: @@ -493,13 +499,13 @@ void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, } void ZonesManager::handleProxyCall(const std::string& caller, - const std::string& target, - const std::string& targetBusName, - const std::string& targetObjectPath, - const std::string& targetInterface, - const std::string& targetMethod, - GVariant* parameters, - dbus::MethodResultBuilder::Pointer result) + const std::string& target, + const std::string& targetBusName, + const std::string& targetObjectPath, + const std::string& targetInterface, + const std::string& targetMethod, + GVariant* parameters, + dbus::MethodResultBuilder::Pointer result) { if (!mProxyCallPolicy->isProxyCallAllowed(caller, target, @@ -569,7 +575,7 @@ void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result } void ZonesManager::handleDbusStateChanged(const std::string& zoneId, - const std::string& dbusAddress) + const std::string& dbusAddress) { mHostConnection.signalZoneDbusState(zoneId, dbusAddress); } @@ -603,7 +609,7 @@ void ZonesManager::handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer } void ZonesManager::handleGetZoneInfoCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("GetZoneInfo call"); @@ -637,11 +643,11 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, } void ZonesManager::handleDeclareFileCall(const std::string& zone, - const int32_t& type, - const std::string& path, - const int32_t& flags, - const int32_t& mode, - dbus::MethodResultBuilder::Pointer result) + const int32_t& type, + const std::string& path, + const int32_t& flags, + const int32_t& mode, + dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareFile call"); @@ -660,12 +666,12 @@ void ZonesManager::handleDeclareFileCall(const std::string& zone, } void ZonesManager::handleDeclareMountCall(const std::string& source, - const std::string& zone, - const std::string& target, - const std::string& type, - const uint64_t& flags, - const std::string& data, - dbus::MethodResultBuilder::Pointer result) + const std::string& zone, + const std::string& target, + const std::string& type, + const uint64_t& flags, + const std::string& data, + dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareMount call"); @@ -684,9 +690,9 @@ void ZonesManager::handleDeclareMountCall(const std::string& source, } void ZonesManager::handleDeclareLinkCall(const std::string& source, - const std::string& zone, - const std::string& target, - dbus::MethodResultBuilder::Pointer result) + const std::string& zone, + const std::string& target, + dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareLink call"); try { @@ -704,7 +710,7 @@ void ZonesManager::handleDeclareLinkCall(const std::string& source, } void ZonesManager::handleSetActiveZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("SetActiveZone call; Id=" << id ); @@ -730,8 +736,8 @@ void ZonesManager::handleSetActiveZoneCall(const std::string& id, void ZonesManager::generateNewConfig(const std::string& id, - const std::string& templatePath, - const std::string& resultPath) + const std::string& templatePath, + const std::string& resultPath) { namespace fs = boost::filesystem; @@ -781,7 +787,7 @@ void ZonesManager::generateNewConfig(const std::string& id, } void ZonesManager::handleCreateZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { if (id.empty()) { LOGE("Failed to add zone - invalid name."); @@ -863,7 +869,7 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, } void ZonesManager::handleDestroyZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { Lock lock(mMutex); @@ -889,8 +895,65 @@ void ZonesManager::handleDestroyZoneCall(const std::string& id, mWorker->addTask(destroyer); } +void ZonesManager::handleShutdownZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("ShutdownZone call; Id=" << id ); + + Lock lock(mMutex); + + if (mZones.find(id) == mZones.end()) { + LOGE("Failed to shutdown zone - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + LOGT("Shutdown zone " << id); + + auto shutdown = [id, result, this] { + try { + mZones[id]->stop(); + } catch (ZoneOperationException& e) { + LOGE("Error during zone shutdown: " << e.what()); + result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone"); + return; + } + result->setVoid(); + }; + + mWorker->addTask(shutdown); +} + +void ZonesManager::handleStartZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("StartZone call; Id=" << id ); + + Lock lock(mMutex); + + if (mZones.find(id) == mZones.end()) { + LOGE("Failed to start zone - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + LOGT("Start zone " << id); + + auto resultCallback = [this, id, result](bool succeeded) { + if (succeeded) { + focus(id); + result->setVoid(); + } else { + LOGE("Failed to start zone."); + result->setError(api::ERROR_INTERNAL, "Failed to start zone"); + } + }; + + mZones[id]->startAsync(resultCallback); +} + void ZonesManager::handleLockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("LockZone call; Id=" << id ); @@ -923,7 +986,7 @@ void ZonesManager::handleLockZoneCall(const std::string& id, } void ZonesManager::handleUnlockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result) + dbus::MethodResultBuilder::Pointer result) { LOGI("UnlockZone call; Id=" << id ); diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index 2ed5312..ccf834a 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -127,13 +127,13 @@ private: const std::string& resultPath); void notifyActiveZoneHandler(const std::string& caller, - const std::string& appliaction, - const std::string& message); + const std::string& appliaction, + const std::string& message); void displayOffHandler(const std::string& caller); void handleZoneMoveFileRequest(const std::string& srcZoneId, - const std::string& dstZoneId, - const std::string& path, - dbus::MethodResultBuilder::Pointer result); + const std::string& dstZoneId, + const std::string& path, + dbus::MethodResultBuilder::Pointer result); void handleProxyCall(const std::string& caller, const std::string& target, const std::string& targetBusName, @@ -165,15 +165,19 @@ private: const std::string& target, dbus::MethodResultBuilder::Pointer result); void handleSetActiveZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); void handleCreateZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); void handleDestroyZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); + void handleShutdownZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result); + void handleStartZoneCall(const std::string& id, + dbus::MethodResultBuilder::Pointer result); void handleLockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); void handleUnlockZoneCall(const std::string& id, - dbus::MethodResultBuilder::Pointer result); + dbus::MethodResultBuilder::Pointer result); }; diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 1e23e81..180b5eb 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -221,6 +221,20 @@ BOOST_AUTO_TEST_CASE(CreateZoneTest) vsm_client_free(client); } +BOOST_AUTO_TEST_CASE(StartShutdownZoneTest) +{ + const std::string newActiveZoneId = "ut-zones-manager-console1-dbus"; + + VsmClient client = vsm_client_create(); + VsmStatus status = vsm_connect(client); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + status = vsm_shutdown_zone(client, newActiveZoneId.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + status = vsm_start_zone(client, newActiveZoneId.c_str()); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, status); + vsm_client_free(client); +} + BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) { const std::string newActiveZoneId = "ut-zones-manager-console2-dbus"; diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 3c62478..06af8eb 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -310,7 +310,7 @@ public: } void callAsyncMethodCreateZone(const std::string& id, - const VoidResultCallback& result) + const VoidResultCallback& result) { auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); @@ -329,7 +329,7 @@ public: } void callAsyncMethodDestroyZone(const std::string& id, - const VoidResultCallback& result) + const VoidResultCallback& result) { auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); @@ -347,6 +347,44 @@ public: asyncResult); } + void callAsyncMethodShutdownZone(const std::string& id, + const VoidResultCallback& result) + { + auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { + BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); + result(); + }; + + assert(isHost()); + GVariant* parameters = g_variant_new("(s)", id.c_str()); + mClient->callMethodAsync(api::host::BUS_NAME, + api::host::OBJECT_PATH, + api::host::INTERFACE, + api::host::METHOD_SHUTDOWN_ZONE, + parameters, + "()", + asyncResult); + } + + void callAsyncMethodStartZone(const std::string& id, + const VoidResultCallback& result) + { + auto asyncResult = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) { + BOOST_CHECK(g_variant_is_of_type(asyncMethodCallResult.get(), G_VARIANT_TYPE_UNIT)); + result(); + }; + + assert(isHost()); + GVariant* parameters = g_variant_new("(s)", id.c_str()); + mClient->callMethodAsync(api::host::BUS_NAME, + api::host::OBJECT_PATH, + api::host::INTERFACE, + api::host::METHOD_START_ZONE, + parameters, + "()", + asyncResult); + } + void callMethodLockZone(const std::string& id) { assert(isHost()); @@ -904,8 +942,8 @@ BOOST_AUTO_TEST_CASE(GetZoneIdsTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; std::vector returnedIds = dbus.callMethodGetZoneIds(); BOOST_CHECK(std::is_permutation(returnedIds.begin(), @@ -921,8 +959,8 @@ BOOST_AUTO_TEST_CASE(GetActiveZoneIdTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; for (std::string& zoneId: zoneIds){ cm.focus(zoneId); @@ -941,8 +979,8 @@ BOOST_AUTO_TEST_CASE(SetActiveZoneTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; for (std::string& zoneId: zoneIds){ dbus.callMethodSetActiveZone(zoneId); @@ -1007,6 +1045,44 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); } +BOOST_AUTO_TEST_CASE(StartShutdownZoneTest) +{ + const std::string zone1 = "ut-zones-manager-console1-dbus"; + const std::string zone2 = "ut-zones-manager-console2-dbus"; + + ZonesManager cm(TEST_DBUS_CONFIG_PATH); + + Latch callDone; + auto resultCallback = [&]() { + callDone.set(); + }; + + DbusAccessory dbus(DbusAccessory::HOST_ID); + + // start zone1 + dbus.callAsyncMethodStartZone(zone1, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(cm.isRunning(zone1)); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1); + + // start zone2 + dbus.callAsyncMethodStartZone(zone2, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(cm.isRunning(zone2)); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone2); + + // shutdown zone2 + dbus.callAsyncMethodShutdownZone(zone2, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(!cm.isRunning(zone2)); + + // shutdown zone1 + dbus.callAsyncMethodShutdownZone(zone1, resultCallback); + BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + BOOST_CHECK(!cm.isRunning(zone1)); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); +} + BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) { ZonesManager cm(TEST_DBUS_CONFIG_PATH); @@ -1015,8 +1091,8 @@ BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) DbusAccessory dbus(DbusAccessory::HOST_ID); std::vector zoneIds = {"ut-zones-manager-console1-dbus", - "ut-zones-manager-console2-dbus", - "ut-zones-manager-console3-dbus"}; + "ut-zones-manager-console2-dbus", + "ut-zones-manager-console3-dbus"}; for (std::string& zoneId: zoneIds){ dbus.callMethodLockZone(zoneId); -- 2.7.4 From d35e4806694cbc6eadea4d0355b75b62de14fb2e Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Fri, 19 Dec 2014 14:56:56 +0100 Subject: [PATCH 03/16] Implement grant and revoke device [Bug/Feature] Was not implemented [Cause] N/A [Solution] N/A [Verification] Run tests, run grant/revoke device cli commands Change-Id: I4cfa67d99cb45d4747cfd780776bc2340c5f4535 --- cli/command-line-interface.cpp | 24 +++++++ cli/command-line-interface.hpp | 17 ++++- cli/main.cpp | 18 +++++ client/vasum-client-impl.cpp | 18 +++-- client/vasum-client-impl.hpp | 6 +- client/vasum-client.cpp | 10 +-- client/vasum-client.h | 8 +-- common/lxc/cgroup.cpp | 120 ++++++++++++++++++++++++++++++++++ common/lxc/cgroup.hpp | 49 ++++++++++++++ server/host-connection.cpp | 37 +++++++++++ server/host-connection.hpp | 21 ++++++ server/host-dbus-definitions.hpp | 11 ++++ server/zones-manager.cpp | 86 ++++++++++++++++++++++++ server/zones-manager.hpp | 7 ++ tests/unit_tests/client/ut-client.cpp | 39 ++++++++++- 15 files changed, 451 insertions(+), 20 deletions(-) create mode 100644 common/lxc/cgroup.cpp create mode 100644 common/lxc/cgroup.hpp diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 8120ca8..1222a86 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -31,6 +31,7 @@ #include #include #include +#include using namespace std; @@ -222,5 +223,28 @@ void lookup_zone_by_id(int pos, int argc, const char** argv) vsm_zone_free(zone); } +void grant_device(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + + uint32_t flags = O_RDWR; + one_shot(bind(vsm_grant_device, _1, argv[pos + 1], argv[pos + 2], flags)); +} + +void revoke_device(int pos, int argc, const char** argv) +{ + using namespace std::placeholders; + + if (argc <= pos + 2) { + throw runtime_error("Not enough parameters"); + } + + one_shot(bind(vsm_revoke_device, _1, argv[pos + 1], argv[pos + 2])); +} + } // namespace cli } // namespace vasum diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index 789abd6..6a655d8 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -25,6 +25,7 @@ #define CLI_COMMAND_LINE_INTERFACE_HPP #include +#include #include #include #include @@ -46,7 +47,7 @@ public: /** * @see CommandLineInterface::CommandLineInterface */ - typedef std::map ArgsSpec; + typedef std::vector> ArgsSpec; /** * Dummy constructor (for stl usage) @@ -152,6 +153,20 @@ void unlock_zone(int pos, int argc, const char** argv); */ void lookup_zone_by_id(int pos, int argc, const char** argv); +/** + * Parses command line arguments and call vsm_grant_device + * + * @see vsm_grant_device + */ +void grant_device(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_revoke_device + * + * @see vsm_revoke_device + */ +void revoke_device(int pos, int argc, const char** argv); + } // namespace cli } // namespace vasum diff --git a/cli/main.cpp b/cli/main.cpp index b91d4e1..1afe134 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -98,6 +98,24 @@ std::map commands = { "Prints informations about zone", {{"zone_id", "id zone name"}} } + }, + { + "grant_device", { + grant_device, + "grant_device zone_id device_name", + "Grants access to the given device", + {{"zone_id", "id zone name"}, + {"device_name", " device name"}} + } + }, + { + "revoke_device", { + revoke_device, + "revoke_device zone_id device_name", + "Revokes access to the given device", + {{"zone_id", "id zone name"}, + {"device_name", " device name"}} + } } }; diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 749b155..0af5269 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -515,16 +515,22 @@ VsmStatus Client::vsm_del_state_callback(VsmSubscriptionId subscriptionId) noexc return signalUnsubscribe(subscriptionId); } -VsmStatus Client::vsm_zone_grant_device(const char*, const char*, uint32_t) noexcept +VsmStatus Client::vsm_grant_device(const char* id, const char* device, uint32_t flags) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + assert(device); + + GVariant* args_in = g_variant_new("(ssu)", id, device, flags); + return callMethod(HOST_INTERFACE, api::host::METHOD_GRANT_DEVICE, args_in); } -VsmStatus Client::vsm_revoke_device(const char*, const char*) noexcept +VsmStatus Client::vsm_revoke_device(const char* id, const char* device) noexcept { - mStatus = Status(VSMCLIENT_OTHER_ERROR, "Not implemented"); - return vsm_get_status(); + assert(id); + assert(device); + + GVariant* args_in = g_variant_new("(ss)", id, device); + return callMethod(HOST_INTERFACE, api::host::METHOD_REVOKE_DEVICE, args_in); } VsmStatus Client::vsm_zone_get_netdevs(const char*, VsmArrayString*) noexcept diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 66ffa82..f545d77 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -183,9 +183,9 @@ public: /** * @see ::vsm_del_state_callback */ - VsmStatus vsm_zone_grant_device(const char* id, - const char* device, - uint32_t flags) noexcept; + VsmStatus vsm_grant_device(const char* id, + const char* device, + uint32_t flags) noexcept; /** * @see ::vsm_revoke_device diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index 2102432..00a4dfb 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -196,12 +196,12 @@ API VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscri return getClient(client).vsm_del_state_callback(subscriptionId); } -API VsmStatus vsm_zone_grant_device(VsmClient client, - const char* id, - const char* device, - uint32_t flags) +API VsmStatus vsm_grant_device(VsmClient client, + const char* id, + const char* device, + uint32_t flags) { - return getClient(client).vsm_zone_grant_device(id, device, flags); + return getClient(client).vsm_grant_device(id, device, flags); } API VsmStatus vsm_revoke_device(VsmClient client, const char* id, const char* device) diff --git a/client/vasum-client.h b/client/vasum-client.h index eb4cbba..eba04e6 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -463,10 +463,10 @@ VsmStatus vsm_del_state_callback(VsmClient client, VsmSubscriptionId subscriptio * @param[in] flags access flags * @return status of this function call */ -VsmStatus vsm_zone_grant_device(VsmClient client, - const char* zone, - const char* device, - uint32_t flags); +VsmStatus vsm_grant_device(VsmClient client, + const char* zone, + const char* device, + uint32_t flags); /** * Revoke access to device diff --git a/common/lxc/cgroup.cpp b/common/lxc/cgroup.cpp new file mode 100644 index 0000000..807ddff --- /dev/null +++ b/common/lxc/cgroup.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Lxc cgroup stuff + */ + + +#include "config.hpp" +#include "logger/logger.hpp" +#include "utils/fs.hpp" + +#include +#include + + +namespace vasum { +namespace lxc { + +namespace { + +std::string flagsToPermissions(bool grant, uint32_t flags) +{ + if (!grant) { + return "rwm"; + } + switch (flags) { + case O_RDWR: + return "rwm"; + case O_RDONLY: + return "rm"; + case O_WRONLY: + return "wm"; + default: + return std::string(); + } +} + +} // namespace + +bool setCgroup(const std::string& zoneName, + const std::string& cgroupName, + const std::string& value) +{ + const std::string path = "/sys/fs/cgroup/devices/lxc/" + zoneName + "/" + cgroupName; + LOGD("Set '" << value << "' to " << path); + return utils::saveFileContent(path, value); +} + +bool isDevice(const std::string& device) +{ + struct stat devStat; + if (::stat(device.c_str(), &devStat) == -1) { + LOGD("Cannot access: " << device); + return false; + } + if (!S_ISCHR(devStat.st_mode) && !S_ISBLK(devStat.st_mode)) { + LOGD("Not a device: " << device); + return false; + } + return true; +} + +bool setDeviceAccess(const std::string& zoneName, + const std::string& device, + bool grant, + uint32_t flags) +{ + struct stat devStat; + if (::stat(device.c_str(), &devStat) == -1) { + LOGD("Cannot access: " << device); + return false; + } + + char type; + if (S_ISCHR(devStat.st_mode)) { + type = 'c'; + } else if (S_ISBLK(devStat.st_mode)) { + type = 'b'; + } else { + LOGD("Not a device: " << device); + return false; + } + + std::string perm = flagsToPermissions(grant, flags); + if (perm.empty()) { + LOGD("Invalid flags"); + return false; + } + + int major = major(devStat.st_rdev); + int minor = minor(devStat.st_rdev); + + char value[100]; + snprintf(value, sizeof(value), "%c %d:%d %s", type, major, minor, perm.c_str()); + + std::string name = grant ? "devices.allow" : "devices.deny"; + return setCgroup(zoneName, name, value); +} + + +} // namespace lxc +} // namespace vasum diff --git a/common/lxc/cgroup.hpp b/common/lxc/cgroup.hpp new file mode 100644 index 0000000..2592054 --- /dev/null +++ b/common/lxc/cgroup.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Lxc cgroup stuff + */ + +#ifndef COMMON_LXC_CGROUP_HPP +#define COMMON_LXC_CGROUP_HPP + +#include + +namespace vasum { +namespace lxc { + +bool setCgroup(const std::string& zoneName, + const std::string& cgroupName, + const std::string& value); + +bool isDevice(const std::string& device); + +bool setDeviceAccess(const std::string& zoneName, + const std::string& device, + bool grant, + uint32_t flags); + + +} // namespace lxc +} // namespace vasum + + +#endif // COMMON_LXC_CGROUP_HPP diff --git a/server/host-connection.cpp b/server/host-connection.cpp index 621e3a2..db6130f 100644 --- a/server/host-connection.cpp +++ b/server/host-connection.cpp @@ -180,6 +180,16 @@ void HostConnection::setUnlockZoneCallback(const UnlockZoneCallback& callback) mUnlockZoneCallback = callback; } +void HostConnection::setGrantDeviceCallback(const GrantDeviceCallback& callback) +{ + mGrantDeviceCallback = callback; +} + +void HostConnection::setRevokeDeviceCallback(const RevokeDeviceCallback& callback) +{ + mRevokeDeviceCallback = callback; +} + void HostConnection::onMessageCall(const std::string& objectPath, const std::string& interface, @@ -316,6 +326,7 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mCreateZoneCallback){ mCreateZoneCallback(id, result); } + return; } if (methodName == api::host::METHOD_DESTROY_ZONE) { @@ -325,6 +336,7 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mDestroyZoneCallback){ mDestroyZoneCallback(id, result); } + return; } if (methodName == api::host::METHOD_SHUTDOWN_ZONE) { @@ -352,6 +364,7 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mLockZoneCallback){ mLockZoneCallback(id, result); } + return; } if (methodName == api::host::METHOD_UNLOCK_ZONE) { @@ -361,6 +374,30 @@ void HostConnection::onMessageCall(const std::string& objectPath, if (mUnlockZoneCallback){ mUnlockZoneCallback(id, result); } + return; + } + + if (methodName == api::host::METHOD_GRANT_DEVICE) { + const gchar* id = NULL; + const gchar* device = NULL; + uint32_t flags; + g_variant_get(parameters, "(&s&su)", &id, &device, &flags); + + if (mGrantDeviceCallback){ + mGrantDeviceCallback(id, device, flags, result); + } + return; + } + + if (methodName == api::host::METHOD_REVOKE_DEVICE) { + const gchar* id = NULL; + const gchar* device = NULL; + g_variant_get(parameters, "(&s&s)", &id, &device); + + if (mRevokeDeviceCallback){ + mRevokeDeviceCallback(id, device, result); + } + return; } } diff --git a/server/host-connection.hpp b/server/host-connection.hpp index 49e2a08..db8cf34 100644 --- a/server/host-connection.hpp +++ b/server/host-connection.hpp @@ -101,6 +101,15 @@ public: typedef std::function UnlockZoneCallback; + typedef std::function GrantDeviceCallback; + typedef std::function RevokeDeviceCallback; /** * Register proxy call callback @@ -183,6 +192,16 @@ public: void setUnlockZoneCallback(const UnlockZoneCallback& callback); /** + * Register a callback called to grant device + */ + void setGrantDeviceCallback(const GrantDeviceCallback& callback); + + /** + * Register a callback called to revoke device + */ + void setRevokeDeviceCallback(const RevokeDeviceCallback& callback); + + /** * Make a proxy call */ void proxyCallAsync(const std::string& busName, @@ -213,6 +232,8 @@ private: StartZoneCallback mStartZoneCallback; LockZoneCallback mLockZoneCallback; UnlockZoneCallback mUnlockZoneCallback; + GrantDeviceCallback mGrantDeviceCallback; + RevokeDeviceCallback mRevokeDeviceCallback; void onNameAcquired(); void onNameLost(); diff --git a/server/host-dbus-definitions.hpp b/server/host-dbus-definitions.hpp index 74ba96d..e17261c 100644 --- a/server/host-dbus-definitions.hpp +++ b/server/host-dbus-definitions.hpp @@ -52,6 +52,8 @@ const std::string METHOD_SHUTDOWN_ZONE = "ShutdownZone"; const std::string METHOD_START_ZONE = "StartZone"; const std::string METHOD_LOCK_ZONE = "LockZone"; const std::string METHOD_UNLOCK_ZONE = "UnlockZone"; +const std::string METHOD_GRANT_DEVICE = "GrantDevice"; +const std::string METHOD_REVOKE_DEVICE = "RevokeDevice"; const std::string SIGNAL_ZONE_DBUS_STATE = "ZoneDbusState"; @@ -122,6 +124,15 @@ const std::string DEFINITION = " " " " " " + " " + " " + " " + " " + " " + " " + " " + " " + " " " " " " " " diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index dcfa04c..ff6fb7e 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -29,6 +29,7 @@ #include "zone-dbus-definitions.hpp" #include "zones-manager.hpp" #include "zone-admin.hpp" +#include "lxc/cgroup.hpp" #include "exception.hpp" #include "utils/paths.hpp" @@ -132,6 +133,12 @@ ZonesManager::ZonesManager(const std::string& managerConfigPath) mHostConnection.setUnlockZoneCallback(bind(&ZonesManager::handleUnlockZoneCall, this, _1, _2)); + mHostConnection.setGrantDeviceCallback(bind(&ZonesManager::handleGrantDeviceCall, + this, _1, _2, _3, _4)); + + mHostConnection.setRevokeDeviceCallback(bind(&ZonesManager::handleRevokeDeviceCall, + this, _1, _2, _3)); + for (auto& zoneConfig : mConfig.zoneConfigs) { createZone(zoneConfig); } @@ -1018,4 +1025,83 @@ void ZonesManager::handleUnlockZoneCall(const std::string& id, result->setVoid(); } +void ZonesManager::handleGrantDeviceCall(const std::string& id, + const std::string& device, + uint32_t flags, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("GrantDevice call; id=" << id << "; dev=" << device); + + Lock lock(mMutex); + + auto iter = mZones.find(id); + if (iter == mZones.end()) { + LOGE("Failed to grant device - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + auto& zone = *iter->second; + if (!zone.isRunning() && !zone.isPaused()) { + LOGE("Zone id=" << id << " is not running"); + result->setError(api::ERROR_INVALID_STATE, "Zone is not running"); + return; + } + + std::string devicePath = "/dev/" + device; + + if (!lxc::isDevice(devicePath)) { + LOGE("Failed to grant device - cannot acces device: " << device); + result->setError(api::ERROR_FORBIDDEN, "Cannot access device"); + return; + } + + // assume device node is created inside zone + if (!lxc::setDeviceAccess(id, devicePath, true, flags)) { + LOGE("Failed to grant device: " << device << " for zone: " << id); + result->setError(api::ERROR_INTERNAL, "Cannot grant device"); + return; + } + + result->setVoid(); +} + +void ZonesManager::handleRevokeDeviceCall(const std::string& id, + const std::string& device, + dbus::MethodResultBuilder::Pointer result) +{ + LOGI("RevokeDevice call; id=" << id << "; dev=" << device); + + Lock lock(mMutex); + + auto iter = mZones.find(id); + if (iter == mZones.end()) { + LOGE("Failed to revoke device - no such zone id: " << id); + result->setError(api::ERROR_INVALID_ID, "No such zone id"); + return; + } + + auto& zone = *iter->second; + if (!zone.isRunning() && !zone.isPaused()) { + LOGE("Zone id=" << id << " is not running"); + result->setError(api::ERROR_INVALID_STATE, "Zone is not running"); + return; + } + std::string devicePath = "/dev/" + device; + + if (!lxc::isDevice(devicePath)) { + LOGE("Failed to revoke device - cannot acces device: " << device); + result->setError(api::ERROR_FORBIDDEN, "Cannot access device"); + return; + } + + if (!lxc::setDeviceAccess(id, devicePath, false, 0)) { + LOGE("Failed to revoke device: " << device << " for zone: " << id); + result->setError(api::ERROR_INTERNAL, "Cannot revoke device"); + return; + } + + result->setVoid(); +} + } // namespace vasum diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index ccf834a..a0a2695 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -178,6 +178,13 @@ private: dbus::MethodResultBuilder::Pointer result); void handleUnlockZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); + void handleGrantDeviceCall(const std::string& id, + const std::string& device, + uint32_t flags, + dbus::MethodResultBuilder::Pointer result); + void handleRevokeDeviceCall(const std::string& id, + const std::string& device, + dbus::MethodResultBuilder::Pointer result); }; diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index 180b5eb..9536ccb 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -123,7 +123,22 @@ int getArrayStringLength(VsmArrayString astring, int max_len = -1) } // namespace -BOOST_FIXTURE_TEST_SUITE(Client, Fixture) +// make nice BOOST_*_EQUAL output +// (does not work inside anonymous namespace) +std::ostream& operator<<(std::ostream& out, VsmStatus status) +{ + switch(status) { + case VSMCLIENT_CUSTOM_ERROR: return out << "CUSTOM_ERROR"; + case VSMCLIENT_IO_ERROR: return out << "IO_ERROR"; + case VSMCLIENT_OPERATION_FAILED: return out << "OPERATION_FAILED"; + case VSMCLIENT_INVALID_ARGUMENT: return out << "INVALID_ARGUMENT"; + case VSMCLIENT_OTHER_ERROR: return out << "OTHER_ERROR"; + case VSMCLIENT_SUCCESS: return out << "SUCCESS"; + default: return out << "UNKNOWN(" << (int)status << ")"; + }; +} + +BOOST_FIXTURE_TEST_SUITE(ClientSuite, Fixture) BOOST_AUTO_TEST_CASE(NotRunningServerTest) { @@ -356,4 +371,26 @@ BOOST_AUTO_TEST_CASE(GetZoneIdByPidTest2) } } +BOOST_AUTO_TEST_CASE(GrantRevokeTest) +{ + const std::string zoneId = "ut-zones-manager-console2-dbus"; + const std::string dev = "tty3"; + + VsmClient client = vsm_client_create(); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_connect(client)); + + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0)); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_revoke_device(client, zoneId.c_str(), dev.c_str())); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_lock_zone(client, zoneId.c_str())); + BOOST_CHECK_EQUAL(VSMCLIENT_SUCCESS, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0)); + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_unlock_zone(client, zoneId.c_str())); + + BOOST_REQUIRE_EQUAL(VSMCLIENT_SUCCESS, vsm_shutdown_zone(client, zoneId.c_str())); + BOOST_CHECK_EQUAL(VSMCLIENT_CUSTOM_ERROR, vsm_grant_device(client, zoneId.c_str(), dev.c_str(), 0)); + BOOST_CHECK_EQUAL(VSMCLIENT_CUSTOM_ERROR, vsm_revoke_device(client, zoneId.c_str(), dev.c_str())); + + vsm_client_free(client); +} + BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From c26341627e1591ddb55a77db0f75df18a2ba4599 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Tue, 23 Dec 2014 16:30:14 +0100 Subject: [PATCH 04/16] Fix creating zone when USE_EXEC is defined [Bug/Feature] Broken zone creation using lxc-create [Cause] N/A [Solution] N/A [Verification] Compile with -DUSE_EXEC, sun server and create zone Change-Id: I164ea2ea17e93e4e22e7b74d2f8539484cedea2f --- common/lxc/zone.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common/lxc/zone.cpp b/common/lxc/zone.cpp index b142de4..e4371f8 100644 --- a/common/lxc/zone.cpp +++ b/common/lxc/zone.cpp @@ -133,6 +133,10 @@ bool LxcZone::create(const std::string& templatePath, const char* const* argv) .add("-t").add(templatePath.c_str()) .add("-P").add(mLxcContainer->config_path); + if (*argv) { + args.add("--"); + } + while (*argv) { args.add(*argv++); } @@ -177,8 +181,11 @@ bool LxcZone::start(const char* const* argv) args.add("lxc-start") .add("-d") .add("-n").add(mLxcContainer->name) - .add("-P").add(mLxcContainer->config_path) - .add("--"); + .add("-P").add(mLxcContainer->config_path); + + if (*argv) { + args.add("--"); + } while (*argv) { args.add(*argv++); -- 2.7.4 From 5be6a32fead5470d8cae59ecb061105819d4b558 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Mon, 22 Dec 2014 12:01:20 +0100 Subject: [PATCH 05/16] Debug utility class: SameThreadGuard [Bug/Feature] A utility for checking the code against invalid assumes about synchronization needs. [Cause] N/A [Solution] N/A [Verification] Run tests Change-Id: I880f9c334d8d461e2472052db5244d9f5eab1bb8 --- common/utils/same-thread-guard.cpp | 75 +++++++++++++++++++++++ common/utils/same-thread-guard.hpp | 80 +++++++++++++++++++++++++ tests/unit_tests/utils/ut-same-thread-guard.cpp | 69 +++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 common/utils/same-thread-guard.cpp create mode 100644 common/utils/same-thread-guard.hpp create mode 100644 tests/unit_tests/utils/ut-same-thread-guard.cpp diff --git a/common/utils/same-thread-guard.cpp b/common/utils/same-thread-guard.cpp new file mode 100644 index 0000000..5b54c73 --- /dev/null +++ b/common/utils/same-thread-guard.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Same thread guard + */ + +#include "config.hpp" +#include "utils/same-thread-guard.hpp" + +#ifdef ENABLE_SAME_THREAD_GUARD + +#include "logger/logger.hpp" +#include "logger/formatter.hpp" + +namespace vasum { +namespace utils { + +namespace { + +typedef decltype(logger::LogFormatter::getCurrentThread()) ThreadId; +const ThreadId NOT_SET = 0; + +ThreadId getCurrentThreadId() { + // use the same thread id numbering mechanism as in logger + // to allow analyse accesses in log + return logger::LogFormatter::getCurrentThread(); +} + +} // namespace + +SameThreadGuard::SameThreadGuard() : mThreadId(NOT_SET) +{ + static_assert(std::is_same::value, + "thread id type mismatch"); +} + +bool SameThreadGuard::check() +{ + const ThreadId thisThreadId = getCurrentThreadId(); + + ThreadId saved = NOT_SET; + if (!mThreadId.compare_exchange_strong(saved, thisThreadId) && saved != thisThreadId) { + LOGE("Detected thread id mismatch; saved: " << saved << "; current: " << thisThreadId); + return false; + } + return true; +} + +void SameThreadGuard::reset() +{ + mThreadId.store(NOT_SET); +} + +} // namespace utils +} // namespace vasum + +#endif // ENABLE_SAME_THREAD_GUARD diff --git a/common/utils/same-thread-guard.hpp b/common/utils/same-thread-guard.hpp new file mode 100644 index 0000000..fd697fd --- /dev/null +++ b/common/utils/same-thread-guard.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Same thread guard + */ + +#ifndef COMMON_UTILS_SAME_THREAD_GUARD_HPP +#define COMMON_UTILS_SAME_THREAD_GUARD_HPP + +#ifndef NDEBUG +#define ENABLE_SAME_THREAD_GUARD +#endif + +#ifdef ENABLE_SAME_THREAD_GUARD +#include +#include +#endif + +namespace vasum { +namespace utils { + +/** + * Same thread guard. + * There are two purposes of this guard: + * - reports invalid assumptions about synchronization needs (only in debug builds) + * - acts as an annotation in the source code about the thread safety + * + * Usage example: + * ASSERT_SAME_THREAD(workerThreadGuard); + */ +class SameThreadGuard { +public: +#ifdef ENABLE_SAME_THREAD_GUARD +# define ASSERT_SAME_THREAD(g) assert(g.check()) + SameThreadGuard(); + + /** + * On the first call it remembers the current thread id. + * On the next call it verifies that current thread is the same as before. + */ + bool check(); + + /** + * Reset thread id + */ + void reset(); + +private: + std::atomic mThreadId; + +#else // ENABLE_SAME_THREAD_GUARD +# define ASSERT_SAME_THREAD(g) + bool check() {return true;} + void reset() {} +#endif // ENABLE_SAME_THREAD_GUARD +}; + +} // namespace utils +} // namespace vasum + + +#endif // COMMON_UTILS_SAME_THREAD_GUARD_HPP diff --git a/tests/unit_tests/utils/ut-same-thread-guard.cpp b/tests/unit_tests/utils/ut-same-thread-guard.cpp new file mode 100644 index 0000000..7dbba57 --- /dev/null +++ b/tests/unit_tests/utils/ut-same-thread-guard.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Unit tests of same thread guard + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "utils/same-thread-guard.hpp" +#include + +#ifdef ENABLE_SAME_THREAD_GUARD + +BOOST_AUTO_TEST_SUITE(SameThreadGuardSuite) + +using namespace vasum::utils; + +BOOST_AUTO_TEST_CASE(SimpleTest) +{ + SameThreadGuard guard; + BOOST_CHECK(guard.check()); + BOOST_CHECK(guard.check()); + guard.reset(); + BOOST_CHECK(guard.check()); + BOOST_CHECK(guard.check()); +} + +BOOST_AUTO_TEST_CASE(ThreadTest) +{ + SameThreadGuard guard; + + std::thread([&] { + BOOST_CHECK(guard.check()); + }).join(); + + BOOST_CHECK(!guard.check()); + BOOST_CHECK(!guard.check()); + + guard.reset(); + BOOST_CHECK(guard.check()); + + std::thread([&] { + BOOST_CHECK(!guard.check()); + }).join(); +} + +BOOST_AUTO_TEST_SUITE_END() + +#endif // ENABLE_SAME_THREAD_GUARD -- 2.7.4 From c799942eb46d800de8145478501d7a1f6c930816 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Wed, 31 Dec 2014 13:28:54 +0100 Subject: [PATCH 06/16] Small code cleanup [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Run tests Change-Id: I45d18c987c7ea90276a3cf6e1eac3760a9df1db7 --- common/lxc/cgroup.cpp | 7 ++++--- common/utils/glib-loop.cpp | 4 +--- server/zone-provision.hpp | 2 +- tests/unit_tests/dbus/ut-connection.cpp | 7 ++++++- tests/unit_tests/utils/ut-worker.cpp | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/common/lxc/cgroup.cpp b/common/lxc/cgroup.cpp index 807ddff..62f3184 100644 --- a/common/lxc/cgroup.cpp +++ b/common/lxc/cgroup.cpp @@ -24,6 +24,7 @@ #include "config.hpp" +#include "lxc/cgroup.hpp" #include "logger/logger.hpp" #include "utils/fs.hpp" @@ -105,11 +106,11 @@ bool setDeviceAccess(const std::string& zoneName, return false; } - int major = major(devStat.st_rdev); - int minor = minor(devStat.st_rdev); + unsigned int major = major(devStat.st_rdev); + unsigned int minor = minor(devStat.st_rdev); char value[100]; - snprintf(value, sizeof(value), "%c %d:%d %s", type, major, minor, perm.c_str()); + snprintf(value, sizeof(value), "%c %u:%u %s", type, major, minor, perm.c_str()); std::string name = grant ? "devices.allow" : "devices.deny"; return setCgroup(zoneName, name, value); diff --git a/common/utils/glib-loop.cpp b/common/utils/glib-loop.cpp index fb2d952..dd82204 100644 --- a/common/utils/glib-loop.cpp +++ b/common/utils/glib-loop.cpp @@ -48,9 +48,7 @@ ScopedGlibLoop::ScopedGlibLoop() #if !GLIB_CHECK_VERSION(2,36,0) g_type_init(); #endif - mLoopThread = std::thread([this] { - g_main_loop_run(mLoop.get()); - }); + mLoopThread = std::thread(g_main_loop_run, mLoop.get()); } ScopedGlibLoop::~ScopedGlibLoop() diff --git a/server/zone-provision.hpp b/server/zone-provision.hpp index d7cc1e4..c87fbe9 100644 --- a/server/zone-provision.hpp +++ b/server/zone-provision.hpp @@ -43,7 +43,7 @@ class ZoneProvision { public: /** * ZoneProvision constructor - * @param zonesPath directory where zones are defined (lxc configs, rootfs etc) + * @param zonePath directory where zones are defined (lxc configs, rootfs etc) */ ZoneProvision(const std::string& zonePath, const std::vector& validLinkPrefixes); diff --git a/tests/unit_tests/dbus/ut-connection.cpp b/tests/unit_tests/dbus/ut-connection.cpp index 3e335ed..d4eb1de 100644 --- a/tests/unit_tests/dbus/ut-connection.cpp +++ b/tests/unit_tests/dbus/ut-connection.cpp @@ -110,6 +110,12 @@ BOOST_AUTO_TEST_CASE(NoDbusTest) BOOST_CHECK_THROW(DbusConnection::create(DBUS_ADDRESS), DbusIOException); } +BOOST_AUTO_TEST_CASE(ConnectionTest) +{ + ScopedGlibLoop loop; + DbusConnection::Pointer connSystem = DbusConnection::createSystem(); +} + BOOST_AUTO_TEST_CASE(SimpleTest) { ScopedDbusDaemon daemon; @@ -273,7 +279,6 @@ BOOST_AUTO_TEST_CASE(RegisterObjectTest) BOOST_AUTO_TEST_CASE(IntrospectSystemTest) { - ScopedDbusDaemon daemon; ScopedGlibLoop loop; DbusConnection::Pointer conn = DbusConnection::createSystem(); std::string xml = conn->introspect("org.freedesktop.DBus", "/org/freedesktop/DBus"); diff --git a/tests/unit_tests/utils/ut-worker.cpp b/tests/unit_tests/utils/ut-worker.cpp index 280889b..91fa6b8 100644 --- a/tests/unit_tests/utils/ut-worker.cpp +++ b/tests/unit_tests/utils/ut-worker.cpp @@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(NoCopyTest) struct Task { Counter& count; - Task(Counter& c) : count(c) {}; + Task(Counter& c) : count(c) {} Task(const Task& t) : count(t.count) {++count;} Task(Task&& r) : count(r.count) {} Task& operator=(const Task&) = delete; -- 2.7.4 From 1d2b75e9a25d971eefff29adc5fd4166e43cf827 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 16 Dec 2014 14:09:33 +0100 Subject: [PATCH 07/16] IPC: External polling loop with a Client [Bug/Feature] Using GMainLoop with a Client is possible. Fixed some buggs [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Iab3350b400739bb951d84e0d6b7de15d0cccf1d3 --- common/ipc/client.cpp | 38 ++++++++-- common/ipc/client.hpp | 24 +++++++ common/ipc/internals/acceptor.cpp | 3 +- common/ipc/internals/processor.cpp | 67 +++++++++++++++-- common/ipc/internals/processor.hpp | 7 ++ common/ipc/ipc-gsource.cpp | 31 +++++--- common/ipc/ipc-gsource.hpp | 16 +++-- common/ipc/service.cpp | 6 +- common/utils/latch.hpp | 8 +-- common/utils/signal.cpp | 63 ++++++++++++++++ common/utils/signal.hpp | 37 ++++++++++ server/CMakeLists.txt | 7 +- tests/unit_tests/CMakeLists.txt | 6 ++ tests/unit_tests/ipc/ut-ipc.cpp | 144 ++++++++++++++++++++++++++++++------- zone-daemon/CMakeLists.txt | 6 ++ 15 files changed, 404 insertions(+), 59 deletions(-) create mode 100644 common/utils/signal.cpp create mode 100644 common/utils/signal.hpp diff --git a/common/ipc/client.cpp b/common/ipc/client.cpp index 835020d..3187f15 100644 --- a/common/ipc/client.cpp +++ b/common/ipc/client.cpp @@ -48,16 +48,21 @@ Client::~Client() LOGD("Destroyed client"); } -void Client::start() +void Client::connect() { - LOGD("Starting client..."); - // Initialize the connection with the server LOGD("Connecting to " + mSocketPath); auto socketPtr = std::make_shared(Socket::connectSocket(mSocketPath)); mServiceFD = mProcessor.addPeer(socketPtr); +} + +void Client::start() +{ + LOGD("Starting client..."); - // Start listening + connect(); + + // Start polling thread mProcessor.start(); LOGD("Started client"); @@ -75,6 +80,31 @@ void Client::stop() LOGD("Stopped"); } +std::vector Client::getFDs() +{ + std::vector fds; + fds.push_back(mProcessor.getEventFD()); + fds.push_back(mServiceFD); + + return fds; +} + +void Client::handle(const FileDescriptor fd, const short pollEvent) +{ + if (fd == mProcessor.getEventFD() && (pollEvent & POLLIN)) { + mProcessor.handleEvent(); + return; + + } else if (pollEvent & POLLIN) { + mProcessor.handleInput(fd); + return; + + } else if (pollEvent & POLLHUP) { + mProcessor.handleLostConnection(fd); + return; + } +} + void Client::setNewPeerCallback(const PeerCallback& newPeerCallback) { mProcessor.setNewPeerCallback(newPeerCallback); diff --git a/common/ipc/client.hpp b/common/ipc/client.hpp index 5751812..b5b00e5 100644 --- a/common/ipc/client.hpp +++ b/common/ipc/client.hpp @@ -55,6 +55,13 @@ public: Client& operator=(const Client&) = delete; /** + * Places a connection request in the internal event queue. + * + * Used with an external polling loop. + */ + void connect(); + + /** * Starts the worker thread */ void start(); @@ -70,6 +77,23 @@ public: void stop(); /** + * Used with an external polling loop + * + * @return vector of internal file descriptors + */ + std::vector getFDs(); + + /** + * Used with an external polling loop. + * Handles one event from the file descriptor. + * + * @param fd file descriptor + * @param pollEvent event on the fd. Defined in poll.h + * + */ + void handle(const FileDescriptor fd, const short pollEvent); + + /** * Set the callback called for each new connection to a peer * * @param newPeerCallback the callback diff --git a/common/ipc/internals/acceptor.cpp b/common/ipc/internals/acceptor.cpp index 1eab1c2..627e1fe 100644 --- a/common/ipc/internals/acceptor.cpp +++ b/common/ipc/internals/acceptor.cpp @@ -67,12 +67,13 @@ void Acceptor::start() 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"); } diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 72a1788..22aa38e 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -27,9 +27,11 @@ #include "ipc/exception.hpp" #include "ipc/internals/processor.hpp" #include "ipc/internals/utils.hpp" +#include "utils/signal.hpp" #include #include +#include #include #include @@ -58,8 +60,9 @@ Processor::Processor(const PeerCallback& newPeerCallback, mMaxNumberOfPeers(maxNumberOfPeers) { LOGT("Creating Processor"); - using namespace std::placeholders; + utils::signalBlock(SIGPIPE); + using namespace std::placeholders; addMethodHandlerInternal(REGISTER_SIGNAL_METHOD_ID, std::bind(&Processor::onNewSignals, this, _1, _2)); @@ -73,6 +76,7 @@ Processor::~Processor() } catch (IPCException& e) { LOGE("Error in Processor's destructor: " << e.what()); } + LOGT("Destroyed Processor"); } @@ -93,10 +97,12 @@ void Processor::start() void Processor::stop() { LOGT("Stopping Processor"); + if (isStarted()) { mEventQueue.send(Event::FINISH); mThread.join(); } + LOGT("Stopped Processor"); } @@ -167,7 +173,10 @@ void Processor::removePeerInternal(const FileDescriptor peerFD, Status status) LOGW("Removing peer. ID: " << peerFD); { Lock lock(mSocketsMutex); - mSockets.erase(peerFD); + if (!mSockets.erase(peerFD)) { + LOGW("No such peer. Another thread called removePeerInternal"); + return; + } // Remove from signal addressees for (auto it = mSignalsPeers.begin(); it != mSignalsPeers.end();) { @@ -269,8 +278,6 @@ void Processor::run() } } - - cleanCommunication(); } bool Processor::handleLostConnections() @@ -327,6 +334,8 @@ bool Processor::handleInput(const FileDescriptor peerFD) std::shared_ptr socketPtr; try { + // Get the peer's socket + Lock lock(mSocketsMutex); socketPtr = mSockets.at(peerFD); } catch (const std::out_of_range&) { LOGE("No such peer: " << peerFD); @@ -497,7 +506,10 @@ bool Processor::handleEvent() switch (mEventQueue.receive()) { case Event::FINISH: { LOGD("Event FINISH"); + mIsRunning = false; + cleanCommunication(); + return false; } @@ -607,6 +619,15 @@ bool Processor::onCall() LOGT("Handle call (from another thread) to send a message."); CallQueue::Call call = getCall(); + if (call.parse && call.process) { + return onMethodCall(call); + } else { + return onSignalCall(call); + } +} + +bool Processor::onSignalCall(CallQueue::Call& call) +{ std::shared_ptr socketPtr; try { // Get the peer's socket @@ -614,11 +635,43 @@ bool Processor::onCall() socketPtr = mSockets.at(call.peerFD); } catch (const std::out_of_range&) { LOGE("Peer disconnected. No socket with a peerFD: " << call.peerFD); + return false; + } + + try { + // Send the call with the socket + Socket::Guard guard = socketPtr->getGuard(); + socketPtr->write(&call.methodID, sizeof(call.methodID)); + socketPtr->write(&call.messageID, sizeof(call.messageID)); + call.serialize(socketPtr->getFD(), call.data); + } catch (const std::exception& e) { + LOGE("Error during sending a signal: " << e.what()); + + removePeerInternal(call.peerFD, Status::SERIALIZATION_ERROR); + return true; + } + + return false; + +} + +bool Processor::onMethodCall(CallQueue::Call& call) +{ + std::shared_ptr socketPtr; + try { + // Get the peer's socket + Lock lock(mSocketsMutex); + socketPtr = mSockets.at(call.peerFD); + } catch (const std::out_of_range&) { + LOGE("Peer disconnected. No socket with a peerFD: " << call.peerFD); + + // Pass the error to the processing callback IGNORE_EXCEPTIONS(call.process(Status::PEER_DISCONNECTED, call.data)); + return false; } - if (call.parse && call.process) { + { // Set what to do with the return message, but only if needed Lock lock(mReturnCallbacksMutex); if (mReturnCallbacks.count(call.messageID) != 0) { @@ -636,9 +689,9 @@ bool Processor::onCall() socketPtr->write(&call.messageID, sizeof(call.messageID)); call.serialize(socketPtr->getFD(), call.data); } catch (const std::exception& e) { - LOGE("Error during sending a message: " << e.what()); + LOGE("Error during sending a method: " << e.what()); - // Inform about the error + // Inform about the error, IGNORE_EXCEPTIONS(mReturnCallbacks[call.messageID].process(Status::SERIALIZATION_ERROR, call.data)); { diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index d33d12b..728b8d2 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -76,6 +76,11 @@ const unsigned int DEFAULT_METHOD_TIMEOUT = 1000; * - callbacks for serialization/parsing * - store Sockets in a vector, maybe SocketStore? * - fix valgrind tests +* - poll loop outside. +* - waiting till the EventQueue is empty before leaving stop() +* - no new events added after stop() called +* - when using IPCGSource: addFD and removeFD can be called from addPeer removePeer callbacks, but +* there is no mechanism to ensure the IPCSource exists.. therefore SIGSEGV :) * * */ @@ -414,6 +419,8 @@ private: void run(); bool onCall(); + bool onSignalCall(CallQueue::Call& call); + bool onMethodCall(CallQueue::Call& call); bool onNewPeer(); bool onRemovePeer(); bool handleLostConnections(); diff --git a/common/ipc/ipc-gsource.cpp b/common/ipc/ipc-gsource.cpp index f5cdbb5..4c098d9 100644 --- a/common/ipc/ipc-gsource.cpp +++ b/common/ipc/ipc-gsource.cpp @@ -57,12 +57,10 @@ IPCGSource::IPCGSource(const std::vector fds, IPCGSource::~IPCGSource() { LOGD("Destroying IPCGSource"); - g_source_destroy(&mGSource); - } -IPCGSource* IPCGSource::create(const std::vector& fds, - const HandlerCallback& handlerCallback) +IPCGSource::Pointer IPCGSource::create(const std::vector& fds, + const HandlerCallback& handlerCallback) { LOGD("Creating IPCGSource"); @@ -80,9 +78,19 @@ IPCGSource* IPCGSource::create(const std::vector& fds, // Fill additional data IPCGSource* source = reinterpret_cast(gSource); - return new(source) IPCGSource(fds, handlerCallback); -} + new(source)IPCGSource(fds, handlerCallback); + + auto deleter = [](IPCGSource * ptr) { + LOGD("Deleter"); + + if (!g_source_is_destroyed(&(ptr->mGSource))) { + // This way finalize method will be run in glib loop's thread + g_source_destroy(&(ptr->mGSource)); + } + }; + return std::shared_ptr(source, deleter); +} void IPCGSource::addFD(const FileDescriptor fd) { @@ -119,7 +127,9 @@ void IPCGSource::removeFD(const FileDescriptor fd) guint IPCGSource::attach(GMainContext* context) { LOGD("Attaching to GMainContext"); - return g_source_attach(&mGSource, context); + guint ret = g_source_attach(&mGSource, context); + g_source_unref(&mGSource); + return ret; } gboolean IPCGSource::prepare(GSource* gSource, gint* timeout) @@ -148,6 +158,11 @@ gboolean IPCGSource::dispatch(GSource* gSource, GSourceFunc /*callback*/, gpointer /*userData*/) { + if (!gSource || g_source_is_destroyed(gSource)) { + // Remove the GSource from the GMainContext + return FALSE; + } + IPCGSource* source = reinterpret_cast(gSource); for (const FDInfo fdInfo : source->mFDInfos) { @@ -157,7 +172,7 @@ gboolean IPCGSource::dispatch(GSource* gSource, } } - return TRUE; // Don't remove the GSource from the GMainContext + return TRUE; } void IPCGSource::finalize(GSource* gSource) diff --git a/common/ipc/ipc-gsource.hpp b/common/ipc/ipc-gsource.hpp index 96e0a1a..bb9a096 100644 --- a/common/ipc/ipc-gsource.hpp +++ b/common/ipc/ipc-gsource.hpp @@ -30,7 +30,7 @@ #include "ipc/service.hpp" #include "ipc/types.hpp" -#include "utils/callback-wrapper.hpp" + #include @@ -43,10 +43,17 @@ namespace ipc { * * It's supposed to be constructed ONLY with the static create method * and destructed in a glib callback. + * + * TODO: + * - waiting till the managed object (Client or Service) is destroyed + * before IPCGSource stops operating. For now programmer has to ensure this. */ struct IPCGSource { public: typedef std::function HandlerCallback; + typedef std::shared_ptr Pointer; + + ~IPCGSource(); IPCGSource() = delete; IPCGSource(const IPCGSource&) = delete; @@ -83,8 +90,8 @@ public: * * @return pointer to the IPCGSource */ - static IPCGSource* create(const std::vector& fds, - const HandlerCallback& handlerCallback); + static Pointer create(const std::vector& fds, + const HandlerCallback& handlerCallback); private: @@ -116,9 +123,6 @@ private: IPCGSource(const std::vector fds, const HandlerCallback& handlerCallback); - // Called only from IPCGSource::finalize - ~IPCGSource(); - struct FDInfo { FDInfo(gpointer tag, FileDescriptor fd) : tag(tag), fd(fd) {} diff --git a/common/ipc/service.cpp b/common/ipc/service.cpp index be95cee..5e720d6 100644 --- a/common/ipc/service.cpp +++ b/common/ipc/service.cpp @@ -91,15 +91,15 @@ std::vector Service::getFDs() void Service::handle(const FileDescriptor fd, const short pollEvent) { - if (fd == mProcessor.getEventFD() && pollEvent & POLLIN) { + if (fd == mProcessor.getEventFD() && (pollEvent & POLLIN)) { mProcessor.handleEvent(); return; - } else if (fd == mAcceptor.getConnectionFD() && pollEvent & POLLIN) { + } else if (fd == mAcceptor.getConnectionFD() && (pollEvent & POLLIN)) { mAcceptor.handleConnection(); return; - } else if (fd == mAcceptor.getEventFD() && pollEvent & POLLIN) { + } else if (fd == mAcceptor.getEventFD() && (pollEvent & POLLIN)) { mAcceptor.handleEvent(); return; diff --git a/common/utils/latch.hpp b/common/utils/latch.hpp index 1fa773b..7ef1dd7 100644 --- a/common/utils/latch.hpp +++ b/common/utils/latch.hpp @@ -54,7 +54,7 @@ public: void wait(); /** - * Waits for a single occurence of event with timeout. + * Waits for a single occurrence of event with timeout. * * @param timeoutMs timeout in ms to wait for * @return false on timeout @@ -64,14 +64,14 @@ public: /** * Waits for @ref n occurrences of event. * - * @param n number of occurences to wait for + * @param n number of occurrences to wait for */ void waitForN(const unsigned int n); /** - * Waits for @ref n occurences of event with timeout. + * Waits for @ref n occurrences of event with timeout. * - * @param n number of occurences to wait for + * @param n number of occurrences to wait for * @param timeoutMs timeout in ms to wait for * @return false on timeout */ diff --git a/common/utils/signal.cpp b/common/utils/signal.cpp new file mode 100644 index 0000000..39e7fca --- /dev/null +++ b/common/utils/signal.cpp @@ -0,0 +1,63 @@ +/* + * 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 Signal related functions + */ + +#include "utils/signal.hpp" +#include "utils/exception.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include + +namespace vasum { +namespace utils { + +void signalBlock(const int signalToBlock) +{ + ::sigset_t set; + if (-1 == ::sigemptyset(&set)) { + LOGE("Error in sigemptyset: " << std::string(strerror(errno))); + UtilsException("Error in sigemptyset: " + std::string(strerror(errno))); + } + + if (-1 ==::sigaddset(&set, signalToBlock)) { + LOGE("Error in sigaddset: " << std::string(strerror(errno))); + UtilsException("Error in sigaddset: " + std::string(strerror(errno))); + } + + int ret = ::pthread_sigmask(SIG_BLOCK, &set, nullptr /*&oldSet*/); + if (ret != 0) { + LOGE("Error in pthread_sigmask: " << std::to_string(ret)); + UtilsException("Error in pthread_sigmask: " + std::to_string(ret)); + } +} + +} // namespace utils +} // namespace vasum + + + + + diff --git a/common/utils/signal.hpp b/common/utils/signal.hpp new file mode 100644 index 0000000..f26e365 --- /dev/null +++ b/common/utils/signal.hpp @@ -0,0 +1,37 @@ +/* + * 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 Signal related functions + */ + +#ifndef COMMON_UTILS_SIGNAL_HPP +#define COMMON_UTILS_SIGNAL_HPP + +namespace vasum { +namespace utils { + +void signalBlock(const int signalsToBlock); + +} // namespace utils +} // namespace vasum + + +#endif // COMMON_UTILS_SIGNAL_HPP diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index adfef3e..79ced75 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -36,8 +36,13 @@ PKG_CHECK_MODULES(SERVER_DEPS REQUIRED lxc json gio-2.0 libsystemd-journal libsy INCLUDE_DIRECTORIES(${COMMON_FOLDER}) INCLUDE_DIRECTORIES(${CLIENT_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) -TARGET_LINK_LIBRARIES(${SERVER_CODENAME} ${SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES}) +SET_TARGET_PROPERTIES(${SERVER_CODENAME} PROPERTIES + COMPILE_FLAGS "-pthread" + LINK_FLAGS "-pthread" +) + +TARGET_LINK_LIBRARIES(${SERVER_CODENAME} ${SERVER_DEPS_LIBRARIES} ${Boost_LIBRARIES}) ## Subdirectories ############################################################## ADD_SUBDIRECTORY(configs) diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index c0cc2a8..8b66b09 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -40,6 +40,12 @@ PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED lxc json gio-2.0 libsystemd-daemon libsystemd-journal libcap-ng libLogger libSimpleDbus libConfig) INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${SERVER_FOLDER} ${UNIT_TESTS_FOLDER} ${CLIENT_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${UT_SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + +SET_TARGET_PROPERTIES(${UT_SERVER_CODENAME} PROPERTIES + COMPILE_FLAGS "-pthread" + LINK_FLAGS "-pthread" +) + 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 index 9ce131d..a7f8645 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include using namespace vasum; @@ -136,7 +137,7 @@ std::shared_ptr longEchoCallback(const FileDescriptor, std::shared_ptr return data; } -FileDescriptor connect(Service& s, Client& c, bool serviceUsesGlib = false) +FileDescriptor connect(Service& s, Client& c) { // Connects the Client to the Service and returns Clients FileDescriptor std::mutex mutex; @@ -149,41 +150,102 @@ FileDescriptor connect(Service& s, Client& c, bool serviceUsesGlib = false) cv.notify_all(); }; + // TODO: On timeout remove the callback + s.setNewPeerCallback(newPeerCallback); + + if (!s.isStarted()) { + s.start(); + } + + c.start(); + + + std::unique_lock lock(mutex); + BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { + return peerFD != 0; + })); + + return peerFD; +} + - if (!serviceUsesGlib) { - s.setNewPeerCallback(newPeerCallback); - if (!s.isStarted()) { - s.start(); - } - } else { #if GLIB_CHECK_VERSION(2,36,0) - IPCGSource* serviceGSourcePtr = IPCGSource::create(s.getFDs(), std::bind(&Service::handle, &s, _1, _2)); +std::pair connectServiceGSource(Service& s, Client& c) +{ + std::mutex mutex; + std::condition_variable cv; - auto agregateCallback = [&newPeerCallback, &serviceGSourcePtr](const FileDescriptor newFD) { - serviceGSourcePtr->addFD(newFD); - newPeerCallback(newFD); - }; + FileDescriptor peerFD = 0; + IPCGSource::Pointer ipcGSourcePtr = IPCGSource::create(s.getFDs(), std::bind(&Service::handle, &s, _1, _2)); - s.setNewPeerCallback(agregateCallback); - s.setRemovedPeerCallback(std::bind(&IPCGSource::removeFD, serviceGSourcePtr, _1)); + auto newPeerCallback = [&cv, &peerFD, &mutex, ipcGSourcePtr](const FileDescriptor newFD) { + if (ipcGSourcePtr) { + //TODO: Remove this if + ipcGSourcePtr->addFD(newFD); + } + std::unique_lock lock(mutex); + peerFD = newFD; + cv.notify_all(); + }; - serviceGSourcePtr->attach(); -#endif // GLIB_CHECK_VERSION - } + // TODO: On timeout remove the callback + s.setNewPeerCallback(newPeerCallback); + s.setRemovedPeerCallback(std::bind(&IPCGSource::removeFD, ipcGSourcePtr, _1)); + + // Service starts to process + ipcGSourcePtr->attach(); c.start(); std::unique_lock lock(mutex); - BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { + BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { return peerFD != 0; })); - return peerFD; + return std::make_pair(peerFD, ipcGSourcePtr); } +std::pair connectClientGSource(Service& s, Client& c) +{ + // Connects the Client to the Service and returns Clients FileDescriptor + std::mutex mutex; + std::condition_variable cv; + + FileDescriptor peerFD = 0; + auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFD) { + std::unique_lock lock(mutex); + peerFD = newFD; + cv.notify_all(); + }; + // TODO: On timeout remove the callback + s.setNewPeerCallback(newPeerCallback); + + if (!s.isStarted()) { + // Service starts to process + s.start(); + } + + + c.connect(); + IPCGSource::Pointer ipcGSourcePtr = IPCGSource::create(c.getFDs(), + std::bind(&Client::handle, &c, _1, _2)); + + ipcGSourcePtr->attach(); + + std::unique_lock lock(mutex); + BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { + return peerFD != 0; + })); + + return std::make_pair(peerFD, ipcGSourcePtr); +} + +#endif // GLIB_CHECK_VERSION + + void testEcho(Client& c, const MethodID methodID) { std::shared_ptr sentData(new SendData(34)); @@ -194,7 +256,7 @@ void testEcho(Client& c, const MethodID methodID) void testEcho(Service& s, const MethodID methodID, const FileDescriptor peerFD) { std::shared_ptr sentData(new SendData(56)); - std::shared_ptr recvData = s.callSync(methodID, peerFD, sentData); + std::shared_ptr recvData = s.callSync(methodID, peerFD, sentData, 1000); BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } @@ -481,17 +543,16 @@ BOOST_AUTO_TEST_CASE(ReadTimeout) { Service s(socketPath); auto longEchoCallback = [](const FileDescriptor, std::shared_ptr& data) { - return std::shared_ptr(new LongSendData(data->intVal)); + return std::shared_ptr(new LongSendData(data->intVal, 4000 /*ms*/)); }; s.addMethodHandler(1, longEchoCallback); - s.start(); Client c(socketPath); - c.start(); + connect(s, c); // Test timeout on read std::shared_ptr sentData(new SendData(334)); - BOOST_CHECK_THROW((c.callSync(1, sentData, 100)), IPCException); + BOOST_CHECK_THROW((c.callSync(1, sentData, 10)), IPCException); } @@ -587,12 +648,15 @@ BOOST_AUTO_TEST_CASE(ServiceGSource) isSignalCalled = true; }; + IPCGSource::Pointer serviceGSource; Service s(socketPath); s.addMethodHandler(1, echoCallback); Client c(socketPath); s.addSignalHandler(2, signalHandler); - connect(s, c, true); + + auto ret = connectServiceGSource(s, c); + serviceGSource = ret.second; testEcho(c, 1); @@ -603,6 +667,36 @@ BOOST_AUTO_TEST_CASE(ServiceGSource) BOOST_CHECK(isSignalCalled); } +BOOST_AUTO_TEST_CASE(ClientGSource) +{ + ScopedGlibLoop loop; + + std::atomic_bool isSignalCalled(false); + auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr&) { + isSignalCalled = true; + }; + + Service s(socketPath); + s.start(); + + IPCGSource::Pointer clientGSource; + Client c(socketPath); + c.addMethodHandler(1, echoCallback); + c.addSignalHandler(2, signalHandler); + + auto ret = connectClientGSource(s, c); + FileDescriptor peerFD = ret.first; + clientGSource = ret.second; + + testEcho(s, 1, peerFD); + + auto data = std::make_shared(1); + s.signal(2, data); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for + BOOST_CHECK(isSignalCalled); +} + #endif // GLIB_CHECK_VERSION // BOOST_AUTO_TEST_CASE(ConnectionLimitTest) diff --git a/zone-daemon/CMakeLists.txt b/zone-daemon/CMakeLists.txt index 50bfa1a..7baf37f 100644 --- a/zone-daemon/CMakeLists.txt +++ b/zone-daemon/CMakeLists.txt @@ -37,6 +37,12 @@ PKG_CHECK_MODULES(ZONE_DAEMON_DEPS REQUIRED gio-2.0 libsystemd-journal libcap-ng libLogger libSimpleDbus libConfig) INCLUDE_DIRECTORIES(${COMMON_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${ZONE_DAEMON_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + +SET_TARGET_PROPERTIES(${ZONE_DAEMON_CODENAME} PROPERTIES + COMPILE_FLAGS "-pthread" + LINK_FLAGS "-pthread" +) + TARGET_LINK_LIBRARIES(${ZONE_DAEMON_CODENAME} ${ZONE_DAEMON_DEPS_LIBRARIES} ${Boost_LIBRARIES}) -- 2.7.4 From 3bf3bb34a99cb34c102467f070c1b0a6064f5515 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 7 Jan 2015 16:02:54 +0100 Subject: [PATCH 08/16] IPC: Fixed DisconnectedPeerError test [Bug/Feature] N/A [Cause] SERIALIZATION_ERROR may also be returned when peer disconnects [Solution] N/A [Verification] Build, install, run tests Change-Id: Ifaf67828796cfa0def8d5f56dc926314abb2d36b --- tests/unit_tests/ipc/ut-ipc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index a7f8645..18c4785 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -532,10 +532,14 @@ BOOST_AUTO_TEST_CASE(DisconnectedPeerError) // Wait for the response std::unique_lock lock(mutex); - BOOST_CHECK(cv.wait_for(lock, std::chrono::seconds(10), [&retStatus]() { + BOOST_CHECK(cv.wait_for(lock, std::chrono::seconds(100), [&retStatus]() { return retStatus != ipc::Status::UNDEFINED; })); - BOOST_CHECK(retStatus == ipc::Status::PEER_DISCONNECTED); //TODO it fails from time to time + + // The disconnection might have happened: + // - after sending the message (PEER_DISCONNECTED) + // - during external serialization (SERIALIZATION_ERROR) + BOOST_CHECK(retStatus == ipc::Status::PEER_DISCONNECTED || retStatus == ipc::Status::SERIALIZATION_ERROR); } -- 2.7.4 From 1da8243f43d8d0f7f6adc052b6f47fb6a34e1091 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 30 Dec 2014 14:46:59 +0100 Subject: [PATCH 09/16] Update tizen common (with wayland) lxc template [Bug/Feature] Adjust template to new platform image [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ic32677fa85a73249af5f1d84b63893b26d1eadd4 Signed-off-by: Dariusz Michaluk --- .../configs/lxc-templates/tizen-common-wayland.sh | 139 ++++----------------- 1 file changed, 24 insertions(+), 115 deletions(-) diff --git a/server/configs/lxc-templates/tizen-common-wayland.sh b/server/configs/lxc-templates/tizen-common-wayland.sh index 12fb223..9424dc4 100755 --- a/server/configs/lxc-templates/tizen-common-wayland.sh +++ b/server/configs/lxc-templates/tizen-common-wayland.sh @@ -86,6 +86,7 @@ ${rootfs}/home \ ${rootfs}/home/alice \ ${rootfs}/home/bob \ ${rootfs}/home/carol \ +${rootfs}/home/developer \ ${rootfs}/home/guest \ ${rootfs}/lib \ ${rootfs}/media \ @@ -104,12 +105,14 @@ ${path}/hooks \ ${path}/scripts \ ${path}/systemd \ ${path}/systemd/system \ +${path}/systemd/system/multi-user.target.wants \ ${path}/systemd/user " /bin/mkdir ${ROOTFS_DIRS} /bin/chown alice:users ${rootfs}/home/alice /bin/chown bob:users ${rootfs}/home/bob /bin/chown carol:users ${rootfs}/home/carol +/bin/chown developer:users ${rootfs}/home/developer /bin/chown guest:users ${rootfs}/home/guest /bin/ln -s /dev/null ${path}/systemd/system/bluetooth.service @@ -117,8 +120,11 @@ ${path}/systemd/user /bin/ln -s /dev/null ${path}/systemd/system/sshd.socket /bin/ln -s /dev/null ${path}/systemd/system/sshd@.service /bin/ln -s /dev/null ${path}/systemd/system/systemd-udevd.service -/bin/ln -s /dev/null ${path}/systemd/system/user-session-launch@seat0-5100.service +/bin/ln -s /dev/null ${path}/systemd/system/systemd-udevd-kernel.socket +/bin/ln -s /dev/null ${path}/systemd/system/systemd-udevd-control.socket /bin/ln -s /dev/null ${path}/systemd/system/vconf-setup.service +/bin/ln -s /usr/lib/systemd/system/tlm.service ${path}/systemd/system/multi-user.target.wants/tlm.service +/bin/ln -s /dev/null ${path}/systemd/user/media-server-user.service cat <>${path}/systemd/system/display-manager-run.service # Run weston with framebuffer backend on selected virtual terminal. @@ -133,7 +139,7 @@ WorkingDirectory=/run/%u ExecStart=/usr/bin/weston --backend=fbdev-backend.so -i0 --log=/tmp/weston.log --tty=${vt} #StandardInput=tty #TTYPath=/dev/tty7 -EnvironmentFile=/etc/systemd/system/weston +EnvironmentFile=/etc/sysconfig/weston Restart=on-failure RestartSec=10 @@ -144,119 +150,18 @@ CapabilityBoundingSet=CAP_SYS_TTY_CONFIG [Install] WantedBy=graphical.target EOF - -cat <>${path}/systemd/system/display-manager.path -# Wayland socket path is changed to /tmp directory. -[Unit] -Description=Wait for wayland socket -Requires=display-manager-run.service -After=display-manager-run.service - -[Path] -PathExists=/tmp/wayland-0 -EOF - -cat <>${path}/systemd/system/display-manager.service -# Wayland socket path is changed to /tmp directory. -[Unit] -Description=Display manager setup service -Requires=display-manager-run.service -After=display-manager-run.service - -[Service] -Type=oneshot -ExecStart=/usr/bin/chmod g+w /tmp/wayland-0 -#ExecStart=/usr/bin/chsmack -a User /tmp/wayland-0 - -[Install] -WantedBy=graphical.target -EOF - -cat <>${path}/systemd/system/weston -# path to display manager runtime dir -XDG_RUNTIME_DIR=/tmp -XDG_CONFIG_HOME=/etc/systemd/system -EOF - -cat <>${path}/systemd/system/weston.ini -# Weston config for zone. -[core] -modules=desktop-shell.so - -[shell] -background-image=/usr/share/backgrounds/tizen/golfe-morbihan.jpg -background-color=0xff002244 -background-type=scale-crop -panel-color=0x95333333 -locking=true -panel=false -animation=zoom -#binding-modifier=ctrl -num-workspaces=4 -#cursor-theme=whiteglass -#cursor-size=24 -startup-animation=fade - -#lockscreen-icon=/usr/share/icons/gnome/256x256/actions/lock.png -#lockscreen=/usr/share/backgrounds/gnome/Garden.jpg -#homescreen=/usr/share/backgrounds/gnome/Blinds.jpg - -## weston - -[launcher] -icon=/usr/share/icons/tizen/32x32/terminal.png -path=/usr/bin/weston-terminal - -[screensaver] -# Uncomment path to disable screensaver -duration=600 - -[input-method] -path=/usr/libexec/weston-keyboard -#path=/bin/weekeyboard - -#[keyboard] -#keymap_layout=fr - -#[output] -#name=LVDS1 -#mode=1680x1050 -#transform=90 -#icc_profile=/usr/share/color/icc/colord/Bluish.icc - -#[output] -#name=VGA1 -#mode=173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync -#transform=flipped - -#[output] -#name=X1 -#mode=1024x768 -#transform=flipped-270 - -#[touchpad] -#constant_accel_factor = 50 -#min_accel_factor = 0.16 -#max_accel_factor = 1.0 - -[output] -name=DP1 -default_output=1 -EOF - -cat <>${path}/systemd/user/weston-user.service -# Wayland socket path is changed to /tmp directory. -[Unit] -Description=Shared weston session - -[Service] -ExecStartPre=/usr/bin/ln -sf /tmp/wayland-0 /run/user/%U/ -ExecStart=/bin/sh -l -c "/usr/bin/tz-launcher -c /usr/share/applications/tizen/launcher.conf %h/.applications/desktop" -EnvironmentFile=/etc/sysconfig/weston-user - -[Install] -WantedBy=default.target -EOF +chmod 644 ${path}/systemd/system/display-manager-run.service + +sed -e 's/run\/display/tmp/g' /usr/lib/systemd/system/display-manager.path >> ${path}/systemd/system/display-manager.path +chmod 644 ${path}/systemd/system/display-manager.path +sed -e 's/run\/display/tmp/g' /usr/lib/systemd/system/display-manager.service >> ${path}/systemd/system/display-manager.service +chmod 644 ${path}/systemd/system/display-manager.service +sed -e 's/run\/display/tmp/g' /etc/sysconfig/weston >> ${path}/weston +sed -e 's/backgrounds\/tizen\/current/backgrounds\/tizen\/golfe-morbihan.jpg/g' /etc/xdg/weston/weston.ini >> ${path}/weston.ini +sed -e 's/run\/display/tmp/g' /usr/lib/systemd/user/weston-user.service >> ${path}/systemd/user/weston-user.service +sed -e 's/run\/display/tmp/g' /etc/tlm.conf >> ${path}/tlm.conf +sed -e 's/run\/display/tmp/g' /etc/session.d/user-session >> ${path}/user-session +chmod 755 ${path}/user-session # Prepare host configuration cat <>/etc/udev/rules.d/99-tty.rules @@ -377,8 +282,12 @@ chmod 770 ${path}/hooks/pre-start.sh cat <>${path}/fstab /bin bin none ro,bind 0 0 /etc etc none ro,bind 0 0 +${path}/tlm.conf etc/tlm.conf none ro,bind 0 0 +${path}/user-session etc/session.d/user-session none rw,bind 0 0 ${path}/systemd/system etc/systemd/system none ro,bind 0 0 ${path}/systemd/user etc/systemd/user none ro,bind 0 0 +${path}/weston etc/sysconfig/weston none ro,bind 0 0 +${path}/weston.ini etc/xdg/weston/weston.ini none ro,bind 0 0 /lib lib none ro,bind 0 0 /media media none ro,bind 0 0 /mnt mnt none ro,bind 0 0 -- 2.7.4 From 952b04e1a069e0c0e55d393ec2460a3fc24766ff Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Thu, 8 Jan 2015 11:12:25 +0100 Subject: [PATCH 10/16] Add correct button configuration [Bug/Feature] Add correct button configuration [Cause] N/A [Solution] N/A [Verification] Build, install, run tests, launch vasum Change-Id: I3561823663c3dd9a058249a0467fa704105f19a1 Signed-off-by: Dariusz Michaluk --- packaging/vasum.spec | 1 - server/configs/daemon.conf.in | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packaging/vasum.spec b/packaging/vasum.spec index 887409e..e743a75 100644 --- a/packaging/vasum.spec +++ b/packaging/vasum.spec @@ -158,7 +158,6 @@ Development package including the header files for the client library %package zone-support Summary: Vasum Support Group: Security/Other -Conflicts: vasum %description zone-support Zones support installed inside every zone. diff --git a/server/configs/daemon.conf.in b/server/configs/daemon.conf.in index 648d986..4522bb5 100644 --- a/server/configs/daemon.conf.in +++ b/server/configs/daemon.conf.in @@ -8,10 +8,10 @@ "foregroundId" : "private", "defaultId" : "private", "lxcTemplatePrefix" : "/etc/vasum/lxc-templates", - "inputConfig" : {"enabled" : false, + "inputConfig" : {"enabled" : true, "device" : "gpio_keys.6", - "code" : 139, - "numberOfEvents" : 1, + "code" : 116, + "numberOfEvents" : 2, "timeWindowMs" : 500}, "proxyCallRules" : [] } -- 2.7.4 From 94b00b1c12569f6efeb68b4e086198f2671bff95 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Fri, 9 Jan 2015 10:11:28 +0100 Subject: [PATCH 11/16] IPC: Cleaned up timeouts in tests [Bug/Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build, install, run tests, run tests under valgrind Change-Id: Ifee2dffed77a5686fdb2165b2a530f39aef0857a --- common/ipc/internals/processor.cpp | 1 + common/ipc/internals/processor.hpp | 12 ++++++- tests/unit_tests/ipc/ut-ipc.cpp | 67 ++++++++++++++++++++------------------ 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/common/ipc/internals/processor.cpp b/common/ipc/internals/processor.cpp index 22aa38e..6084718 100644 --- a/common/ipc/internals/processor.cpp +++ b/common/ipc/internals/processor.cpp @@ -588,6 +588,7 @@ bool Processor::onNewPeer() Lock lock(mCallbacksMutex); if (mNewPeerCallback) { // Notify about the new user. + LOGT("Calling NewPeerCallback"); mNewPeerCallback(socketInfo.peerFD); } } diff --git a/common/ipc/internals/processor.hpp b/common/ipc/internals/processor.hpp index 728b8d2..0e8fe8f 100644 --- a/common/ipc/internals/processor.hpp +++ b/common/ipc/internals/processor.hpp @@ -602,11 +602,21 @@ std::shared_ptr Processor::callSync(const MethodID methodID, }; std::unique_lock lock(mutex); + LOGT("Waiting for the response..."); + // TODO: There is a race here. mReturnCallbacks were used to indicate if the return call was served or not, + // but if the timeout occurs before the call is even sent, then this method is broken. if (!cv.wait_for(lock, std::chrono::milliseconds(timeoutMS), isResultInitialized)) { + // Timeout occurred: + // - call isn't sent => delete it + // - call is sent and no reply => throw IPCTimeoutError + // - call is being serviced => wait for it with the same timeout + LOGT("Probably a timeout in callSync. Checking..."); + bool isTimeout = false; { Lock lock(mReturnCallbacksMutex); if (1 == mReturnCallbacks.erase(messageID)) { + // Return callback was present, so there was a timeout isTimeout = true; } } @@ -615,7 +625,7 @@ std::shared_ptr Processor::callSync(const MethodID methodID, LOGE("Function call timeout; methodID: " << methodID); throw IPCTimeoutException("Function call timeout; methodID: " + std::to_string(methodID)); } else { - // Timeout started during the return value processing, so wait for it to finish + //Timeout started during the return value processing, so wait for it to finish cv.wait(lock, isResultInitialized); } } diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index 18c4785..65ee27e 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -55,6 +55,16 @@ using namespace std::placeholders; namespace fs = boost::filesystem; namespace { + +// Timeout for sending one message +const int TIMEOUT = 1000 /*ms*/; + +// Time that won't cause "TIMEOUT" methods to throw +const int SHORT_OPERATION_TIME = TIMEOUT / 100; + +// Time that will cause "TIMEOUT" methods to throw +const int LONG_OPERATION_TIME = 3 * TIMEOUT; + struct Fixture { std::string socketPath; @@ -133,7 +143,7 @@ std::shared_ptr echoCallback(const FileDescriptor, std::shared_ptr longEchoCallback(const FileDescriptor, std::shared_ptr& data) { - std::this_thread::sleep_for(std::chrono::seconds(1)); + std::this_thread::sleep_for(std::chrono::milliseconds(LONG_OPERATION_TIME)); return data; } @@ -161,7 +171,7 @@ FileDescriptor connect(Service& s, Client& c) std::unique_lock lock(mutex); - BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { + BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(3 * TIMEOUT), [&peerFD]() { return peerFD != 0; })); @@ -201,7 +211,7 @@ std::pair connectServiceGSource(Service& s, c.start(); std::unique_lock lock(mutex); - BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { + BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(3 * TIMEOUT), [&peerFD]() { return peerFD != 0; })); @@ -236,7 +246,7 @@ std::pair connectClientGSource(Service& s, ipcGSourcePtr->attach(); std::unique_lock lock(mutex); - BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(2000), [&peerFD]() { + BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(3 * TIMEOUT), [&peerFD]() { return peerFD != 0; })); @@ -249,14 +259,14 @@ std::pair connectClientGSource(Service& s, void testEcho(Client& c, const MethodID methodID) { std::shared_ptr sentData(new SendData(34)); - std::shared_ptr recvData = c.callSync(methodID, sentData, 1000); + std::shared_ptr recvData = c.callSync(methodID, sentData, TIMEOUT); BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } void testEcho(Service& s, const MethodID methodID, const FileDescriptor peerFD) { std::shared_ptr sentData(new SendData(56)); - std::shared_ptr recvData = s.callSync(methodID, peerFD, sentData, 1000); + std::shared_ptr recvData = s.callSync(methodID, peerFD, sentData, TIMEOUT); BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); } @@ -274,7 +284,6 @@ BOOST_AUTO_TEST_CASE(ConstructorDestructor) BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod) { Service s(socketPath); - s.addMethodHandler(1, returnEmptyCallback); s.addMethodHandler(1, returnDataCallback); @@ -284,7 +293,7 @@ BOOST_AUTO_TEST_CASE(ServiceAddRemoveMethod) s.addMethodHandler(2, returnDataCallback); Client c(socketPath); - c.start(); + connect(s, c); testEcho(c, 1); s.removeMethod(1); @@ -352,9 +361,9 @@ BOOST_AUTO_TEST_CASE(SyncClientToServiceEcho) s.addMethodHandler(1, echoCallback); s.addMethodHandler(2, echoCallback); - s.start(); Client c(socketPath); - c.start(); + connect(s, c); + testEcho(c, 1); testEcho(c, 2); } @@ -421,7 +430,7 @@ BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho) // Wait for the response std::unique_lock lock(mutex); - BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(100), [&recvData]() { + BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(TIMEOUT), [&recvData]() { return static_cast(recvData); })); @@ -452,7 +461,7 @@ BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho) // Wait for the response std::unique_lock lock(mutex); - BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(1000), [&recvData]() { + BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(TIMEOUT), [&recvData]() { return recvData.get() != nullptr; })); @@ -465,23 +474,20 @@ BOOST_AUTO_TEST_CASE(SyncTimeout) Service s(socketPath); s.addMethodHandler(1, longEchoCallback); - s.start(); Client c(socketPath); - c.start(); + connect(s, c); std::shared_ptr sentData(new SendData(78)); - - BOOST_CHECK_THROW((c.callSync(1, sentData, 10)), IPCException); //TODO it fails from time to time + BOOST_REQUIRE_THROW((c.callSync(1, sentData, TIMEOUT)), IPCException); } BOOST_AUTO_TEST_CASE(SerializationError) { Service s(socketPath); s.addMethodHandler(1, echoCallback); - s.start(); Client c(socketPath); - c.start(); + connect(s, c); std::shared_ptr throwingData(new ThrowOnAcceptData()); @@ -532,7 +538,7 @@ BOOST_AUTO_TEST_CASE(DisconnectedPeerError) // Wait for the response std::unique_lock lock(mutex); - BOOST_CHECK(cv.wait_for(lock, std::chrono::seconds(100), [&retStatus]() { + BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(TIMEOUT), [&retStatus]() { return retStatus != ipc::Status::UNDEFINED; })); @@ -547,7 +553,7 @@ BOOST_AUTO_TEST_CASE(ReadTimeout) { Service s(socketPath); auto longEchoCallback = [](const FileDescriptor, std::shared_ptr& data) { - return std::shared_ptr(new LongSendData(data->intVal, 4000 /*ms*/)); + return std::shared_ptr(new LongSendData(data->intVal, LONG_OPERATION_TIME)); }; s.addMethodHandler(1, longEchoCallback); @@ -556,7 +562,7 @@ BOOST_AUTO_TEST_CASE(ReadTimeout) // Test timeout on read std::shared_ptr sentData(new SendData(334)); - BOOST_CHECK_THROW((c.callSync(1, sentData, 10)), IPCException); + BOOST_CHECK_THROW((c.callSync(1, sentData, TIMEOUT)), IPCException); } @@ -570,13 +576,13 @@ BOOST_AUTO_TEST_CASE(WriteTimeout) c.start(); // Test echo with a minimal timeout - std::shared_ptr sentDataA(new LongSendData(34, 10 /*ms*/)); - std::shared_ptr recvData = c.callSync(1, sentDataA, 100); + std::shared_ptr sentDataA(new LongSendData(34, SHORT_OPERATION_TIME)); + std::shared_ptr recvData = c.callSync(1, sentDataA, TIMEOUT); BOOST_CHECK_EQUAL(recvData->intVal, sentDataA->intVal); // Test timeout on write - std::shared_ptr sentDataB(new LongSendData(34, 1000 /*ms*/)); - BOOST_CHECK_THROW((c.callSync(1, sentDataB, 100)), IPCTimeoutException); + std::shared_ptr sentDataB(new LongSendData(34, LONG_OPERATION_TIME)); + BOOST_CHECK_THROW((c.callSync(1, sentDataB, TIMEOUT)), IPCTimeoutException); } @@ -604,7 +610,7 @@ BOOST_AUTO_TEST_CASE(AddSignalInRuntime) s.signal(1, data); // Wait for the signals to arrive - std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for + std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //TODO wait_for BOOST_CHECK(isHandlerACalled && isHandlerBCalled); } @@ -630,13 +636,13 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline) connect(s, c); // Wait for the information about the signals to propagate - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); auto data = std::make_shared(1); s.signal(2, data); s.signal(1, data); // Wait for the signals to arrive - std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for + std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT)); BOOST_CHECK(isHandlerACalled && isHandlerBCalled); } @@ -646,7 +652,6 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline) BOOST_AUTO_TEST_CASE(ServiceGSource) { ScopedGlibLoop loop; - std::atomic_bool isSignalCalled(false); auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr&) { isSignalCalled = true; @@ -667,7 +672,7 @@ BOOST_AUTO_TEST_CASE(ServiceGSource) auto data = std::make_shared(1); c.signal(2, data); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for + std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //TODO wait_for BOOST_CHECK(isSignalCalled); } @@ -697,7 +702,7 @@ BOOST_AUTO_TEST_CASE(ClientGSource) auto data = std::make_shared(1); s.signal(2, data); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); //TODO wait_for + std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //TODO wait_for BOOST_CHECK(isSignalCalled); } -- 2.7.4 From 3035b39d565506bc70dce035cd4178e883b7f976 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Fri, 9 Jan 2015 12:34:12 +0100 Subject: [PATCH 12/16] Fixed generation of log_sink file [Bug]i log_sink file is invalid formated xml [Cause] Lxc invoke exit(0) after fork. Exit from child and from parent cause redundant log write [Solution] Add defintion to CMakeLists.txt [Verification] Build, install, build run test with '--log_sink=/tmp/log.xml', run 'xmllint --huge --format /tmp/log.xml' (there should be no parse errors). Note: StartHasStoppedTest can fail. Change-Id: I9a1d4bee9030eb8e3958ff914ef7cf2dc1a953ce --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a12367f..06a1064 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ ADD_DEFINITIONS("-pedantic") # Be pedantic ADD_DEFINITIONS("-pedantic-errors") # Make pedantic warnings into errors ADD_DEFINITIONS(-DPROGRAM_VERSION="${VERSION}") ADD_DEFINITIONS(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") +ADD_DEFINITIONS(-DUSE_EXEC) IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # Warn about documentation problems -- 2.7.4 From a83342a7862458d2a6a35fe8889e80cd9a9dc1bb Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Wed, 7 Jan 2015 15:39:02 +0100 Subject: [PATCH 13/16] IPC: Convert test synchronization method to Latch [Bug/Feature] Some tests were using sleep_for in order to synchronize threads [Cause] N/A [Solution] Use Latch instead of atomic_bool set and sleep_for to synchronize threads in tests [Verification] Build, install, run tests. Should pass as they did before. Change-Id: I3067ed1f13cdde047f720c0b6d05ce19ec156dbe --- tests/unit_tests/ipc/ut-ipc.cpp | 50 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index 65ee27e..74c18f3 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -36,12 +36,12 @@ #include "ipc/ipc-gsource.hpp" #include "ipc/types.hpp" #include "utils/glib-loop.hpp" +#include "utils/latch.hpp" #include "config/fields.hpp" #include "logger/logger.hpp" #include -#include #include #include #include @@ -592,14 +592,14 @@ BOOST_AUTO_TEST_CASE(AddSignalInRuntime) Client c(socketPath); connect(s, c); - std::atomic_bool isHandlerACalled(false); - auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr&) { - isHandlerACalled = true; + utils::Latch latchA; + auto handlerA = [&latchA](const FileDescriptor, std::shared_ptr&) { + latchA.set(); }; - std::atomic_bool isHandlerBCalled(false); - auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr&) { - isHandlerBCalled = true; + utils::Latch latchB; + auto handlerB = [&latchB](const FileDescriptor, std::shared_ptr&) { + latchB.set(); }; c.addSignalHandler(1, handlerA); @@ -610,8 +610,7 @@ BOOST_AUTO_TEST_CASE(AddSignalInRuntime) s.signal(1, data); // Wait for the signals to arrive - std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //TODO wait_for - BOOST_CHECK(isHandlerACalled && isHandlerBCalled); + BOOST_CHECK(latchA.wait(TIMEOUT) && latchB.wait(TIMEOUT)); } @@ -620,14 +619,14 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline) Service s(socketPath); Client c(socketPath); - std::atomic_bool isHandlerACalled(false); - auto handlerA = [&isHandlerACalled](const FileDescriptor, std::shared_ptr&) { - isHandlerACalled = true; + utils::Latch latchA; + auto handlerA = [&latchA](const FileDescriptor, std::shared_ptr&) { + latchA.set(); }; - std::atomic_bool isHandlerBCalled(false); - auto handlerB = [&isHandlerBCalled](const FileDescriptor, std::shared_ptr&) { - isHandlerBCalled = true; + utils::Latch latchB; + auto handlerB = [&latchB](const FileDescriptor, std::shared_ptr&) { + latchB.set(); }; c.addSignalHandler(1, handlerA); @@ -642,8 +641,7 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline) s.signal(1, data); // Wait for the signals to arrive - std::this_thread::sleep_for(std::chrono::milliseconds(2 * TIMEOUT)); - BOOST_CHECK(isHandlerACalled && isHandlerBCalled); + BOOST_CHECK(latchA.wait(TIMEOUT) && latchB.wait(TIMEOUT)); } @@ -652,9 +650,9 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline) BOOST_AUTO_TEST_CASE(ServiceGSource) { ScopedGlibLoop loop; - std::atomic_bool isSignalCalled(false); - auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr&) { - isSignalCalled = true; + utils::Latch l; + auto signalHandler = [&l](const FileDescriptor, std::shared_ptr&) { + l.set(); }; IPCGSource::Pointer serviceGSource; @@ -672,17 +670,16 @@ BOOST_AUTO_TEST_CASE(ServiceGSource) auto data = std::make_shared(1); c.signal(2, data); - std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //TODO wait_for - BOOST_CHECK(isSignalCalled); + BOOST_CHECK(l.wait(TIMEOUT)); } BOOST_AUTO_TEST_CASE(ClientGSource) { ScopedGlibLoop loop; - std::atomic_bool isSignalCalled(false); - auto signalHandler = [&isSignalCalled](const FileDescriptor, std::shared_ptr&) { - isSignalCalled = true; + utils::Latch l; + auto signalHandler = [&l](const FileDescriptor, std::shared_ptr&) { + l.set(); }; Service s(socketPath); @@ -702,8 +699,7 @@ BOOST_AUTO_TEST_CASE(ClientGSource) auto data = std::make_shared(1); s.signal(2, data); - std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); //TODO wait_for - BOOST_CHECK(isSignalCalled); + BOOST_CHECK(l.wait(TIMEOUT)); } #endif // GLIB_CHECK_VERSION -- 2.7.4 From e310694aca62b3359f7ca1e9027e4f8650a52ade Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Wed, 17 Dec 2014 10:42:41 +0100 Subject: [PATCH 14/16] Add test for LoggerScope [Feature] Test for new LoggerScope interface. [Cause] New interface in libLogger. [Solution] Test for new LoggerScope interface. [Verification] Build, install, run Log tests. Change-Id: I43767c1f65d09ca8304046fd9d8586f1077a8b63 --- tests/unit_tests/log/ut-logger.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/unit_tests/log/ut-logger.cpp b/tests/unit_tests/log/ut-logger.cpp index e574505..74028db 100644 --- a/tests/unit_tests/log/ut-logger.cpp +++ b/tests/unit_tests/log/ut-logger.cpp @@ -26,6 +26,7 @@ #include "config.hpp" #include "ut.hpp" #include "logger/logger.hpp" +#include "logger/logger-scope.hpp" #include "logger/formatter.hpp" #include "logger/backend.hpp" #include "logger/backend-stderr.hpp" @@ -197,6 +198,19 @@ BOOST_AUTO_TEST_CASE(TestLogsTrace) BOOST_CHECK(tf.logContains("[TRACE]") == true); } +BOOST_AUTO_TEST_CASE(TestLoggerScope) +{ + LOGS("Main function scope"); + + { + LOGS("Scope inside function"); + LOGD("Some additional information in-between scoped logs"); + { + LOGS("Additional scope with " << "stringstream" << ' ' << "test" << 3 << ' ' << 3.42); + LOGD("More additional information in-between scoped logs"); + } + } +} BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From da1be5620b803b7d826376faffe7ecd0083b8222 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Mon, 12 Jan 2015 16:16:47 +0100 Subject: [PATCH 15/16] Added testing wrong or unset union type [Bug/Feature] Added testing wrong or unset union type [Cause] N/A [Solution] Throw ConfigException [Verification] Build, install, run ConfigurationSuite tests Change-Id: I813b52870c4c12a58a9a614821056278a5445a68 --- tests/unit_tests/config/ut-configuration.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index 8e82905..95fc52a 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -95,6 +95,9 @@ BOOST_AUTO_TEST_CASE(ToStringTest) std::string out = saveToString(testConfig); BOOST_CHECK_EQUAL(out, jsonTestString); + + TestConfig::SubConfigOption unionConfig; + BOOST_CHECK_THROW(saveToString(unionConfig), ConfigException); } namespace loadErrorsTest { @@ -111,6 +114,13 @@ DECLARE_CONFIG(BoolConfig, bool) DECLARE_CONFIG(ArrayConfig, std::vector) DECLARE_CONFIG(ObjectConfig, IntConfig) #undef DECLARE_CONFIG +struct UnionConfig { + CONFIG_DECLARE_UNION + ( + int, + bool + ) +}; } // namespace loadErrorsTest @@ -177,6 +187,12 @@ BOOST_AUTO_TEST_CASE(LoadErrorsTest) BOOST_CHECK_THROW(loadFromString("{\"field\": []}", objectConfig), ConfigException); BOOST_CHECK_THROW(loadFromString("{\"field\": {}}", objectConfig), ConfigException); BOOST_CHECK_NO_THROW(loadFromString("{\"field\": {\"field\": 1}}", objectConfig)); + + UnionConfig unionConfig; + BOOST_CHECK_THROW(loadFromString("{\"type\": \"long\", \"value\": 1}", unionConfig), ConfigException); + BOOST_CHECK_THROW(loadFromString("{\"type\": \"int\"}", unionConfig), ConfigException); + BOOST_CHECK_NO_THROW(loadFromString("{\"type\": \"int\", \"value\": 1}", unionConfig)); + BOOST_CHECK_NO_THROW(loadFromString("{\"type\": \"bool\", \"value\": true}", unionConfig)); } namespace hasVisitableTest { -- 2.7.4 From 04f7e267d40ba248d20251dcb13223134996095f Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Wed, 7 Jan 2015 14:29:30 +0100 Subject: [PATCH 16/16] Add get_zone_ids, get_active_zone_id and get_zones_status to cli [Feature] Add get_zone_ids, get_active_zone_id and get_zones_status to cli [Cause] N/A [Solution] N/A [Verification] Build, run appropriate functions (through cli) Change-Id: If6823243d66606d28bf6b45c5b2ece8ab7c06e49 --- cli/command-line-interface.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++ cli/command-line-interface.hpp | 20 ++++++++++ cli/main.cpp | 24 ++++++++++++ 3 files changed, 133 insertions(+) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 1222a86..06be364 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -31,7 +31,12 @@ #include #include #include +#include +#include +#include +#include #include +#include using namespace std; @@ -82,6 +87,14 @@ finish: } } +template +string stringAsInStream(const T& value) +{ + std::ostringstream stream; + stream << value; + return stream.str(); +} + ostream& operator<<(ostream& out, const VsmZoneState& state) { const char* name; @@ -113,6 +126,30 @@ ostream& operator<<(ostream& out, const VsmZone& zone) return out; } +typedef vector> Table; + +ostream& operator<<(ostream& out, const Table& table) +{ + vector sizes; + for (const auto& row : table) { + if (sizes.size() < row.size()) { + sizes.resize(row.size()); + } + for (size_t i = 0; i < row.size(); ++i) { + sizes[i] = max(sizes[i], row[i].length()); + } + } + + for (const auto& row : table) { + for (size_t i = 0; i < row.size(); ++i) { + out << left << setw(sizes[i]+2) << row[i]; + } + out << "\n"; + } + + return out; +} + } // namespace void CommandLineInterface::printUsage(std::ostream& out) const @@ -210,6 +247,58 @@ void unlock_zone(int pos, int argc, const char** argv) one_shot(bind(vsm_unlock_zone, _1, argv[pos + 1])); } +void get_zones_status(int /* pos */, int /* argc */, const char** /* argv */) +{ + using namespace std::placeholders; + + VsmArrayString ids; + VsmString activeId; + Table table; + + one_shot(bind(vsm_get_zone_ids, _1, &ids)); + one_shot(bind(vsm_get_active_zone_id, _1, &activeId)); + table.push_back({"Active", "Id", "State", "Terminal", "Root"}); + for (VsmString* id = ids; *id; ++id) { + VsmZone zone; + one_shot(bind(vsm_lookup_zone_by_id, _1, *id, &zone)); + assert(string(zone->id) == string(*id)); + table.push_back({string(zone->id) == string(activeId) ? "*" : "", + zone->id, + stringAsInStream(zone->state), + to_string(zone->terminal), + zone->rootfs_path}); + vsm_zone_free(zone); + } + vsm_string_free(activeId); + vsm_array_string_free(ids); + cout << table << endl; +} + +void get_zone_ids(int /* pos */, int /* argc */, const char** /* argv */) +{ + using namespace std::placeholders; + + VsmArrayString ids; + one_shot(bind(vsm_get_zone_ids, _1, &ids)); + string delim; + for (VsmString* id = ids; *id; ++id) { + cout << delim << *id; + delim = ", "; + } + cout << endl; + vsm_array_string_free(ids); +} + +void get_active_zone_id(int /* pos */, int /* argc */, const char** /* argv */) +{ + using namespace std::placeholders; + + VsmString id; + one_shot(bind(vsm_get_active_zone_id, _1, &id)); + cout << id << endl; + vsm_string_free(id); +} + void lookup_zone_by_id(int pos, int argc, const char** argv) { using namespace std::placeholders; diff --git a/cli/command-line-interface.hpp b/cli/command-line-interface.hpp index 6a655d8..055bb1e 100644 --- a/cli/command-line-interface.hpp +++ b/cli/command-line-interface.hpp @@ -147,6 +147,26 @@ void lock_zone(int pos, int argc, const char** argv); void unlock_zone(int pos, int argc, const char** argv); /** + * Parses command line arguments and prints list of zone with + * some useful informations (id, state, terminal, root path) + */ +void get_zones_status(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_get_zone_ids + * + * @see vsm_get_zone_ids + */ +void get_zone_ids(int pos, int argc, const char** argv); + +/** + * Parses command line arguments and call vsm_get_active_zone_id + * + * @see vsm_get_active_zone_id + */ +void get_active_zone_id(int pos, int argc, const char** argv); + +/** * Parses command line arguments and call vsm_lookup_zone_by_id * * @see vsm_lookup_zone_by_id diff --git a/cli/main.cpp b/cli/main.cpp index 1afe134..d003d10 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -92,6 +92,30 @@ std::map commands = { } }, { + "get_zones_status", { + get_zones_status, + "get_zones_status", + "Get list of zone with some useful informations (id, state, terminal, root path)", + {} + } + }, + { + "get_zone_ids", { + get_zone_ids, + "get_zone_ids", + "Get all zone ids", + {} + } + }, + { + "get_active_zone_id", { + get_active_zone_id, + "get_active_zone_id", + "Get active (foreground) zone ids", + {} + } + }, + { "lookup_zone_by_id", { lookup_zone_by_id, "lookup_zone_by_id zone_id", -- 2.7.4