From 21d934cb08890c90816f9f94cbdc096599c548e2 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Tue, 16 Dec 2014 11:40:03 +0100 Subject: [PATCH 01/16] Some cppcheck fixes [Bug] Some cppcheck fixes (const, uniused variable) [Cause] N/A [Solution] N/A [Verification] Build, install, run cppcheck Change-Id: I58fa2e3434c6a7546441ba310bb437b59c7fd6de --- client/vasum-client-impl.cpp | 4 ++-- client/vasum-client-impl.hpp | 4 ++-- server/input-monitor.cpp | 2 +- server/input-monitor.hpp | 2 +- server/proxy-call-policy.cpp | 2 +- server/proxy-call-policy.hpp | 2 +- server/zone-connection-transport.cpp | 2 +- server/zone-connection-transport.hpp | 2 +- server/zone.cpp | 2 +- server/zone.hpp | 2 +- server/zones-manager.cpp | 6 +++--- server/zones-manager.hpp | 6 +++--- tests/unit_tests/config/ut-configuration.cpp | 2 +- zone-daemon/main.cpp | 2 -- 14 files changed, 19 insertions(+), 21 deletions(-) diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 3c9448b..23270e4 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -307,12 +307,12 @@ VsmStatus Client::signalUnsubscribe(VsmSubscriptionId id) return vsm_get_status(); } -const char* Client::vsm_get_status_message() noexcept +const char* Client::vsm_get_status_message() const noexcept { return mStatus.mMsg.c_str(); } -VsmStatus Client::vsm_get_status() noexcept +VsmStatus Client::vsm_get_status() const noexcept { return mStatus.mVsmStatus; } diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 18eb726..66ffa82 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -96,12 +96,12 @@ public: /** * @see ::vsm_get_status_message */ - const char* vsm_get_status_message() noexcept; + const char* vsm_get_status_message() const noexcept; /** * @see ::vsm_get_status */ - VsmStatus vsm_get_status() noexcept; + VsmStatus vsm_get_status() const noexcept; /** * @see ::vsm_get_zone_dbuses diff --git a/server/input-monitor.cpp b/server/input-monitor.cpp index 8d641b1..7937840 100644 --- a/server/input-monitor.cpp +++ b/server/input-monitor.cpp @@ -148,7 +148,7 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, } } // namespace -std::string InputMonitor::getDevicePath() +std::string InputMonitor::getDevicePath() const { std::string device = mConfig.device; if (fs::path(device).is_absolute() diff --git a/server/input-monitor.hpp b/server/input-monitor.hpp index 93c2d75..2b0ed45 100644 --- a/server/input-monitor.hpp +++ b/server/input-monitor.hpp @@ -56,7 +56,7 @@ private: std::list mEventTimes; GIOChannel* mChannelPtr; - std::string getDevicePath(); + std::string getDevicePath() const; void createGIOChannel(const std::string& devicePath); // Internal callback to be registered at glib g_io_add_watch() diff --git a/server/proxy-call-policy.cpp b/server/proxy-call-policy.cpp index 281cb3a..9fed52b 100644 --- a/server/proxy-call-policy.cpp +++ b/server/proxy-call-policy.cpp @@ -51,7 +51,7 @@ bool ProxyCallPolicy::isProxyCallAllowed(const std::string& caller, const std::string& targetBusName, const std::string& targetObjectPath, const std::string& targetInterface, - const std::string& targetMethod) + const std::string& targetMethod) const { for (const ProxyCallRule& rule : mProxyCallRules) { if (match(rule.caller, caller) diff --git a/server/proxy-call-policy.hpp b/server/proxy-call-policy.hpp index 4176979..d6fbaed 100644 --- a/server/proxy-call-policy.hpp +++ b/server/proxy-call-policy.hpp @@ -44,7 +44,7 @@ public: const std::string& targetBusName, const std::string& targetObjectPath, const std::string& targetInterface, - const std::string& targetMethod); + const std::string& targetMethod) const; private: std::vector mProxyCallRules; diff --git a/server/zone-connection-transport.cpp b/server/zone-connection-transport.cpp index d209705..0757241 100644 --- a/server/zone-connection-transport.cpp +++ b/server/zone-connection-transport.cpp @@ -100,7 +100,7 @@ ZoneConnectionTransport::~ZoneConnectionTransport() } -std::string ZoneConnectionTransport::acquireAddress() +std::string ZoneConnectionTransport::acquireAddress() const { if (mRunMountPoint.empty()) { return std::string(); diff --git a/server/zone-connection-transport.hpp b/server/zone-connection-transport.hpp index 5729389..d651f3c 100644 --- a/server/zone-connection-transport.hpp +++ b/server/zone-connection-transport.hpp @@ -44,7 +44,7 @@ public: /** * Gets dbus addres. Will block until address is available. */ - std::string acquireAddress(); + std::string acquireAddress() const; /** * Set whether object should detach from transport filesystem on exit diff --git a/server/zone.cpp b/server/zone.cpp index 5f6dce0..199ecff 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -200,7 +200,7 @@ void Zone::disconnect() } } -std::string Zone::getDbusAddress() +std::string Zone::getDbusAddress() const { Lock lock(mReconnectMutex); return mDbusAddress; diff --git a/server/zone.hpp b/server/zone.hpp index 1fdc588..fba9399 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -226,7 +226,7 @@ public: /** * Get a dbus address */ - std::string getDbusAddress(); + std::string getDbusAddress() const; /** * Get id of VT diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 2ce03d7..a1dcc71 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -305,7 +305,7 @@ bool ZonesManager::isRunning(const std::string& zoneId) return iter->second->isRunning(); } -std::string ZonesManager::getRunningForegroundZoneId() +std::string ZonesManager::getRunningForegroundZoneId() const { for (auto& zone : mZones) { if (zone.first == mConfig.foregroundId && @@ -523,7 +523,7 @@ void ZonesManager::handleProxyCall(const std::string& caller, asyncResultCallback); } -void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) const { std::vector entries; for (auto& zone : mZones) { @@ -542,7 +542,7 @@ void ZonesManager::handleDbusStateChanged(const std::string& zoneId, mHostConnection.signalZoneDbusState(zoneId, dbusAddress); } -void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) +void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) const { std::vector zoneIds; for(auto& zone: mZones){ diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index b58da86..41a3e7c 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -92,7 +92,7 @@ public: /** * @return id of the currently focused/foreground zone */ - std::string getRunningForegroundZoneId(); + std::string getRunningForegroundZoneId() const; /** * @return id of next to currently focused/foreground zone. If currently focused zone @@ -138,9 +138,9 @@ private: const std::string& targetMethod, GVariant* parameters, dbus::MethodResultBuilder::Pointer result); - void handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result); + void handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) const; void handleDbusStateChanged(const std::string& zoneId, const std::string& dbusAddress); - void handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result); + void handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) const; void handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result); void handleGetZoneInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result); void handleDeclareFileCall(const std::string& zone, diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index e46bfd8..4e6bedc 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -50,7 +50,7 @@ struct TestConfig { ( intVal ) - SubSubConfig() : moved(false) {} + 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; diff --git a/zone-daemon/main.cpp b/zone-daemon/main.cpp index 846f431..f0c00c6 100644 --- a/zone-daemon/main.cpp +++ b/zone-daemon/main.cpp @@ -57,8 +57,6 @@ const std::string PROGRAM_NAME_AND_VERSION = int main(int argc, char* argv[]) { - std::string configPath ; - try { po::options_description desc("Allowed options"); -- 2.7.4 From 9d21e0db847cebff18406f16f5d3234ec0d9e0ec Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Wed, 17 Dec 2014 13:41:13 +0100 Subject: [PATCH 02/16] Fix synchronization issues [Bug/Feature] Missing mutex [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Iec7e016b62a0f3a671fd4843957e1bc6a265f84e --- server/zones-manager.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++- server/zones-manager.hpp | 4 ++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index a1dcc71..a0cb274 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -195,6 +195,8 @@ void ZonesManager::createZone(const std::string& zoneConfig) zone->setDbusStateChangedCallback(bind(&ZonesManager::handleDbusStateChanged, this, id, _1)); + Lock lock(mMutex); + mZones.insert(ZoneMap::value_type(id, std::move(zone))); // after zone is created successfully, put a file informing that zones are enabled @@ -208,7 +210,8 @@ void ZonesManager::createZone(const std::string& zoneConfig) void ZonesManager::destroyZone(const std::string& zoneId) { - // TODO mutex for mZones access + Lock lock(mMutex); + auto it = mZones.find(zoneId); if (it == mZones.end()) { LOGE("Failed to destroy zone " << zoneId << ": no such zone"); @@ -228,6 +231,8 @@ void ZonesManager::destroyZone(const std::string& zoneId) void ZonesManager::focus(const std::string& zoneId) { + Lock lock(mMutex); + /* try to access the object first to throw immediately if it doesn't exist */ ZoneMap::mapped_type& foregroundZone = mZones.at(zoneId); @@ -249,6 +254,8 @@ void ZonesManager::startAll() { LOGI("Starting all zones"); + Lock lock(mMutex); + bool isForegroundFound = false; for (auto& zone : mZones) { @@ -279,6 +286,8 @@ void ZonesManager::stopAll() { LOGI("Stopping all zones"); + Lock lock(mMutex); + for (auto& zone : mZones) { zone.second->stop(); } @@ -286,6 +295,8 @@ void ZonesManager::stopAll() bool ZonesManager::isPaused(const std::string& zoneId) { + Lock lock(mMutex); + auto iter = mZones.find(zoneId); if (iter == mZones.end()) { LOGE("No such zone id: " << zoneId); @@ -297,6 +308,8 @@ bool ZonesManager::isPaused(const std::string& zoneId) bool ZonesManager::isRunning(const std::string& zoneId) { + Lock lock(mMutex); + auto iter = mZones.find(zoneId); if (iter == mZones.end()) { LOGE("No such zone id: " << zoneId); @@ -307,6 +320,8 @@ bool ZonesManager::isRunning(const std::string& zoneId) std::string ZonesManager::getRunningForegroundZoneId() const { + Lock lock(mMutex); + for (auto& zone : mZones) { if (zone.first == mConfig.foregroundId && zone.second->isRunning()) { @@ -318,6 +333,8 @@ std::string ZonesManager::getRunningForegroundZoneId() const std::string ZonesManager::getNextToForegroundZoneId() { + Lock lock(mMutex); + // handles case where there is no next zone if (mZones.size() < 2) { return std::string(); @@ -349,6 +366,8 @@ void ZonesManager::switchingSequenceMonitorNotify() void ZonesManager::setZonesDetachOnExit() { + Lock lock(mMutex); + mDetachOnExit = true; for (auto& zone : mZones) { @@ -362,6 +381,9 @@ void ZonesManager::notifyActiveZoneHandler(const std::string& caller, { LOGI("notifyActiveZoneHandler(" << caller << ", " << application << ", " << message << ") called"); + + Lock lock(mMutex); + try { const std::string activeZone = getRunningForegroundZoneId(); if (!activeZone.empty() && caller != activeZone) { @@ -375,6 +397,8 @@ void ZonesManager::notifyActiveZoneHandler(const std::string& caller, void ZonesManager::displayOffHandler(const std::string& /*caller*/) { // get config of currently set zone and switch if switchToDefaultAfterTimeout is true + Lock lock(mMutex); + const std::string activeZoneName = getRunningForegroundZoneId(); const auto& activeZone = mZones.find(activeZoneName); @@ -414,6 +438,8 @@ void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId, << "dst: " << dstZoneId << "\n" << "path: " << path); + Lock lock(mMutex); + ZoneMap::const_iterator srcIter = mZones.find(srcZoneId); if (srcIter == mZones.end()) { LOGE("Source zone '" << srcZoneId << "' not found"); @@ -507,6 +533,8 @@ void ZonesManager::handleProxyCall(const std::string& caller, return; } + Lock lock(mMutex); + ZoneMap::const_iterator targetIter = mZones.find(target); if (targetIter == mZones.end()) { LOGE("Target zone '" << target << "' not found"); @@ -525,6 +553,8 @@ void ZonesManager::handleProxyCall(const std::string& caller, void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) const { + Lock lock(mMutex); + std::vector entries; for (auto& zone : mZones) { GVariant* zoneId = g_variant_new_string(zone.first.c_str()); @@ -544,6 +574,8 @@ void ZonesManager::handleDbusStateChanged(const std::string& zoneId, void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) const { + Lock lock(mMutex); + std::vector zoneIds; for(auto& zone: mZones){ zoneIds.push_back(g_variant_new_string(zone.first.c_str())); @@ -558,6 +590,9 @@ void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer resul void ZonesManager::handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result) { LOGI("GetActiveZoneId call"); + + Lock lock(mMutex); + if (!mConfig.foregroundId.empty() && mZones[mConfig.foregroundId]->isRunning()){ result->set(g_variant_new("(s)", mConfig.foregroundId.c_str())); } else { @@ -569,6 +604,9 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("GetZoneInfo call"); + + Lock lock(mMutex); + if (mZones.count(id) == 0) { LOGE("No zone with id=" << id); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -607,7 +645,10 @@ void ZonesManager::handleDeclareFileCall(const std::string& zone, dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareFile call"); + try { + Lock lock(mMutex); + mZones.at(zone)->declareFile(type, path, flags, mode); result->setVoid(); } catch (const std::out_of_range&) { @@ -628,7 +669,10 @@ void ZonesManager::handleDeclareMountCall(const std::string& source, dbus::MethodResultBuilder::Pointer result) { LOGI("DeclareMount call"); + try { + Lock lock(mMutex); + mZones.at(zone)->declareMount(source, target, type, flags, data); result->setVoid(); } catch (const std::out_of_range&) { @@ -647,6 +691,8 @@ void ZonesManager::handleDeclareLinkCall(const std::string& source, { LOGI("DeclareLink call"); try { + Lock lock(mMutex); + mZones.at(zone)->declareLink(source, target); result->setVoid(); } catch (const std::out_of_range&) { @@ -662,6 +708,9 @@ void ZonesManager::handleSetActiveZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("SetActiveZone call; Id=" << id ); + + Lock lock(mMutex); + auto zone = mZones.find(id); if (zone == mZones.end()){ LOGE("No zone with id=" << id ); @@ -737,6 +786,8 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, LOGI("Creating zone " << id); + Lock lock(mMutex); + // TODO: This solution is temporary. It utilizes direct access to config files when creating new // zones. Update this handler when config database will appear. namespace fs = boost::filesystem; @@ -819,6 +870,8 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, void ZonesManager::handleDestroyZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { + Lock lock(mMutex); + if (mZones.find(id) == mZones.end()) { LOGE("Failed to destroy zone - no such zone id: " << id); result->setError(api::ERROR_INVALID_ID, "No such zone id"); @@ -845,6 +898,9 @@ void ZonesManager::handleLockZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("LockZone call; Id=" << id ); + + Lock lock(mMutex); + auto iter = mZones.find(id); if (iter == mZones.end()) { LOGE("Failed to lock zone - no such zone id: " << id); @@ -875,6 +931,9 @@ void ZonesManager::handleUnlockZoneCall(const std::string& id, dbus::MethodResultBuilder::Pointer result) { LOGI("UnlockZone call; Id=" << id ); + + Lock lock(mMutex); + auto iter = mZones.find(id); if (iter == mZones.end()) { LOGE("Failed to unlock zone - no such zone id: " << id); diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index 41a3e7c..2ed5312 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -106,7 +106,11 @@ public: void setZonesDetachOnExit(); private: + typedef std::recursive_mutex Mutex; + typedef std::unique_lock Lock; + utils::Worker::Pointer mWorker; + mutable Mutex mMutex; // used to protect mZones ZonesManagerConfig mConfig; std::string mConfigPath; HostConnection mHostConnection; -- 2.7.4 From 01d4faf05df4e0be4b5daa7e9468583bc2992c06 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Wed, 17 Dec 2014 15:46:59 +0100 Subject: [PATCH 03/16] Rearrange create zone process [Bug/Feature] Remove start functionality, add VT support. [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I67a7eebf6e45bb2cab29e833fbf53c1bdaeb88fb Signed-off-by: Dariusz Michaluk --- server/configs/daemon.conf.in | 2 +- server/configs/templates/template.conf | 4 ++-- server/zones-manager.cpp | 20 +++++++++----------- tests/unit_tests/server/ut-zones-manager.cpp | 6 +++--- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/server/configs/daemon.conf.in b/server/configs/daemon.conf.in index 296c1c7..648d986 100644 --- a/server/configs/daemon.conf.in +++ b/server/configs/daemon.conf.in @@ -1,7 +1,7 @@ { "zoneConfigs" : ["zones/private.conf", "zones/business.conf"], "zonesPath" : "${DATA_DIR}/.zones", - "zoneImagePath" : "${DATA_DIR}/.zones/img/system-data.img", + "zoneImagePath" : "", "zoneTemplatePath" : "templates", "zoneNewConfigPrefix" : "/var/lib/vasum", "runMountPointPrefix" : "/var/run/zones", diff --git a/server/configs/templates/template.conf b/server/configs/templates/template.conf index 1939d51..fa6ef5c 100644 --- a/server/configs/templates/template.conf +++ b/server/configs/templates/template.conf @@ -1,13 +1,13 @@ { "name" : "~NAME~", - "lxcTemplate" : "template.sh", + "lxcTemplate" : "tizen-common-wayland.sh", "initWithArgs" : [], "ipv4Gateway" : "10.0.~IP~.1", "ipv4" : "10.0.~IP~.2", "cpuQuotaForeground" : -1, "cpuQuotaBackground" : 1000, "privilege" : 10, - "vt" : -1, + "vt" : ~VT~, "switchToDefaultAfterTimeout" : true, "enableDbusIntegration" : true, "runMountPoint" : "~NAME~/run", diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index a0cb274..eafbe4b 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -69,8 +69,10 @@ const std::string ENABLED_FILE_NAME = "enabled"; const boost::regex ZONE_NAME_REGEX("~NAME~"); const boost::regex ZONE_IP_THIRD_OCTET_REGEX("~IP~"); +const boost::regex ZONE_VT_REGEX("~VT~"); const unsigned int ZONE_IP_BASE_THIRD_OCTET = 100; +const unsigned int ZONE_VT_BASE = 1; } // namespace @@ -764,6 +766,12 @@ void ZonesManager::generateNewConfig(const std::string& id, LOGD("IP third octet: " << thirdOctetStr); resultConfig = boost::regex_replace(resultConfig, ZONE_IP_THIRD_OCTET_REGEX, thirdOctetStr); + // generate first free VT number + // TODO change algorithm after implementing removeZone + std::string freeVT = std::to_string(ZONE_VT_BASE + mZones.size() + 1); + LOGD("VT number: " << freeVT); + resultConfig = boost::regex_replace(resultConfig, ZONE_VT_REGEX, freeVT); + if (!utils::saveFileContent(resultPath, resultConfig)) { LOGE("Faield to save new config file."); throw ZoneOperationException("Failed to save new config file."); @@ -854,17 +862,7 @@ void ZonesManager::handleCreateZoneCall(const std::string& id, return; } - auto resultCallback = [this, id, result](bool succeeded) { - if (succeeded) { - focus(id); - result->setVoid(); - } else { - LOGE("Failed to start zone."); - // TODO removeZone - result->setError(api::ERROR_INTERNAL, "Failed to start zone"); - } - }; - mZones[id]->startAsync(resultCallback); + result->setVoid(); } void ZonesManager::handleDestroyZoneCall(const std::string& id, diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 07c03dd..4db9434 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -1065,16 +1065,16 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) dbus.callAsyncMethodCreateZone(zone1, resultCallback); BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); - BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone1); - // create zone2 dbus.callAsyncMethodCreateZone(zone2, resultCallback); BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); - BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone2); //TODO is this valid? // create zone3 dbus.callAsyncMethodCreateZone(zone3, resultCallback); BOOST_REQUIRE(callDone.wait(EVENT_TIMEOUT)); + + cm.startAll(); + BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), zone3); // destroy zone2 -- 2.7.4 From fa7b65cc7179ef7a037f5d3114fe501147e0e72b Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Tue, 9 Dec 2014 15:56:40 +0100 Subject: [PATCH 04/16] Taking into account the provision configuration [Feature] Taking into account the provision configuration [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I2b564bb3f9af2514781ff2233cabb7b1ca654e69 --- client/vasum-client-impl.cpp | 4 +- common/base-exception.cpp | 41 ++++ common/base-exception.hpp | 5 + common/utils/fs.cpp | 182 ++++++++++++++-- common/utils/fs.hpp | 33 +++ server/CMakeLists.txt | 1 + server/configs/templates/template.conf | 6 +- server/configs/zones/business.conf | 6 +- server/configs/zones/private.conf | 6 +- server/zone-config.hpp | 8 +- ...ioning-config.hpp => zone-provision-config.hpp} | 0 server/zone-provision.cpp | 214 ++++++++++++++++++ server/zone-provision.hpp | 95 ++++++++ server/zone.cpp | 57 ++--- server/zone.hpp | 8 +- server/zones-manager.cpp | 5 +- .../configs/ut-client/zones/console1-dbus.conf.in | 3 +- .../configs/ut-client/zones/console2-dbus.conf.in | 3 +- .../configs/ut-client/zones/console3-dbus.conf.in | 3 +- .../server/configs/ut-server/zones/zone1.conf | 3 +- .../server/configs/ut-server/zones/zone2.conf | 3 +- .../server/configs/ut-server/zones/zone3.conf | 3 +- .../server/configs/ut-zone-admin/zones/buggy.conf | 3 +- .../configs/ut-zone-admin/zones/missing.conf | 3 +- .../ut-zone-admin/zones/test-no-shutdown.conf | 3 +- .../server/configs/ut-zone-admin/zones/test.conf | 3 +- .../server/configs/ut-zone/zones/buggy.conf | 3 +- .../server/configs/ut-zone/zones/test-dbus.conf.in | 3 +- .../server/configs/ut-zone/zones/test.conf | 3 +- .../ut-zones-manager/templates/template.conf.in | 3 +- .../ut-zones-manager/zones/console1-dbus.conf.in | 3 +- .../configs/ut-zones-manager/zones/console1.conf | 3 +- .../ut-zones-manager/zones/console2-dbus.conf.in | 3 +- .../configs/ut-zones-manager/zones/console2.conf | 3 +- .../ut-zones-manager/zones/console3-dbus.conf.in | 3 +- .../configs/ut-zones-manager/zones/console3.conf | 3 +- tests/unit_tests/server/ut-zone-provision.cpp | 242 +++++++++++++++++++++ tests/unit_tests/server/ut-zones-manager.cpp | 151 ------------- zone-daemon/CMakeLists.txt | 3 +- 39 files changed, 894 insertions(+), 233 deletions(-) create mode 100644 common/base-exception.cpp rename server/{provisioning-config.hpp => zone-provision-config.hpp} (100%) create mode 100644 server/zone-provision.cpp create mode 100644 server/zone-provision.hpp create mode 100644 tests/unit_tests/server/ut-zone-provision.cpp diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 23270e4..2aa923d 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -322,7 +322,7 @@ VsmStatus Client::vsm_get_zone_dbuses(VsmArrayString* keys, VsmArrayString* valu assert(keys); assert(values); - GVariant* out; + GVariant* out = NULL; VsmStatus ret = callMethod(HOST_INTERFACE, api::host::METHOD_GET_ZONE_DBUSES, NULL, @@ -343,7 +343,7 @@ VsmStatus Client::vsm_get_zone_ids(VsmArrayString* array) noexcept { assert(array); - GVariant* out; + GVariant* out = NULL; VsmStatus ret = callMethod(HOST_INTERFACE, api::host::METHOD_GET_ZONE_ID_LIST, NULL, diff --git a/common/base-exception.cpp b/common/base-exception.cpp new file mode 100644 index 0000000..bcfe600 --- /dev/null +++ b/common/base-exception.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Vasum base exception implementation + */ + +#include "base-exception.hpp" + +#include +#include +#include + +namespace vasum { + +const int ERROR_MESSAGE_BUFFER_CAPACITY = 256; + +std::string getSystemErrorMessage() +{ + char buf[ERROR_MESSAGE_BUFFER_CAPACITY]; + return strerror_r(errno, buf, sizeof(buf)); +} + +} // namespace vasum diff --git a/common/base-exception.hpp b/common/base-exception.hpp index d501e3d..3969404 100644 --- a/common/base-exception.hpp +++ b/common/base-exception.hpp @@ -41,6 +41,11 @@ struct VasumException: public std::runtime_error { VasumException(const std::string& error = "") : std::runtime_error(error) {} }; +/** + * Return string describing error number + * it is wrapper for strerror_r + */ +std::string getSystemErrorMessage(); } // namespace vasum diff --git a/common/utils/fs.cpp b/common/utils/fs.cpp index 3330cbf..763bdfe 100644 --- a/common/utils/fs.cpp +++ b/common/utils/fs.cpp @@ -38,10 +38,13 @@ #include #include #include +#include #include +namespace fs = boost::filesystem; + namespace vasum { namespace utils { @@ -140,6 +143,36 @@ bool mountRun(const std::string& path) || utils::mountTmpfs(path, RUN_MOUNT_POINT_FLAGS, RUN_MOUNT_POINT_OPTIONS_NO_SMACK); } +bool mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data) +{ + int ret = ::mount(source.c_str(), + target.c_str(), + filesystemtype.c_str(), + mountflags, + data.c_str()); + if (ret < 0) { + LOGE("Mount operation failure: " + << "source path: " + << source + << ", target path: " + << target + << ", filesystemtype: " + << filesystemtype + << ", mountflags: " + << mountflags + << ", data: " + << data + << ", msg: " + << getSystemErrorMessage()); + return false; + } + return true; +} + bool umount(const std::string& path) { if (::umount(path.c_str()) != 0) { @@ -181,7 +214,6 @@ bool moveFile(const std::string& src, const std::string& dst) { bool bResult; - namespace fs = boost::filesystem; boost::system::error_code error; // The destination has to be a full path (including a file name) @@ -218,8 +250,6 @@ namespace { bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesystem::path& dst) { - namespace fs = boost::filesystem; - try { for (fs::directory_iterator file(src); file != fs::directory_iterator(); @@ -274,26 +304,34 @@ bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesys return true; } +boost::filesystem::perms getPerms(const mode_t& mode) +{ + return static_cast(mode); +} + +bool copySmackLabel(const std::string& /* src */, const std::string& /* dst */) +{ + //TODO: fill copySmackLabel function + return true; +} + + } // namespace bool copyDirContents(const std::string& src, const std::string& dst) { - namespace fs = boost::filesystem; - return copyDirContentsRec(fs::path(src), fs::path(dst)); } bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode) { - namespace fs = boost::filesystem; - fs::path dirPath(path); - boost::system::error_code ec; + boost::system::error_code errorCode; bool runDirCreated = false; if (!fs::exists(dirPath)) { - if (!fs::create_directory(dirPath, ec)) { + if (!fs::create_directory(dirPath, errorCode)) { LOGE("Failed to create directory '" << path << "': " - << ec.message()); + << errorCode.message()); return false; } runDirCreated = true; @@ -303,10 +341,10 @@ bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem: } // set permissions - fs::permissions(dirPath, mode, ec); + fs::permissions(dirPath, mode, errorCode); if (fs::status(dirPath).permissions() != mode) { LOGE("Failed to set permissions to '" << path << "': " - << ec.message()); + << errorCode.message()); return false; } @@ -323,10 +361,36 @@ bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem: return true; } -bool createEmptyDir(const std::string& path) +bool createDirs(const std::string& path, mode_t mode) { - namespace fs = boost::filesystem; + boost::filesystem::perms perms = getPerms(mode); + std::vector dirs; + fs::path prefix; + fs::path dirPath = fs::path(path); + for (const auto dir : dirPath) { + prefix /= dir; + if (!fs::exists(prefix)) { + bool created = createDir(prefix.string(), -1, -1, perms); + if (created) { + dirs.push_back(prefix); + } else { + LOGE("Failed to create dir"); + for (auto dir = dirs.rbegin(); dir != dirs.rend(); ++dir) { + boost::system::error_code errorCode; + fs::remove(*dir, errorCode); + if (errorCode) { + LOGE("Error during cleaning: dir: " << *dir << ", msg: " << errorCode.message()); + } + } + return false; + } + } + } + return true; +} +bool createEmptyDir(const std::string& path) +{ fs::path dirPath(path); boost::system::error_code ec; bool cleanDirCreated = false; @@ -353,5 +417,95 @@ bool createEmptyDir(const std::string& path) return true; } +bool createFile(const std::string& path, int flags, mode_t mode) +{ + // TODO: Check if we really need *flags* in the API + int ret = ::open(path.c_str(), flags, mode); + if (ret < 0) { + LOGE("Failed to create file: path=host:" + << path + << ", msg: " + << getSystemErrorMessage()); + return false; + } + close(ret); + return true; +} + +bool createFifo(const std::string& path, mode_t mode) +{ + int ret = ::mkfifo(path.c_str(), mode); + if (ret < 0) { + LOGE("Failed to make fifo: path=host:" << path); + return false; + } + return true; +} + +bool copyFile(const std::string& src, const std::string& dest) +{ + boost::system::error_code errorCode; + fs::copy_file(src, dest, errorCode); + if (errorCode) { + LOGE("Failed to copy file: msg: " + << errorCode.message() + << ", path=host:" + << src + << ", path=host:" + << dest); + return false; + } + bool retSmack = copySmackLabel(src, dest); + if (!retSmack) { + LOGE("Failed to copy file: msg: (can't copy smacklabel) " + << ", path=host:" + << src + << ", path=host:" + << dest); + fs::remove(src, errorCode); + if (errorCode) { + LOGE("Failed to clean after copy failure: path=host:" + << src + << ", msg: " + << errorCode.message()); + } + return false; + } + return true; +} + +bool createLink(const std::string& src, const std::string& dest) +{ + int retLink = ::link(src.c_str(), dest.c_str()); + if (retLink < 0) { + LOGE("Failed to hard link: path=host:" + << src + << ", path=host:" + << dest + << ", msg:" + << getSystemErrorMessage()); + return false; + } + bool retSmack = copySmackLabel(src, dest); + if (!retSmack) { + LOGE("Failed to copy smack label: path=host:" + << src + << ", path=host:" + << dest); + boost::system::error_code ec; + fs::remove(dest, ec); + if (!ec) { + LOGE("Failed to clean after hard link creation failure: path=host:" + << src + << ", to: " + << dest + << ", msg: " + << ec.message()); + } + return false; + } + return true; +} + } // namespace utils } // namespace vasum diff --git a/common/utils/fs.hpp b/common/utils/fs.hpp index 35000c6..b00f95a 100644 --- a/common/utils/fs.hpp +++ b/common/utils/fs.hpp @@ -70,6 +70,15 @@ bool listDir(const std::string& path, std::vector& files); bool mountRun(const std::string& path); /** + * Creates mount point + */ +bool mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data); + +/** * Umounts a filesystem */ bool umount(const std::string& path); @@ -102,12 +111,36 @@ bool copyDirContents(const std::string& src, const std::string& dst); bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode); /** + * Recursively creates a directory with specific permissions set. + */ +bool createDirs(const std::string& path, mode_t mode); + +/** * Creates an empty directory, ready to serve as mount point. * Succeeds either if path did not exist and was created successfully, or if already existing dir * under the same path is empty and is not a mount point. */ bool createEmptyDir(const std::string& path); +/** + * Creates an empty file + */ +bool createFile(const std::string& path, int flags, mode_t mode); + +/** + * Creates an FIFO special file + */ +bool createFifo(const std::string& path, mode_t mode); + +/** + * Copy an file + */ +bool copyFile(const std::string& src, const std::string& dest); + +/** + * Create hard link + */ +bool createLink(const std::string& src, const std::string& dest); } // namespace utils } // namespace vasum diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 8c7604a..adfef3e 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -34,6 +34,7 @@ PKG_CHECK_MODULES(SERVER_DEPS REQUIRED lxc json gio-2.0 libsystemd-journal libsy libcap-ng libLogger libSimpleDbus libConfig) 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}) diff --git a/server/configs/templates/template.conf b/server/configs/templates/template.conf index fa6ef5c..e011a64 100644 --- a/server/configs/templates/template.conf +++ b/server/configs/templates/template.conf @@ -12,5 +12,9 @@ "enableDbusIntegration" : true, "runMountPoint" : "~NAME~/run", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [ "/tmp/", + "/run/", + "/opt/usr/data/", + "/opt/usr/dbsapce/" ] } diff --git a/server/configs/zones/business.conf b/server/configs/zones/business.conf index 6a4bc50..74c3bad 100644 --- a/server/configs/zones/business.conf +++ b/server/configs/zones/business.conf @@ -12,5 +12,9 @@ "switchToDefaultAfterTimeout" : true, "runMountPoint" : "business/run", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [ "/tmp/", + "/run/", + "/opt/usr/data/", + "/opt/usr/dbsapce/" ] } diff --git a/server/configs/zones/private.conf b/server/configs/zones/private.conf index 2a1147f..ffb7e85 100644 --- a/server/configs/zones/private.conf +++ b/server/configs/zones/private.conf @@ -12,5 +12,9 @@ "switchToDefaultAfterTimeout" : true, "runMountPoint" : "private/run", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [ "/tmp/", + "/run/", + "/opt/usr/data/", + "/opt/usr/dbsapce/" ] } diff --git a/server/zone-config.hpp b/server/zone-config.hpp index b801af8..f92ca8f 100644 --- a/server/zone-config.hpp +++ b/server/zone-config.hpp @@ -112,6 +112,11 @@ struct ZoneConfig { */ std::vector permittedToRecv; + /** + * Valid hard link prefixes. + */ + std::vector validLinkPrefixes; + CONFIG_REGISTER ( name, @@ -127,7 +132,8 @@ struct ZoneConfig { cpuQuotaBackground, runMountPoint, permittedToSend, - permittedToRecv + permittedToRecv, + validLinkPrefixes ) }; diff --git a/server/provisioning-config.hpp b/server/zone-provision-config.hpp similarity index 100% rename from server/provisioning-config.hpp rename to server/zone-provision-config.hpp diff --git a/server/zone-provision.cpp b/server/zone-provision.cpp new file mode 100644 index 0000000..3bff4b5 --- /dev/null +++ b/server/zone-provision.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Implementation of class for managing zone provsion + */ + +#include "config.hpp" + +#include "zone-provision.hpp" +#include "zone-provision-config.hpp" + +#include "logger/logger.hpp" +#include "utils/fs.hpp" +#include "utils/exception.hpp" +#include "config/manager.hpp" +#include "vasum-client.h" + +#include + +#include +#include + +namespace fs = boost::filesystem; + +namespace vasum { + +namespace { + +const std::string ZONE_PROVISION_FILE = "provision.conf"; + +void declareUnit(const std::string& file, ZoneProvisioning::Unit&& unit) +{ + // TODO: Add to the dynamic configuration + ZoneProvisioning config; + if (fs::exists(file)) { + config::loadFromFile(file, config); + } + config.units.push_back(std::move(unit)); + config::saveToFile(file, config); +} + +} // namespace + +ZoneProvision::ZoneProvision(const std::string& zonePath, + const std::vector& validLinkPrefixes) +{ + mProvisionFile = (fs::path(zonePath) / fs::path(ZONE_PROVISION_FILE)).string(); + mRootPath = (zonePath / fs::path("rootfs")).string(); + mValidLinkPrefixes = validLinkPrefixes; +} + +std::string ZoneProvision::getRootPath() const +{ + return mRootPath; +} + + +void ZoneProvision::declareFile(const int32_t& type, + const std::string& path, + const int32_t& flags, + const int32_t& mode) +{ + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::File({type, path, flags, mode})); + + declareUnit(mProvisionFile, std::move(unit)); +} + +void ZoneProvision::declareMount(const std::string& source, + const std::string& target, + const std::string& type, + const int64_t& flags, + const std::string& data) +{ + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::Mount({source, target, type, flags, data})); + + declareUnit(mProvisionFile, std::move(unit)); +} + +void ZoneProvision::declareLink(const std::string& source, + const std::string& target) +{ + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::Link({source, target})); + + declareUnit(mProvisionFile, std::move(unit)); +} + +void ZoneProvision::start() noexcept +{ + if (fs::exists(mProvisionFile)) { + config::loadFromFile(mProvisionFile, mProvisioningConfig); + for (const auto& unit : mProvisioningConfig.units) { + try { + if (unit.is()) { + file(unit.as()); + } else if (unit.is()) { + mount(unit.as()); + } else if (unit.is()) { + link(unit.as()); + } + } catch (std::exception& ex) { + LOGE("Provsion error: " << ex.what()); + } + } + } +} + +void ZoneProvision::stop() noexcept +{ + for (auto it = mProvisioningConfig.units.rbegin(); + it != mProvisioningConfig.units.rend(); + ++it) { + if (it->is()) { + umount(it->as()); + } + } +} + +void ZoneProvision::file(const ZoneProvisioning::File& config) +{ + bool ret = false; + const fs::path hostPath = fs::path(mRootPath) / fs::path(config.path); + switch (config.type) { + case VSMFILE_DIRECTORY: + ret = utils::createDirs(hostPath.string(), config.mode); + if (!ret) { + throw UtilsException("Can't create dir: " + hostPath.string()); + } + break; + + case VSMFILE_FIFO: + ret = utils::createFifo(hostPath.string(), config.mode); + if (!ret) { + throw UtilsException("Failed to make fifo: " + config.path); + } + break; + + case VSMFILE_REGULAR: + if ((config.flags & O_CREAT)) { + ret = utils::createFile(hostPath.string(), config.flags, config.mode); + if (!ret) { + throw UtilsException("Failed to create file: " + config.path); + } + } else { + ret = utils::copyFile(config.path, hostPath.string()); + if (!ret) { + throw UtilsException("Failed to copy file: " + config.path); + } + } + break; + } +} + +void ZoneProvision::mount(const ZoneProvisioning::Mount& config) +{ + const fs::path hostPath = fs::path(mRootPath) / fs::path(config.target); + bool ret = utils::mount(config.source, + hostPath.string(), + config.type, + config.flags, + config.data); + if (!ret) { + throw UtilsException("Mount operation failure - source : " + config.source); + } +} + +void ZoneProvision::umount(const ZoneProvisioning::Mount& config) +{ + const fs::path hostPath = fs::path(mRootPath) / fs::path(config.target); + utils::umount(hostPath.string()); +} + +void ZoneProvision::link(const ZoneProvisioning::Link& config) +{ + const std::string srcHostPath = fs::path(config.source).normalize().string(); + for (const std::string& prefix : mValidLinkPrefixes) { + if (prefix.length() <= srcHostPath.length() + && srcHostPath.compare(0, prefix.length(), prefix) == 0) { + + const fs::path destHostPath = fs::path(mRootPath) / fs::path(config.target); + bool ret = utils::createLink(srcHostPath, destHostPath.string()); + if (!ret) { + throw UtilsException("Failed to create hard link: " + config.source); + } + return; + } + } + LOGE("Failed to create hard link: path=host: " + << srcHostPath + << ", msg: Path prefix is not valid path"); + throw UtilsException("Failed to hard link: path prefix is not valid"); +} + +} // namespace vasum diff --git a/server/zone-provision.hpp b/server/zone-provision.hpp new file mode 100644 index 0000000..d7cc1e4 --- /dev/null +++ b/server/zone-provision.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Declaration of the class for managing zone provision + */ + + +#ifndef SERVER_ZONE_PROVISION_HPP +#define SERVER_ZONE_PROVISION_HPP + +#include "zone-provision-config.hpp" + +#include +#include + +namespace vasum { + + +/** + * Class is responsible for prepare filesystem for zone + * It allows to create directories, files, mount points and copying files from host + */ +class ZoneProvision { + +public: + /** + * ZoneProvision constructor + * @param zonesPath directory where zones are defined (lxc configs, rootfs etc) + */ + ZoneProvision(const std::string& zonePath, + const std::vector& validLinkPrefixes); + + /** + * Declare file, directory or pipe that will be created while zone startup + */ + void declareFile(const int32_t& type, + const std::string& path, + const int32_t& flags, + const int32_t& mode); + /** + * Declare mount that will be created while zone startup + */ + void declareMount(const std::string& source, + const std::string& target, + const std::string& type, + const int64_t& flags, + const std::string& data); + /** + * Declare link that will be created while zone startup + */ + void declareLink(const std::string& source, + const std::string& target); + + /** + * Get zone root path + */ + std::string getRootPath() const; + + void start() noexcept; + void stop() noexcept; + +private: + ZoneProvisioning mProvisioningConfig; + std::string mRootPath; + std::string mProvisionFile; + std::vector mValidLinkPrefixes; + + void mount(const ZoneProvisioning::Mount& config); + void umount(const ZoneProvisioning::Mount& config); + void file(const ZoneProvisioning::File& config); + void link(const ZoneProvisioning::Link& config); +}; + + +} // namespace vasum + +#endif // SERVER_ZONE_PROVISION_HPP diff --git a/server/zone.cpp b/server/zone.cpp index 199ecff..f6fb188 100644 --- a/server/zone.cpp +++ b/server/zone.cpp @@ -26,7 +26,6 @@ #include "zone.hpp" #include "base-exception.hpp" -#include "provisioning-config.hpp" #include "logger/logger.hpp" #include "utils/paths.hpp" @@ -38,7 +37,6 @@ #include #include - namespace vasum { namespace fs = boost::filesystem; @@ -50,18 +48,6 @@ typedef std::lock_guard Lock; // TODO: move constants to the config file when default values are implemented there const int RECONNECT_RETRIES = 15; const int RECONNECT_DELAY = 1 * 1000; -const std::string ZONE_PROVISION_FILE = "provision.conf"; - -void declareUnit(const std::string& file, ZoneProvisioning::Unit&& unit) -{ - // TODO: Add to the dynamic configuration - ZoneProvisioning config; - if (fs::exists(file)) { - config::loadFromFile(file, config); - } - config.units.push_back(std::move(unit)); - config::saveToFile(file, config); -} } // namespace @@ -86,8 +72,8 @@ Zone::Zone(const utils::Worker::Pointer& worker, } mAdmin.reset(new ZoneAdmin(zonesPath, lxcTemplatePrefix, mConfig)); - const fs::path baseProvision = fs::path(zonesPath) / mAdmin->getId(); - mProvisionConfig = fs::absolute(ZONE_PROVISION_FILE, baseProvision).string(); + const fs::path zonePath = fs::path(zonesPath) / mAdmin->getId(); + mProvision.reset(new ZoneProvision(zonePath.string(), mConfig.validLinkPrefixes)); } Zone::~Zone() @@ -123,6 +109,7 @@ int Zone::getPrivilege() const void Zone::start() { Lock lock(mReconnectMutex); + mProvision->start(); if (mConfig.enableDbusIntegration) { mConnectionTransport.reset(new ZoneConnectionTransport(mRunMountPoint)); } @@ -162,6 +149,7 @@ void Zone::stop() disconnect(); mAdmin->stop(); mConnectionTransport.reset(); + mProvision->stop(); } void Zone::connect() @@ -211,6 +199,12 @@ int Zone::getVT() const return mConfig.vt; } +std::string Zone::getRootPath() const +{ + return mProvision->getRootPath(); +} + + bool Zone::activateVT() { Lock lock(mReconnectMutex); @@ -399,35 +393,26 @@ void Zone::proxyCallAsync(const std::string& busName, } void Zone::declareFile(const int32_t& type, - const std::string& path, - const int32_t& flags, - const int32_t& mode) + const std::string& path, + const int32_t& flags, + const int32_t& mode) { - ZoneProvisioning::Unit unit; - unit.set(std::move(ZoneProvisioning::File({type, path, flags, mode}))); - - declareUnit(mProvisionConfig, std::move(unit)); + mProvision->declareFile(type, path, flags, mode); } void Zone::declareMount(const std::string& source, - const std::string& target, - const std::string& type, - const int64_t& flags, - const std::string& data) + const std::string& target, + const std::string& type, + const int64_t& flags, + const std::string& data) { - ZoneProvisioning::Unit unit; - unit.set(std::move(ZoneProvisioning::Mount({source, target, type, flags, data}))); - - declareUnit(mProvisionConfig, std::move(unit)); + mProvision->declareMount(source, target, type, flags, data); } void Zone::declareLink(const std::string& source, - const std::string& target) + const std::string& target) { - ZoneProvisioning::Unit unit; - unit.set(std::move(ZoneProvisioning::Link({source, target}))); - - declareUnit(mProvisionConfig, std::move(unit)); + mProvision->declareLink(source, target); } } // namespace vasum diff --git a/server/zone.hpp b/server/zone.hpp index fba9399..dd0daca 100644 --- a/server/zone.hpp +++ b/server/zone.hpp @@ -30,6 +30,7 @@ #include "zone-admin.hpp" #include "zone-connection.hpp" #include "zone-connection-transport.hpp" +#include "zone-provision.hpp" #include "utils/worker.hpp" #include @@ -254,6 +255,11 @@ public: void declareLink(const std::string& source, const std::string& target); + /** + * Get zone root path + */ + std::string getRootPath() const; + private: utils::Worker::Pointer mWorker; ZoneConfig mConfig; @@ -262,6 +268,7 @@ private: std::unique_ptr mConnectionTransport; std::unique_ptr mAdmin; std::unique_ptr mConnection; + std::unique_ptr mProvision; mutable std::recursive_mutex mReconnectMutex; NotifyActiveZoneCallback mNotifyCallback; DisplayOffCallback mDisplayOffCallback; @@ -270,7 +277,6 @@ private: DbusStateChangedCallback mDbusStateChangedCallback; std::string mDbusAddress; std::string mRunMountPoint; - std::string mProvisionConfig; void onNameLostCallback(); void reconnectHandler(); diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index eafbe4b..1f9bef0 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -628,15 +628,12 @@ void ZonesManager::handleGetZoneInfoCall(const std::string& id, result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone"); return; } - const auto zonePath = boost::filesystem::absolute(id, mConfig.zonesPath); - const auto rootfsDir = boost::filesystem::path("rootfs"); - const auto rootfsPath = zonePath / rootfsDir; result->set(g_variant_new("((siss))", id.c_str(), zone->getVT(), state, - rootfsPath.string().c_str())); + zone->getRootPath().c_str())); } void ZonesManager::handleDeclareFileCall(const std::string& zone, diff --git a/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in index 72fbdbf..46fb716 100644 --- a/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/zones/console1-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console1-dbus", "permittedToSend" : [ "/tmp/.*", "/etc/secret2" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in index 1386878..260d01f 100644 --- a/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/zones/console2-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console2-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ] + "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in b/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in index f91754f..e20ffe9 100644 --- a/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in +++ b/tests/unit_tests/client/configs/ut-client/zones/console3-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console3-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-server/zones/zone1.conf b/tests/unit_tests/server/configs/ut-server/zones/zone1.conf index 898eef2..2c2ccde 100644 --- a/tests/unit_tests/server/configs/ut-server/zones/zone1.conf +++ b/tests/unit_tests/server/configs/ut-server/zones/zone1.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-server/zones/zone2.conf b/tests/unit_tests/server/configs/ut-server/zones/zone2.conf index 8f4687a..1d500a7 100644 --- a/tests/unit_tests/server/configs/ut-server/zones/zone2.conf +++ b/tests/unit_tests/server/configs/ut-server/zones/zone2.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-server/zones/zone3.conf b/tests/unit_tests/server/configs/ut-server/zones/zone3.conf index fdb958f..a9856c7 100644 --- a/tests/unit_tests/server/configs/ut-server/zones/zone3.conf +++ b/tests/unit_tests/server/configs/ut-server/zones/zone3.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf index 320dc00..e96dca8 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/buggy.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf index 9aaf464..9a5ec68 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/missing.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf index b3b0141..be05697 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/test-no-shutdown.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf b/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf index ae046e5..45213d7 100644 --- a/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf +++ b/tests/unit_tests/server/configs/ut-zone-admin/zones/test.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf b/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf index 55586b4..eaf0811 100644 --- a/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf +++ b/tests/unit_tests/server/configs/ut-zone/zones/buggy.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in b/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in index 4f8e6db..e4a18fa 100644 --- a/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zone/zones/test-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zone-test-dbus", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zone/zones/test.conf b/tests/unit_tests/server/configs/ut-zone/zones/test.conf index a8f9692..4386c8c 100644 --- a/tests/unit_tests/server/configs/ut-zone/zones/test.conf +++ b/tests/unit_tests/server/configs/ut-zone/zones/test.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [], - "permittedToRecv" : [] + "permittedToRecv" : [], + "validLinkPrefixes" : [ "/tmp" ] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in index 15185d6..7c2f5e4 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/templates/template.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/~NAME~", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in index 72fbdbf..46fb716 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console1-dbus", "permittedToSend" : [ "/tmp/.*", "/etc/secret2" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf index 3e88c3d..41071ad 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console1.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in index 1386878..260d01f 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console2-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ] + "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf index 22f7a39..f152092 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console2.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in index f91754f..e20ffe9 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3-dbus.conf.in @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "/tmp/ut-run/ut-zones-manager-console3-dbus", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf index 2f99f23..4979dd2 100644 --- a/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf +++ b/tests/unit_tests/server/configs/ut-zones-manager/zones/console3.conf @@ -12,5 +12,6 @@ "cpuQuotaBackground" : 1000, "runMountPoint" : "", "permittedToSend" : [ "/tmp/.*" ], - "permittedToRecv" : [ "/tmp/.*" ] + "permittedToRecv" : [ "/tmp/.*" ], + "validLinkPrefixes" : [] } diff --git a/tests/unit_tests/server/ut-zone-provision.cpp b/tests/unit_tests/server/ut-zone-provision.cpp new file mode 100644 index 0000000..6a6e4ef --- /dev/null +++ b/tests/unit_tests/server/ut-zone-provision.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Mateusz Malicki (m.malicki2@samsung.com) + * @brief Unit tests of the ZoneProvsion class + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "zone.hpp" + +#include "utils/glib-loop.hpp" +#include "utils/scoped-dir.hpp" +#include "config/manager.hpp" +#include "zone-provision-config.hpp" +#include "vasum-client.h" + +#include +#include + +#include +#include +#include + +using namespace vasum; +using namespace config; + +namespace fs = boost::filesystem; + +namespace { + +const std::string PROVISON_CONFIG_FILE = "provision.conf"; +const std::string ZONE = "ut-zone-test"; +const fs::path TEST_CONFIG_PATH = VSM_TEST_CONFIG_INSTALL_DIR "/server/ut-zone/zones/test.conf"; +const fs::path ZONES_PATH = "/tmp/ut-zones"; +const fs::path LXC_TEMPLATES_PATH = VSM_TEST_LXC_TEMPLATES_INSTALL_DIR; +const fs::path ZONE_PATH = ZONES_PATH / fs::path(ZONE); +const fs::path PROVISION_FILE_PATH = ZONE_PATH / fs::path(PROVISON_CONFIG_FILE); +const fs::path ROOTFS_PATH = ZONE_PATH / fs::path("rootfs"); + +struct Fixture { + utils::ScopedGlibLoop mLoop; + utils::ScopedDir mZonesPathGuard; + utils::ScopedDir mRunGuard; + utils::ScopedDir mRootfsPath; + + Fixture() + : mZonesPathGuard(ZONES_PATH.string()) + , mRunGuard("/tmp/ut-run") + , mRootfsPath(ROOTFS_PATH.string()) + { + } + + std::unique_ptr create(const std::string& configPath) + { + return std::unique_ptr(new Zone(utils::Worker::create(), + ZONES_PATH.string(), + configPath, + LXC_TEMPLATES_PATH.string(), + "")); + } +}; + +} // namespace + + +BOOST_FIXTURE_TEST_SUITE(ZoneProvisionSuite, Fixture) + +BOOST_AUTO_TEST_CASE(FileTest) +{ + //TODO: Test Fifo + const fs::path regularFile = fs::path("/opt/usr/data/ut-regular-file"); + const fs::path copyFile = PROVISION_FILE_PATH; + + ZoneProvisioning config; + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::File({VSMFILE_DIRECTORY, + regularFile.parent_path().string(), + 0, + 0777})); + config.units.push_back(unit); + + unit.set(ZoneProvisioning::File({VSMFILE_REGULAR, + regularFile.string(), + O_CREAT, + 0777})); + config.units.push_back(unit); + + unit.set(ZoneProvisioning::File({VSMFILE_DIRECTORY, + copyFile.parent_path().string(), + 0, + 0777})); + config.units.push_back(unit); + unit.set(ZoneProvisioning::File({VSMFILE_REGULAR, + copyFile.string(), + 0, + 0777})); + config.units.push_back(unit); + config::saveToFile(PROVISION_FILE_PATH.string(), config); + auto c = create(TEST_CONFIG_PATH.string()); + c->start(); + + BOOST_CHECK(fs::exists(ROOTFS_PATH / regularFile.parent_path())); + BOOST_CHECK(fs::exists(ROOTFS_PATH / regularFile)); + BOOST_CHECK(fs::exists(ROOTFS_PATH / copyFile.parent_path())); + BOOST_CHECK(fs::exists(ROOTFS_PATH / copyFile)); + + c->stop(); +} + +BOOST_AUTO_TEST_CASE(MountTest) +{ + //TODO: Test Fifo + const fs::path mountTarget = fs::path("/opt/usr/data/ut-from-host-provision"); + const fs::path mountSource = fs::path("/tmp/ut-provision"); + const fs::path sharedFile = fs::path("ut-regular-file"); + + utils::ScopedDir provisionfs(mountSource.string()); + + + ZoneProvisioning config; + ZoneProvisioning::Unit unit; + unit.set(ZoneProvisioning::File({VSMFILE_DIRECTORY, + mountTarget.string(), + 0, + 0777})); + config.units.push_back(unit); + unit.set(ZoneProvisioning::Mount({mountSource.string(), + mountTarget.string(), + "", + MS_BIND, + ""})); + config.units.push_back(unit); + unit.set(ZoneProvisioning::File({VSMFILE_REGULAR, + (mountTarget / sharedFile).string(), + O_CREAT, + 0777})); + config.units.push_back(unit); + + config::saveToFile(PROVISION_FILE_PATH.string(), config); + auto c = create(TEST_CONFIG_PATH.string()); + c->start(); + + BOOST_CHECK(fs::exists(ROOTFS_PATH / mountTarget)); + BOOST_CHECK(fs::exists(ROOTFS_PATH / mountTarget / sharedFile)); + BOOST_CHECK(fs::exists(mountSource / sharedFile)); + + c->stop(); +} + +BOOST_AUTO_TEST_CASE(LinkTest) +{ + const fs::path linkFile = fs::path("/ut-from-host-provision.conf"); + + ZoneProvisioning config; + ZoneProvisioning::Unit unit; + + unit.set(ZoneProvisioning::Link({PROVISION_FILE_PATH.string(), + linkFile.string()})); + config.units.push_back(unit); + config::saveToFile(PROVISION_FILE_PATH.string(), config); + auto c = create(TEST_CONFIG_PATH.string()); + c->start(); + + BOOST_CHECK(fs::exists(ROOTFS_PATH / linkFile)); + + c->stop(); +} + +BOOST_AUTO_TEST_CASE(DeclareFile) +{ + ZoneProvision zoneProvision(ZONE_PATH.string(), {}); + zoneProvision.declareFile(1, "path", 0747, 0777); + zoneProvision.declareFile(2, "path", 0747, 0777); + + ZoneProvisioning config; + BOOST_REQUIRE_NO_THROW(loadFromFile(PROVISION_FILE_PATH.string(), config)); + BOOST_REQUIRE_EQUAL(config.units.size(), 2); + BOOST_REQUIRE(config.units[0].is()); + BOOST_REQUIRE(config.units[1].is()); + const ZoneProvisioning::File& unit = config.units[0].as(); + BOOST_CHECK_EQUAL(unit.type, 1); + BOOST_CHECK_EQUAL(unit.path, "path"); + BOOST_CHECK_EQUAL(unit.flags, 0747); + BOOST_CHECK_EQUAL(unit.mode, 0777); +} + +BOOST_AUTO_TEST_CASE(DeclareMount) +{ + ZoneProvision zoneProvision(ZONE_PATH.string(), {}); + zoneProvision.declareMount("/fake/path1", "/fake/path2", "tmpfs", 077, "fake"); + zoneProvision.declareMount("/fake/path2", "/fake/path2", "tmpfs", 077, "fake"); + + ZoneProvisioning config; + BOOST_REQUIRE_NO_THROW(loadFromFile(PROVISION_FILE_PATH.string(), config)); + BOOST_REQUIRE_EQUAL(config.units.size(), 2); + BOOST_REQUIRE(config.units[0].is()); + BOOST_REQUIRE(config.units[1].is()); + const ZoneProvisioning::Mount& unit = config.units[0].as(); + BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); + BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); + BOOST_CHECK_EQUAL(unit.type, "tmpfs"); + BOOST_CHECK_EQUAL(unit.flags, 077); + BOOST_CHECK_EQUAL(unit.data, "fake"); +} + +BOOST_AUTO_TEST_CASE(DeclareLink) +{ + ZoneProvision zoneProvision(ZONE_PATH.string(), {}); + zoneProvision.declareLink("/fake/path1", "/fake/path2"); + zoneProvision.declareLink("/fake/path2", "/fake/path2"); + + ZoneProvisioning config; + BOOST_REQUIRE_NO_THROW(loadFromFile(PROVISION_FILE_PATH.string(), config)); + BOOST_REQUIRE_EQUAL(config.units.size(), 2); + BOOST_REQUIRE(config.units[0].is()); + BOOST_REQUIRE(config.units[1].is()); + const ZoneProvisioning::Link& unit = config.units[0].as(); + BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); + BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 4db9434..3c62478 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -27,9 +27,6 @@ #include "ut.hpp" #include "zones-manager.hpp" -#include "zones-manager-config.hpp" -#include "zone-config.hpp" -#include "provisioning-config.hpp" #include "zone-dbus-definitions.hpp" #include "host-dbus-definitions.hpp" #include "test-dbus-definitions.hpp" @@ -40,7 +37,6 @@ #include "dbus/connection.hpp" #include "dbus/exception.hpp" #include "utils/glib-loop.hpp" -#include "config/manager.hpp" #include "config/exception.hpp" #include "utils/latch.hpp" #include "utils/fs.hpp" @@ -82,7 +78,6 @@ const std::string FILE_CONTENT = "File content\n" "Line 2\n"; const std::string NON_EXISTANT_ZONE_ID = "NON_EXISTANT_ZONE_ID"; const std::string ZONES_PATH = "/tmp/ut-zones"; // the same as in daemon.conf -const std::string PROVISON_CONFIG_FILE = "provision.conf"; class DbusAccessory { public: @@ -314,67 +309,6 @@ public: } - void callMethodDeclareFile(const std::string& zone, - const int32_t& type, - const std::string& path, - const int32_t& flags, - const int32_t& mode) - { - assert(isHost()); - GVariant* parameters = g_variant_new("(sisii)", - zone.c_str(), - type, - path.c_str(), - flags, - mode); - GVariantPtr result = mClient->callMethod(api::host::BUS_NAME, - api::host::OBJECT_PATH, - api::host::INTERFACE, - api::host::METHOD_DECLARE_FILE, - parameters, - "()"); - } - - void callMethodDeclareMount(const std::string& source, - const std::string& zone, - const std::string& target, - const std::string& type, - const uint64_t& flags, - const std::string& data) - { - assert(isHost()); - GVariant* parameters = g_variant_new("(ssssts)", - source.c_str(), - zone.c_str(), - target.c_str(), - type.c_str(), - flags, - data.c_str()); - GVariantPtr result = mClient->callMethod(api::host::BUS_NAME, - api::host::OBJECT_PATH, - api::host::INTERFACE, - api::host::METHOD_DECLARE_MOUNT, - parameters, - "()"); - } - - void callMethodDeclareLink(const std::string& source, - const std::string& zone, - const std::string& target) - { - assert(isHost()); - GVariant* parameters = g_variant_new("(sss)", - source.c_str(), - zone.c_str(), - target.c_str()); - GVariantPtr result = mClient->callMethod(api::host::BUS_NAME, - api::host::OBJECT_PATH, - api::host::INTERFACE, - api::host::METHOD_DECLARE_LINK, - parameters, - "()"); - } - void callAsyncMethodCreateZone(const std::string& id, const VoidResultCallback& result) { @@ -477,26 +411,6 @@ struct Fixture { {} }; -std::string getProvisionConfigPath(const std::string& zone) -{ - namespace fs = boost::filesystem; - ZonesManagerConfig managerConfig; - loadFromFile(TEST_CONFIG_PATH, managerConfig); - for (const auto& zonesPath : managerConfig.zoneConfigs) { - ZoneConfig zoneConfig; - const fs::path configConfigPath = fs::absolute(zonesPath, - fs::path(TEST_CONFIG_PATH).parent_path()); - - loadFromFile(configConfigPath.string(), zoneConfig); - if (zoneConfig.name == zone) { - const fs::path base = fs::path(managerConfig.zonesPath) / fs::path(zone); - return fs::absolute(PROVISON_CONFIG_FILE, base).string(); - } - } - BOOST_FAIL("There is no provision config file for " + zone); - return std::string(); -} - } // namespace @@ -1093,71 +1007,6 @@ BOOST_AUTO_TEST_CASE(CreateDestroyZoneTest) BOOST_CHECK_EQUAL(cm.getRunningForegroundZoneId(), ""); } -BOOST_AUTO_TEST_CASE(DeclareFile) -{ - const std::string zone = EXPECTED_DBUSES_NO_DBUS.begin()->first; - const std::string provisionConfigPath = getProvisionConfigPath(zone); - - ZonesManager cm(TEST_CONFIG_PATH); - DbusAccessory dbus(DbusAccessory::HOST_ID); - dbus.callMethodDeclareFile(zone, 1, "path", 0747, 0777); - dbus.callMethodDeclareFile(zone, 2, "path", 0747, 0777); - - ZoneProvisioning config; - BOOST_REQUIRE_NO_THROW(loadFromFile(provisionConfigPath, config)); - BOOST_REQUIRE_EQUAL(config.units.size(), 2); - BOOST_REQUIRE(config.units[0].is()); - BOOST_REQUIRE(config.units[1].is()); - const ZoneProvisioning::File& unit = config.units[0].as(); - BOOST_CHECK_EQUAL(unit.type, 1); - BOOST_CHECK_EQUAL(unit.path, "path"); - BOOST_CHECK_EQUAL(unit.flags, 0747); - BOOST_CHECK_EQUAL(unit.mode, 0777); -} - -BOOST_AUTO_TEST_CASE(DeclareMount) -{ - const std::string zone = EXPECTED_DBUSES_NO_DBUS.begin()->first; - const std::string provisionConfigPath = getProvisionConfigPath(zone); - - ZonesManager cm(TEST_CONFIG_PATH); - DbusAccessory dbus(DbusAccessory::HOST_ID); - dbus.callMethodDeclareMount("/fake/path1", zone, "/fake/path2", "tmpfs", 077, "fake"); - dbus.callMethodDeclareMount("/fake/path2", zone, "/fake/path2", "tmpfs", 077, "fake"); - - ZoneProvisioning config; - BOOST_REQUIRE_NO_THROW(loadFromFile(provisionConfigPath, config)); - BOOST_REQUIRE_EQUAL(config.units.size(), 2); - BOOST_REQUIRE(config.units[0].is()); - BOOST_REQUIRE(config.units[1].is()); - const ZoneProvisioning::Mount& unit = config.units[0].as(); - BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); - BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); - BOOST_CHECK_EQUAL(unit.type, "tmpfs"); - BOOST_CHECK_EQUAL(unit.flags, 077); - BOOST_CHECK_EQUAL(unit.data, "fake"); -} - -BOOST_AUTO_TEST_CASE(DeclareLink) -{ - const std::string zone = EXPECTED_DBUSES_NO_DBUS.begin()->first; - const std::string provisionConfigPath = getProvisionConfigPath(zone); - - ZonesManager cm(TEST_CONFIG_PATH); - DbusAccessory dbus(DbusAccessory::HOST_ID); - dbus.callMethodDeclareLink("/fake/path1", zone, "/fake/path2"); - dbus.callMethodDeclareLink("/fake/path2", zone, "/fake/path2"); - - ZoneProvisioning config; - BOOST_REQUIRE_NO_THROW(loadFromFile(provisionConfigPath, config)); - BOOST_REQUIRE_EQUAL(config.units.size(), 2); - BOOST_REQUIRE(config.units[0].is()); - BOOST_REQUIRE(config.units[1].is()); - const ZoneProvisioning::Link& unit = config.units[0].as(); - BOOST_CHECK_EQUAL(unit.source, "/fake/path1"); - BOOST_CHECK_EQUAL(unit.target, "/fake/path2"); -} - BOOST_AUTO_TEST_CASE(LockUnlockZoneTest) { ZonesManager cm(TEST_DBUS_CONFIG_PATH); diff --git a/zone-daemon/CMakeLists.txt b/zone-daemon/CMakeLists.txt index 3fd611f..50bfa1a 100644 --- a/zone-daemon/CMakeLists.txt +++ b/zone-daemon/CMakeLists.txt @@ -22,7 +22,8 @@ MESSAGE(STATUS "Generating makefile for the Zone Daemon...") FILE(GLOB project_SRCS *.cpp *.hpp) FILE(GLOB common_SRCS ${COMMON_FOLDER}/dbus/*.cpp ${COMMON_FOLDER}/dbus/*.hpp ${COMMON_FOLDER}/log/*.cpp ${COMMON_FOLDER}/log/*.hpp - ${COMMON_FOLDER}/utils/*.cpp ${COMMON_FOLDER}/utils/*.hpp) + ${COMMON_FOLDER}/utils/*.cpp ${COMMON_FOLDER}/utils/*.hpp + ${COMMON_FOLDER}/*.cpp) ## Setup target ################################################################ SET(ZONE_DAEMON_CODENAME "${PROJECT_NAME}-zone-daemon") -- 2.7.4 From 719daa453260d4cbcad85d0e6dbfd2434b779625 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Sat, 6 Dec 2014 13:05:46 +0100 Subject: [PATCH 05/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 06/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 07/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 08/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 09/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 10/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 11/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 12/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 13/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 14/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 15/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 16/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