2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Jan Olszak <j.olszak@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Jan Olszak (j.olszak@samsung.com)
22 * @brief Definition of the class for managing zones
27 #include "common-definitions.hpp"
28 #include "dynamic-config-scheme.hpp"
29 #include "zones-manager.hpp"
30 #include "lxc/cgroup.hpp"
31 #include "exception.hpp"
33 #include "utils/paths.hpp"
34 #include "logger/logger.hpp"
35 #include "config/manager.hpp"
36 #include "dbus/exception.hpp"
37 #include "utils/fs.hpp"
38 #include "utils/img.hpp"
39 #include "utils/environment.hpp"
40 #include "utils/vt.hpp"
41 #include "api/messages.hpp"
43 #include <boost/filesystem.hpp>
44 #include <boost/regex.hpp>
45 #include <boost/exception/diagnostic_information.hpp>
59 const std::string HOST_ID = "host";
60 const std::string ENABLED_FILE_NAME = "enabled";
62 const boost::regex ZONE_NAME_REGEX("~NAME~");
63 const boost::regex ZONE_IP_THIRD_OCTET_REGEX("~IP~");
65 const unsigned int ZONE_IP_BASE_THIRD_OCTET = 100;
67 const std::vector<std::string> prohibitedZonesNames{
73 void remove(std::vector<T>& v, const T& item)
75 // erase-remove idiom, ask google for explanation
76 v.erase(std::remove(v.begin(), v.end(), item), v.end());
79 template<typename Iter, typename Predicate>
80 Iter circularFindNext(Iter begin, Iter end, Iter current, Predicate pred)
82 if (begin == end || current == end) {
85 for (Iter next = current;;) {
90 if (next == current) {
99 Zone& get(std::vector<std::unique_ptr<Zone>>::iterator iter)
104 bool zoneIsRunning(const std::unique_ptr<Zone>& zone) {
105 return zone->isRunning();
108 bool isalnum(const std::string& str)
110 for (const auto& c : str) {
111 if (!std::isalnum(c)) {
118 void cleanUpUnknownsFromRoot(const boost::filesystem::path& zonesPath,
119 const std::vector<std::string>& zoneIds,
122 namespace fs = boost::filesystem;
123 const auto end = fs::directory_iterator();
125 std::set<std::string> knowns(zoneIds.begin(), zoneIds.end());
126 knowns.insert(prohibitedZonesNames.begin(), prohibitedZonesNames.end());
128 // Remove all directories that start with '.'
129 for (auto zoneDir = fs::directory_iterator(zonesPath); zoneDir != end; ++zoneDir) {
130 if (zoneDir->path().filename().string()[0] == '.') {
132 fs::remove_all(zoneDir->path());
133 LOGI("Remove directory entry: " << *zoneDir);
135 LOGI("Remove directory entry (dry run): " << *zoneDir);
140 for (auto zoneDir = fs::directory_iterator(zonesPath); zoneDir != end; ++zoneDir) {
141 const auto zoneIt = knowns.find(zoneDir->path().filename().string());
142 if (zoneIt == knowns.end()) {
144 const std::string filename = '.' + zoneDir->path().filename().string();
145 fs::path newName = zoneDir->path().parent_path() / filename;
147 fs::rename(zoneDir->path(), newName);
148 fs::remove_all(newName);
149 LOGI("Remove directory entry: " << *zoneDir);
151 LOGI("Remove directory entry (dry run): " << *zoneDir);
160 ZonesManager::ZonesManager(ipc::epoll::EventPoll& eventPoll, const std::string& configPath)
162 , mWorker(utils::Worker::create())
163 , mDetachOnExit(false)
164 , mExclusiveIDLock(INVALID_CONNECTION_ID)
165 , mHostIPCConnection(eventPoll, this)
166 #ifdef DBUS_CONNECTION
167 , mHostDbusConnection(this)
170 LOGD("Instantiating ZonesManager object...");
172 config::loadFromJsonFile(configPath, mConfig);
173 config::loadFromKVStoreWithJsonFile(mConfig.dbPath,
178 if (mConfig.inputConfig.enabled) {
179 LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]");
180 mSwitchingSequenceMonitor.reset(new InputMonitor(eventPoll, mConfig.inputConfig, this));
184 ZonesManager::~ZonesManager()
186 LOGD("Destroying ZonesManager object...");
190 void ZonesManager::start()
194 LOGD("Starting ZonesManager");
198 cleanUpUnknownsFromRoot(mConfig.zonesPath, mDynamicConfig.zoneIds, !mConfig.cleanUpZonesPath);
200 #ifdef DBUS_CONNECTION
201 using namespace std::placeholders;
202 mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
203 mHostDbusConnection.setProxyCallCallback(std::bind(&ZonesManager::handleProxyCall,
204 this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
205 #endif //DBUS_CONNECTION
207 for (const auto& zoneId : mDynamicConfig.zoneIds) {
208 insertZone(zoneId, getTemplatePathForExistingZone(zoneId));
213 LOGD("ZonesManager object instantiated");
215 if (mConfig.inputConfig.enabled) {
216 LOGI("Starting input monitor ");
217 mSwitchingSequenceMonitor->start();
220 // After everything's initialized start to respond to clients' requests
221 mHostIPCConnection.start();
224 void ZonesManager::stop(bool wait)
227 LOGD("Stopping ZonesManager");
233 if (!mDetachOnExit) {
236 } catch (ServerException&) {
237 LOGE("Failed to shutdown all of the zones");
241 // wait for all tasks to complete
243 mHostIPCConnection.stop(wait);
244 if (mConfig.inputConfig.enabled) {
245 LOGI("Stopping input monitor ");
246 mSwitchingSequenceMonitor->stop();
251 bool ZonesManager::isRunning()
254 return mIsRunning || mHostIPCConnection.isRunning();
257 ZonesManager::Zones::iterator ZonesManager::findZone(const std::string& id)
259 return std::find_if(mZones.begin(), mZones.end(), [&id](const std::unique_ptr<Zone>& zone) {
260 return zone->getId() == id;
264 Zone& ZonesManager::getZone(const std::string& id)
266 auto iter = findZone(id);
267 if (iter == mZones.end()) {
268 throw InvalidZoneIdException("Zone id not found");
273 void ZonesManager::saveDynamicConfig()
275 config::saveToKVStore(mConfig.dbPath, mDynamicConfig, getVasumDbPrefix());
278 void ZonesManager::updateDefaultId()
280 // TODO add an api to change defaultId
281 if (mZones.empty() && mDynamicConfig.defaultId.empty()) {
282 LOGT("Keep empty defaultId");
285 if (findZone(mDynamicConfig.defaultId) != mZones.end()) {
286 LOGT("Keep " << mDynamicConfig.defaultId << " as defaultId");
291 if (mZones.empty()) {
292 mDynamicConfig.defaultId.clear();
293 LOGD("DefaultId cleared");
295 mDynamicConfig.defaultId = mZones.front()->getId();
296 LOGD("DefaultId changed to " << mDynamicConfig.defaultId);
301 std::string ZonesManager::getTemplatePathForExistingZone(const std::string& id)
303 ZoneTemplatePathConfig config;
304 config::loadFromKVStore(mConfig.dbPath, config, getZoneDbPrefix(id));
305 return config.zoneTemplatePath;
308 void ZonesManager::insertZone(const std::string& zoneId, const std::string& zoneTemplatePath)
310 if (zoneId == HOST_ID) {
311 throw InvalidZoneIdException("Cannot use reserved zone ID");
313 if (findZone(zoneId) != mZones.end()) {
314 throw InvalidZoneIdException("Zone already exists");
317 LOGT("Creating Zone " << zoneId);
318 std::unique_ptr<Zone> zone(new Zone(zoneId,
322 mConfig.zoneTemplateDir,
323 mConfig.runMountPointPrefix));
325 mZones.push_back(std::move(zone));
327 // after zone is created successfully, put a file informing that zones are enabled
328 if (mZones.size() == 1) {
329 if (!utils::saveFileContent(
330 utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME), "")) {
331 throw ZoneOperationException(ENABLED_FILE_NAME + ": cannot create.");
336 void ZonesManager::tryAddTask(const utils::Worker::Task& task, api::MethodResultBuilder::Pointer result, bool wait)
339 Lock lock(mExclusiveIDMutex);
341 if (mExclusiveIDLock != INVALID_CONNECTION_ID &&
342 mExclusiveIDLock != result->getID()) {
343 result->setError(api::ERROR_QUEUE, "Queue is locked by another client");
349 mWorker->addTaskAndWait(task);
351 mWorker->addTask(task);
355 void ZonesManager::destroyZone(const std::string& zoneId)
359 auto iter = findZone(zoneId);
360 if (iter == mZones.end()) {
361 const std::string msg = "Failed to destroy zone " + zoneId + ": no such zone";
363 throw InvalidZoneIdException(msg);
366 get(iter).setDestroyOnExit();
369 if (mZones.empty()) {
370 if (!utils::removeFile(utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME))) {
371 LOGE("Failed to remove enabled file.");
375 // update dynamic config
376 remove(mDynamicConfig.zoneIds, zoneId);
383 void ZonesManager::focus(const std::string& zoneId)
386 auto iter = findZone(zoneId);
390 void ZonesManager::focusInternal(Zones::iterator iter)
392 // assume mutex is locked
393 if (iter == mZones.end()) {
394 if (!mActiveZoneId.empty()) {
395 LOGI("Focus to: host");
396 utils::activateVT(mConfig.hostVT);
397 mActiveZoneId.clear();
402 Zone& zoneToFocus = get(iter);
403 const std::string& idToFocus = zoneToFocus.getId();
405 if (idToFocus == mActiveZoneId) {
409 if (!zoneToFocus.isRunning()) {
410 LOGE("Can't focus not running zone " << idToFocus);
415 LOGI("Focus to: " << idToFocus);
417 if (!zoneToFocus.activateVT()) {
418 LOGE("Failed to activate zones VT");
422 for (auto& zone : mZones) {
423 if (zone->isRunning()) {
424 std::string id = zone->getId();
425 if (id == idToFocus) {
426 LOGD(id << ": being sent to foreground");
427 zone->goForeground();
429 LOGD(id << ": being sent to background");
430 zone->goBackground();
434 mActiveZoneId = idToFocus;
437 void ZonesManager::refocus()
439 // assume mutex is locked
441 // check if refocus is required
442 auto oldIter = findZone(mActiveZoneId);
443 if (oldIter != mZones.end() && get(oldIter).isRunning()) {
447 // try to refocus to defaultId
448 auto iter = findZone(mDynamicConfig.defaultId);
449 if (iter == mZones.end() || !get(iter).isRunning()) {
450 // focus to any running or to host if not found
451 iter = std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
456 void ZonesManager::restoreAll()
458 LOGI("Restoring all zones");
462 for (auto& zone : mZones) {
469 void ZonesManager::shutdownAll()
471 LOGI("Stopping all zones");
475 for (auto& zone : mZones) {
482 bool ZonesManager::isPaused(const std::string& zoneId)
485 return getZone(zoneId).isPaused();
488 bool ZonesManager::isRunning(const std::string& zoneId)
491 return getZone(zoneId).isRunning();
494 bool ZonesManager::isStopped(const std::string& zoneId)
497 return getZone(zoneId).isStopped();
500 std::string ZonesManager::getRunningForegroundZoneId()
503 auto iter = getRunningForegroundZoneIterator();
504 return iter == mZones.end() ? std::string() : get(iter).getId();
507 std::string ZonesManager::getNextToForegroundZoneId()
510 auto iter = getNextToForegroundZoneIterator();
511 return iter == mZones.end() ? std::string() : get(iter).getId();
514 ZonesManager::Zones::iterator ZonesManager::getRunningForegroundZoneIterator()
516 // assume mutex is locked
517 if (mActiveZoneId.empty()) {
520 auto iter = findZone(mActiveZoneId);
521 if (!get(iter).isRunning()) {
522 LOGW("Active zone " << mActiveZoneId << " is not running any more!");
528 ZonesManager::Zones::iterator ZonesManager::getNextToForegroundZoneIterator()
530 // assume mutex is locked
531 auto current = findZone(mActiveZoneId);
532 if (current == mZones.end()) {
534 return std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
537 return circularFindNext(mZones.begin(), mZones.end(), current, zoneIsRunning);
541 void ZonesManager::switchingSequenceMonitorNotify()
543 LOGI("switchingSequenceMonitorNotify() called");
547 auto next = getNextToForegroundZoneIterator();
549 if (next != mZones.end()) {
555 void ZonesManager::setZonesDetachOnExit()
559 mDetachOnExit = true;
561 for (auto& zone : mZones) {
562 zone->setDetachOnExit();
566 void ZonesManager::disconnectedCallback(const std::string& id)
568 LOGD("Client Disconnected: " << id);
571 Lock lock(mExclusiveIDMutex);
573 if (mExclusiveIDLock == id) {
574 mExclusiveIDLock = INVALID_CONNECTION_ID;
579 void ZonesManager::handleSwitchToDefaultCall(const std::string& /*caller*/,
580 api::MethodResultBuilder::Pointer result)
582 auto handler = [&, this] {
583 // get config of currently set zone and switch if switchToDefaultAfterTimeout is true
586 auto activeIter = findZone(mActiveZoneId);
587 auto defaultIter = findZone(mDynamicConfig.defaultId);
589 if (activeIter != mZones.end() &&
590 defaultIter != mZones.end() &&
591 get(activeIter).isSwitchToDefaultAfterTimeoutAllowed() &&
592 get(defaultIter).isRunning()) {
594 LOGI("Switching to default zone " << mDynamicConfig.defaultId);
595 focusInternal(defaultIter);
600 tryAddTask(handler, result, true);
603 void ZonesManager::handleCreateFileCall(const api::CreateFileIn& request,
604 api::MethodResultBuilder::Pointer result)
606 auto handler = [&, this] {
607 LOGI("CreateFile call");
611 auto srcIter = findZone(request.id);
612 if (srcIter == mZones.end()) {
613 LOGE("Zone '" << request.id << "' not found");
614 result->setError(api::ERROR_INVALID_ID, "Requested Zone was not found.");
617 Zone& srcZone = get(srcIter);
619 auto retValue = std::make_shared<api::CreateFileOut>();
621 retValue->fd = srcZone.createFile(request.path, request.flags, request.mode);
622 } catch(ZoneOperationException& e) {
623 result->setError(api::ERROR_CREATE_FILE_FAILED, "Unable to create file");
627 result->set(retValue);
630 tryAddTask(handler, result, true);
633 #ifdef DBUS_CONNECTION
634 void ZonesManager::handleProxyCall(const std::string& caller,
635 const std::string& target,
636 const std::string& targetBusName,
637 const std::string& targetObjectPath,
638 const std::string& targetInterface,
639 const std::string& targetMethod,
640 GVariant* parameters,
641 dbus::MethodResultBuilder::Pointer result)
643 auto handler = [&, this] {
644 if (!mProxyCallPolicy->isProxyCallAllowed(caller,
650 LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
651 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
652 result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
656 LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
657 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
659 auto asyncResultCallback = [result](dbus::AsyncMethodCallResult & asyncMethodCallResult) {
661 GVariant* targetResult = asyncMethodCallResult.get();
662 result->set(g_variant_new("(v)", targetResult));
663 } catch (dbus::DbusException& e) {
664 result->setError(api::ERROR_FORWARDED, e.what());
668 if (target != HOST_ID) {
669 result->setError(api::ERROR_INVALID_ID, "Unknown proxy call target");
673 mHostDbusConnection.proxyCallAsync(targetBusName,
678 asyncResultCallback);
681 // This call cannot be locked by lock/unlock queue
682 mWorker->addTaskAndWait(handler);
684 #endif //DBUS_CONNECTION
686 void ZonesManager::handleLockQueueCall(api::MethodResultBuilder::Pointer result)
688 Lock lock(mExclusiveIDMutex);
689 std::string id = result->getID();
691 LOGI("Lock Queue: " << id);
693 if (mExclusiveIDLock == id) {
694 result->setError(api::ERROR_QUEUE, "Queue already locked");
698 if (mExclusiveIDLock != INVALID_CONNECTION_ID) {
699 result->setError(api::ERROR_QUEUE, "Queue locked by another connection");
703 mExclusiveIDLock = id;
707 void ZonesManager::handleUnlockQueueCall(api::MethodResultBuilder::Pointer result)
709 Lock lock(mExclusiveIDMutex);
710 std::string id = result->getID();
712 LOGI("Unlock Queue: " << id);
714 if (mExclusiveIDLock == INVALID_CONNECTION_ID) {
715 result->setError(api::ERROR_QUEUE, "Queue not locked");
719 if (mExclusiveIDLock != id) {
720 result->setError(api::ERROR_QUEUE, "Queue locked by another connection");
724 mExclusiveIDLock = INVALID_CONNECTION_ID;
728 void ZonesManager::handleGetZoneIdsCall(api::MethodResultBuilder::Pointer result)
730 auto handler = [&, this] {
731 LOGI("GetZoneIds call");
735 auto zoneIds = std::make_shared<api::ZoneIds>();
736 for (const auto& zone : mZones) {
737 zoneIds->values.push_back(zone->getId());
740 result->set(zoneIds);
743 // This call cannot be locked by lock/unlock queue
744 mWorker->addTaskAndWait(handler);
747 void ZonesManager::handleGetActiveZoneIdCall(api::MethodResultBuilder::Pointer result)
749 auto handler = [&, this] {
750 LOGI("GetActiveZoneId call");
752 auto zoneId = std::make_shared<api::ZoneId>();
753 zoneId->value = getRunningForegroundZoneId();
757 // This call cannot be locked by lock/unlock queue
758 mWorker->addTaskAndWait(handler);
761 void ZonesManager::handleGetZoneInfoCall(const api::ZoneId& zoneId,
762 api::MethodResultBuilder::Pointer result)
764 auto handler = [&, this] {
765 LOGI("GetZoneInfo call");
769 auto iter = findZone(zoneId.value);
770 if (iter == mZones.end()) {
771 LOGE("No zone with id=" << zoneId.value);
772 result->setError(api::ERROR_INVALID_ID, "No such zone id");
776 Zone& zone = get(iter);
777 auto zoneInfo = std::make_shared<api::ZoneInfoOut>();
779 if (zone.isRunning()) {
780 zoneInfo->state = "RUNNING";
781 } else if (zone.isStopped()) {
782 zoneInfo->state = "STOPPED";
783 } else if (zone.isPaused()) {
784 zoneInfo->state = "FROZEN";
786 LOGE("Unrecognized state of zone id=" << zoneId.value);
787 result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone");
791 zoneInfo->id = zone.getId();
792 zoneInfo->vt = zone.getVT();
793 zoneInfo->rootPath = zone.getRootPath();
794 result->set(zoneInfo);
797 // This call cannot be locked by lock/unlock queue
798 mWorker->addTaskAndWait(handler);
801 void ZonesManager::handleSetNetdevAttrsCall(const api::SetNetDevAttrsIn& data,
802 api::MethodResultBuilder::Pointer result)
804 auto handler = [&, this] {
805 LOGI("SetNetdevAttrs call");
810 // TODO: Use vector<StringPair> instead of tuples
811 std::vector<std::tuple<std::string, std::string>> attrsAsTuples;
812 for(const auto& entry: data.attrs){
813 attrsAsTuples.push_back(std::make_tuple(entry.first, entry.second));
816 getZone(data.id).setNetdevAttrs(data.netDev, attrsAsTuples);
818 } catch (const InvalidZoneIdException&) {
819 LOGE("No zone with id=" << data.id);
820 result->setError(api::ERROR_INVALID_ID, "No such zone id");
821 } catch (const std::runtime_error& ex) {
822 LOGE("Can't set attributes: " << ex.what());
823 result->setError(api::ERROR_INTERNAL, ex.what());
827 tryAddTask(handler, result, true);
830 void ZonesManager::handleGetNetdevAttrsCall(const api::GetNetDevAttrsIn& data,
831 api::MethodResultBuilder::Pointer result)
833 auto handler = [&, this] {
834 LOGI("GetNetdevAttrs call");
838 auto netDevAttrs = std::make_shared<api::GetNetDevAttrs>();
839 const auto attrs = getZone(data.first).getNetdevAttrs(data.second);
841 for (size_t i = 0; i < attrs.size(); ++i) {
842 netDevAttrs->values.push_back({std::get<0>(attrs[i]), std::get<1>(attrs[i])});
844 result->set(netDevAttrs);
845 } catch (const InvalidZoneIdException&) {
846 LOGE("No zone with id=" << data.first);
847 result->setError(api::ERROR_INVALID_ID, "No such zone id");
848 } catch (const std::runtime_error& ex) {
849 LOGE("Can't set attributes: " << ex.what());
850 result->setError(api::ERROR_INTERNAL, ex.what());
854 tryAddTask(handler, result, true);
857 void ZonesManager::handleGetNetdevListCall(const api::ZoneId& zoneId,
858 api::MethodResultBuilder::Pointer result)
860 auto handler = [&, this] {
861 LOGI("GetNetdevList call");
865 auto netDevList = std::make_shared<api::NetDevList>();
866 netDevList->values = getZone(zoneId.value).getNetdevList();
867 result->set(netDevList);
868 } catch (const InvalidZoneIdException&) {
869 LOGE("No zone with id=" << zoneId.value);
870 result->setError(api::ERROR_INVALID_ID, "No such zone id");
871 } catch (const std::runtime_error& ex) {
872 LOGE("Can't set attributes: " << ex.what());
873 result->setError(api::ERROR_INTERNAL, ex.what());
877 tryAddTask(handler, result, true);
880 void ZonesManager::handleCreateNetdevVethCall(const api::CreateNetDevVethIn& data,
881 api::MethodResultBuilder::Pointer result)
883 auto handler = [&, this] {
884 LOGI("CreateNetdevVeth call");
889 getZone(data.id).createNetdevVeth(data.zoneDev, data.hostDev);
891 } catch (const InvalidZoneIdException&) {
892 LOGE("No zone with id=" << data.id);
893 result->setError(api::ERROR_INVALID_ID, "No such zone id");
894 } catch (const std::runtime_error& ex) {
895 LOGE("Can't create veth: " << ex.what());
896 result->setError(api::ERROR_INTERNAL, ex.what());
900 tryAddTask(handler, result, true);
903 void ZonesManager::handleCreateNetdevMacvlanCall(const api::CreateNetDevMacvlanIn& data,
904 api::MethodResultBuilder::Pointer result)
906 auto handler = [&, this] {
907 LOGI("CreateNetdevMacvlan call");
911 getZone(data.id).createNetdevMacvlan(data.zoneDev, data.hostDev, data.mode);
913 } catch (const InvalidZoneIdException&) {
914 LOGE("No zone with id=" << data.id);
915 result->setError(api::ERROR_INVALID_ID, "No such zone id");
916 } catch (const std::runtime_error& ex) {
917 LOGE("Can't create macvlan: " << ex.what());
918 result->setError(api::ERROR_INTERNAL, ex.what());
922 tryAddTask(handler, result, true);
925 void ZonesManager::handleCreateNetdevPhysCall(const api::CreateNetDevPhysIn& data,
926 api::MethodResultBuilder::Pointer result)
928 auto handler = [&, this] {
929 LOGI("CreateNetdevPhys call");
934 getZone(data.first).moveNetdev(data.second);
936 } catch (const InvalidZoneIdException&) {
937 LOGE("No zone with id=" << data.first);
938 result->setError(api::ERROR_INVALID_ID, "No such zone id");
939 } catch (const std::runtime_error& ex) {
940 LOGE("Can't create netdev: " << ex.what());
941 result->setError(api::ERROR_INTERNAL, ex.what());
945 tryAddTask(handler, result, true);
948 void ZonesManager::handleDestroyNetdevCall(const api::DestroyNetDevIn& data,
949 api::MethodResultBuilder::Pointer result)
951 auto handler = [&, this] {
952 LOGI("DestroyNetdev call");
957 getZone(data.first).destroyNetdev(data.second);
959 } catch (const InvalidZoneIdException&) {
960 LOGE("No zone with id=" << data.first);
961 result->setError(api::ERROR_INVALID_ID, "No such zone id");
962 } catch (const std::runtime_error& ex) {
963 LOGE("Can't create netdev: " << ex.what());
964 result->setError(api::ERROR_INTERNAL, ex.what());
968 tryAddTask(handler, result, true);
971 void ZonesManager::handleDeleteNetdevIpAddressCall(const api::DeleteNetdevIpAddressIn& data,
972 api::MethodResultBuilder::Pointer result)
974 auto handler = [&, this] {
975 LOGI("DelNetdevIpAddress call");
979 getZone(data.zone).deleteNetdevIpAddress(data.netdev, data.ip);
981 } catch (const InvalidZoneIdException&) {
982 LOGE("No zone with id=" << data.zone);
983 result->setError(api::ERROR_INVALID_ID, "No such zone id");
984 } catch (const std::runtime_error& ex) {
985 LOGE("Can't delete address: " << ex.what());
986 result->setError(api::ERROR_INTERNAL, ex.what());
990 tryAddTask(handler, result, true);
993 void ZonesManager::handleDeclareFileCall(const api::DeclareFileIn& data,
994 api::MethodResultBuilder::Pointer result)
996 auto handler = [&, this] {
997 LOGI("DeclareFile call");
1001 auto declaration = std::make_shared<api::Declaration>();
1002 declaration->value = getZone(data.zone).declareFile(data.type, data.path, data.flags, data.mode);
1003 result->set(declaration);
1004 } catch (const InvalidZoneIdException&) {
1005 LOGE("No zone with id=" << data.zone);
1006 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1007 } catch (const config::ConfigException& ex) {
1008 LOGE("Can't declare file: " << ex.what());
1009 result->setError(api::ERROR_INTERNAL, "Internal error");
1013 tryAddTask(handler, result, true);
1016 void ZonesManager::handleDeclareMountCall(const api::DeclareMountIn& data,
1017 api::MethodResultBuilder::Pointer result)
1019 auto handler = [&, this] {
1020 LOGI("DeclareMount call");
1024 auto declaration = std::make_shared<api::Declaration>();
1025 declaration->value = getZone(data.zone).declareMount(data.source, data.target, data.type, data.flags, data.data);
1026 result->set(declaration);
1027 } catch (const InvalidZoneIdException&) {
1028 LOGE("No zone with id=" << data.zone);
1029 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1030 } catch (const config::ConfigException& ex) {
1031 LOGE("Can't declare mount: " << ex.what());
1032 result->setError(api::ERROR_INTERNAL, "Internal error");
1036 tryAddTask(handler, result, true);
1039 void ZonesManager::handleDeclareLinkCall(const api::DeclareLinkIn& data,
1040 api::MethodResultBuilder::Pointer result)
1042 auto handler = [&, this] {
1043 LOGI("DeclareLink call");
1047 auto declaration = std::make_shared<api::Declaration>();
1048 declaration->value = getZone(data.zone).declareLink(data.source, data.target);
1049 result->set(declaration);
1050 } catch (const InvalidZoneIdException&) {
1051 LOGE("No zone with id=" << data.zone);
1052 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1053 } catch (const config::ConfigException& ex) {
1054 LOGE("Can't declare link: " << ex.what());
1055 result->setError(api::ERROR_INTERNAL, "Internal error");
1059 tryAddTask(handler, result, true);
1062 void ZonesManager::handleGetDeclarationsCall(const api::ZoneId& zoneId,
1063 api::MethodResultBuilder::Pointer result)
1065 auto handler = [&, this] {
1066 LOGI("GetDeclarations call Id=" << zoneId.value);
1070 auto declarations = std::make_shared<api::Declarations>();
1071 declarations->values = getZone(zoneId.value).getDeclarations();
1072 result->set(declarations);
1073 } catch (const InvalidZoneIdException&) {
1074 LOGE("No zone with id=" << zoneId.value);
1075 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1076 } catch (const std::runtime_error& ex) {
1078 result->setError(api::ERROR_INTERNAL, ex.what());
1082 tryAddTask(handler, result, true);
1085 void ZonesManager::handleRemoveDeclarationCall(const api::RemoveDeclarationIn& data,
1086 api::MethodResultBuilder::Pointer result)
1088 auto handler = [&, this] {
1089 LOGI("RemoveDeclaration call Id=" << data.first);
1093 getZone(data.first).removeDeclaration(data.second);
1095 } catch (const InvalidZoneIdException&) {
1096 LOGE("No zone with id=" << data.first);
1097 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1098 } catch (const std::runtime_error& ex) {
1100 result->setError(api::ERROR_INTERNAL, ex.what());
1104 tryAddTask(handler, result, true);
1107 void ZonesManager::handleSetActiveZoneCall(const api::ZoneId& zoneId,
1108 api::MethodResultBuilder::Pointer result)
1110 auto handler = [&, this] {
1111 LOGI("SetActiveZone call; Id=" << zoneId.value );
1115 auto iter = findZone(zoneId.value);
1116 if (iter == mZones.end()) {
1117 LOGE("No zone with id=" << zoneId.value);
1118 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1122 if (!get(iter).isRunning()) {
1123 LOGE("Could not activate stopped or paused zone");
1124 result->setError(api::ERROR_ZONE_NOT_RUNNING,
1125 "Could not activate stopped or paused zone");
1129 focusInternal(iter);
1133 tryAddTask(handler, result, true);
1137 void ZonesManager::generateNewConfig(const std::string& id,
1138 const std::string& templatePath)
1140 const std::string dbPrefix = getZoneDbPrefix(id);
1141 ZoneDynamicConfig dynamicConfig;
1142 config::loadFromKVStoreWithJsonFile(mConfig.dbPath, templatePath, dynamicConfig, dbPrefix);
1144 // update mount point path
1145 dynamicConfig.runMountPoint = boost::regex_replace(dynamicConfig.runMountPoint,
1149 if (dynamicConfig.vt >= 0) {
1150 // generate first free VT number
1151 const int freeVT = getVTForNewZone();
1152 LOGD("VT number: " << freeVT);
1153 dynamicConfig.vt = freeVT;
1155 if (!dynamicConfig.ipv4Gateway.empty() && !dynamicConfig.ipv4.empty()) {
1156 // generate third IP octet for network config
1157 std::string thirdOctetStr = std::to_string(ZONE_IP_BASE_THIRD_OCTET + freeVT);
1158 LOGD("IP third octet: " << thirdOctetStr);
1159 dynamicConfig.ipv4Gateway = boost::regex_replace(dynamicConfig.ipv4Gateway,
1160 ZONE_IP_THIRD_OCTET_REGEX,
1162 dynamicConfig.ipv4 = boost::regex_replace(dynamicConfig.ipv4,
1163 ZONE_IP_THIRD_OCTET_REGEX,
1168 // save dynamic config
1169 config::saveToKVStore(mConfig.dbPath, dynamicConfig, dbPrefix);
1171 // save zone template path
1172 ZoneTemplatePathConfig templatePathConfig;
1173 templatePathConfig.zoneTemplatePath = templatePath;
1174 config::saveToKVStore(mConfig.dbPath, templatePathConfig, dbPrefix);
1178 int ZonesManager::getVTForNewZone()
1180 if (mConfig.availableVTs.empty()) {
1183 std::set<int> candidates(mConfig.availableVTs.begin(), mConfig.availableVTs.end());
1185 for (auto& zone : mZones) {
1186 candidates.erase(zone->getVT());
1188 if (candidates.empty()) {
1189 const std::string msg = "No free VT for zone";
1191 throw ZoneOperationException(msg);
1193 // return the smallest
1194 return *candidates.begin();
1197 void ZonesManager::createZone(const std::string& id,
1198 const std::string& templateName)
1200 if (id.empty() || !isalnum(id)) {
1201 const std::string msg = "Failed to add zone - invalid name.";
1203 throw InvalidZoneIdException(msg);
1206 if (find(prohibitedZonesNames.begin(), prohibitedZonesNames.end(), id) != prohibitedZonesNames.end()) {
1207 const std::string msg = "Cannot create " + id + " zone - name is not allowed!";
1209 throw InvalidZoneIdException(msg);
1212 LOGI("Creating zone " << id);
1216 // TODO: This solution is temporary. It utilizes direct access to config files when creating new
1217 // zones. Update this handler when config database will appear.
1218 namespace fs = boost::filesystem;
1220 // check if zone does not exist
1221 if (findZone(id) != mZones.end()) {
1222 const std::string msg = "Cannot create " + id + " zone - already exists!";
1224 throw InvalidZoneIdException(msg);
1227 if (fs::exists(fs::path(mConfig.zonesPath) / id)) {
1228 const std::string msg = "Cannot create " + id + " zone - file system already exists!";
1230 throw InvalidZoneIdException(msg);
1233 const std::string zonePathStr = utils::createFilePath(mConfig.zonesPath, id, "/");
1235 // copy zone image if config contains path to image
1236 LOGT("Image path: " << mConfig.zoneImagePath);
1237 if (!mConfig.zoneImagePath.empty()) {
1238 auto copyImageContentsWrapper = std::bind(&utils::copyImageContents,
1239 mConfig.zoneImagePath,
1242 if (!utils::launchAsRoot(copyImageContentsWrapper)) {
1243 const std::string msg = "Failed to copy zone image.";
1245 throw ZoneOperationException(msg);
1249 auto removeAllWrapper = [](const std::string & path) -> bool {
1251 LOGD("Removing copied data");
1252 fs::remove_all(fs::path(path));
1253 } catch (const std::exception& e) {
1254 LOGW("Failed to remove data: " << boost::diagnostic_information(e));
1259 std::string zoneTemplatePath = utils::createFilePath(mConfig.zoneTemplateDir,
1260 templateName + ".conf");
1263 LOGI("Generating config from " << zoneTemplatePath);
1264 generateNewConfig(id, zoneTemplatePath);
1265 } catch (std::runtime_error& e) {
1266 LOGE("Generate config failed: " << e.what());
1267 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1271 LOGT("Creating new zone");
1273 insertZone(id, zoneTemplatePath);
1274 } catch (std::runtime_error& e) {
1275 LOGE("Creating new zone failed: " << e.what());
1276 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1280 mDynamicConfig.zoneIds.push_back(id);
1281 saveDynamicConfig();
1285 void ZonesManager::handleCreateZoneCall(const api::CreateZoneIn& data,
1286 api::MethodResultBuilder::Pointer result)
1288 auto creator = [&, this] {
1290 createZone(data.first, data.second);
1292 } catch (const InvalidZoneIdException& e) {
1293 result->setError(api::ERROR_INVALID_ID, e.what());
1294 } catch (const std::exception& e) {
1295 result->setError(api::ERROR_INTERNAL, e.what());
1299 tryAddTask(creator, result, true);
1302 void ZonesManager::handleDestroyZoneCall(const api::ZoneId& zoneId,
1303 api::MethodResultBuilder::Pointer result)
1305 auto destroyer = [=] {
1307 LOGI("Destroying zone " << zoneId.value);
1308 destroyZone(zoneId.value);
1309 } catch (const InvalidZoneIdException&) {
1310 LOGE("Failed to destroy zone - no such zone id: " << zoneId.value);
1311 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1312 } catch (const std::runtime_error& e) {
1313 LOGE("Error during zone destruction: " << e.what());
1314 result->setError(api::ERROR_INTERNAL, "Failed to destroy zone");
1320 tryAddTask(destroyer, result, false);
1323 void ZonesManager::handleShutdownZoneCall(const api::ZoneId& zoneId,
1324 api::MethodResultBuilder::Pointer result)
1326 auto shutdown = [=] {
1327 LOGI("ShutdownZone call; Id=" << zoneId.value);
1330 LOGT("Shutdown zone " << zoneId.value);
1333 auto iter = findZone(zoneId.value);
1334 if (iter == mZones.end()) {
1335 LOGE("Failed to shutdown zone - no such zone id: " << zoneId.value);
1336 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1339 get(iter).stop(true);
1342 } catch (ZoneOperationException& e) {
1343 LOGE("Error during zone shutdown: " << e.what());
1344 result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone");
1349 tryAddTask(shutdown, result, false);
1352 void ZonesManager::handleStartZoneCall(const api::ZoneId& zoneId,
1353 api::MethodResultBuilder::Pointer result)
1355 auto startAsync = [=] {
1356 LOGI("StartZone call; Id=" << zoneId.value);
1359 LOGT("Start zone " << zoneId.value);
1362 auto iter = findZone(zoneId.value);
1363 if (iter == mZones.end()) {
1364 LOGE("Failed to start zone - no such zone id: " << zoneId.value);
1365 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1369 focusInternal(iter);
1371 } catch (const std::exception& e) {
1372 LOGE(zoneId.value << ": failed to start: " << e.what());
1373 result->setError(api::ERROR_INTERNAL, "Failed to start zone");
1376 tryAddTask(startAsync, result, false);
1379 void ZonesManager::handleLockZoneCall(const api::ZoneId& zoneId,
1380 api::MethodResultBuilder::Pointer result)
1382 auto handler = [&, this] {
1383 LOGI("LockZone call; Id=" << zoneId.value );
1387 auto iter = findZone(zoneId.value);
1388 if (iter == mZones.end()) {
1389 LOGE("Failed to lock zone - no such zone id: " << zoneId.value);
1390 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1394 Zone& zone = get(iter);
1395 if (!zone.isRunning()) {
1396 LOGE("Zone id=" << zoneId.value << " is not running.");
1397 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1403 zone.goBackground();// make sure it will be in background after unlock
1406 } catch (ZoneOperationException& e) {
1408 result->setError(api::ERROR_INTERNAL, e.what());
1415 tryAddTask(handler, result, true);
1418 void ZonesManager::handleUnlockZoneCall(const api::ZoneId& zoneId,
1419 api::MethodResultBuilder::Pointer result)
1421 auto handler = [&, this] {
1422 LOGI("UnlockZone call; Id=" << zoneId.value );
1426 auto iter = findZone(zoneId.value);
1427 if (iter == mZones.end()) {
1428 LOGE("Failed to unlock zone - no such zone id: " << zoneId.value);
1429 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1433 Zone& zone = get(iter);
1434 if (!zone.isPaused()) {
1435 LOGE("Zone id=" << zoneId.value << " is not paused.");
1436 result->setError(api::ERROR_INVALID_STATE, "Zone is not paused");
1440 LOGT("Unlock zone");
1443 } catch (ZoneOperationException& e) {
1445 result->setError(api::ERROR_INTERNAL, e.what());
1452 tryAddTask(handler, result, true);
1455 void ZonesManager::handleGrantDeviceCall(const api::GrantDeviceIn& data,
1456 api::MethodResultBuilder::Pointer result)
1458 auto handler = [&, this] {
1459 LOGI("GrantDevice call; id=" << data.id << "; dev=" << data.device);
1463 auto iter = findZone(data.id);
1464 if (iter == mZones.end()) {
1465 LOGE("Failed to grant device - no such zone id: " << data.id);
1466 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1470 Zone& zone = get(iter);
1471 if (!zone.isRunning() && !zone.isPaused()) {
1472 LOGE("Zone id=" << data.id << " is not running");
1473 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1477 std::string devicePath = "/dev/" + data.device;
1479 if (!lxc::isDevice(devicePath)) {
1480 LOGE("Failed to grant device - cannot acces device: " << data.device);
1481 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1485 // assume device node is created inside zone
1486 if (!lxc::setDeviceAccess(data.id, devicePath, true, data.flags)) {
1487 LOGE("Failed to grant device: " << data.device << " for zone: " << data.id);
1488 result->setError(api::ERROR_INTERNAL, "Cannot grant device");
1495 tryAddTask(handler, result, true);
1498 void ZonesManager::handleRevokeDeviceCall(const api::RevokeDeviceIn& data,
1499 api::MethodResultBuilder::Pointer result)
1501 auto handler = [&, this] {
1502 LOGI("RevokeDevice call; id=" << data.first << "; dev=" << data.second);
1506 auto iter = findZone(data.first);
1507 if (iter == mZones.end()) {
1508 LOGE("Failed to revoke device - no such zone id: " << data.first);
1509 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1513 Zone& zone = get(iter);
1514 if (!zone.isRunning() && !zone.isPaused()) {
1515 LOGE("Zone id=" << data.first << " is not running");
1516 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1519 std::string devicePath = "/dev/" + data.second;
1521 if (!lxc::isDevice(devicePath)) {
1522 LOGE("Failed to revoke device - cannot acces device: " << data.second);
1523 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1527 if (!lxc::setDeviceAccess(data.first, devicePath, false, 0)) {
1528 LOGE("Failed to revoke device: " << data.second << " for zone: " << data.first);
1529 result->setError(api::ERROR_INTERNAL, "Cannot revoke device");
1536 tryAddTask(handler, result, true);
1539 void ZonesManager::handleCleanUpZonesRootCall(api::MethodResultBuilder::Pointer result)
1541 auto handler = [&, this] {
1542 LOGI("CleanUpZonesRoot call");
1544 std::vector<std::string> zonesIds;
1546 for (const auto& zone : mZones) {
1547 zonesIds.push_back(zone->getId());
1549 cleanUpUnknownsFromRoot(mConfig.zonesPath, zonesIds, false);
1550 } catch (const std::exception& e) {
1551 result->setError(api::ERROR_INTERNAL, e.what());
1556 tryAddTask(handler, result, true);
1559 } // namespace vasum