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 if (mConfig.hostVT > 0) {
396 LOGI("Focus to: host");
397 utils::activateVT(mConfig.hostVT);
399 mActiveZoneId.clear();
404 Zone& zoneToFocus = get(iter);
405 const std::string& idToFocus = zoneToFocus.getId();
407 if (idToFocus == mActiveZoneId) {
411 if (!zoneToFocus.isRunning()) {
412 LOGE("Can't focus not running zone " << idToFocus);
417 LOGI("Focus to: " << idToFocus);
419 if (!zoneToFocus.activateVT()) {
420 LOGE("Failed to activate zones VT");
424 for (auto& zone : mZones) {
425 if (zone->isRunning()) {
426 std::string id = zone->getId();
427 if (id == idToFocus) {
428 LOGD(id << ": being sent to foreground");
429 zone->goForeground();
431 LOGD(id << ": being sent to background");
432 zone->goBackground();
436 mActiveZoneId = idToFocus;
439 void ZonesManager::refocus()
441 // assume mutex is locked
443 // check if refocus is required
444 auto oldIter = findZone(mActiveZoneId);
445 if (oldIter != mZones.end() && get(oldIter).isRunning()) {
449 // try to refocus to defaultId
450 auto iter = findZone(mDynamicConfig.defaultId);
451 if (iter == mZones.end() || !get(iter).isRunning()) {
452 // focus to any running or to host if not found
453 iter = std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
458 void ZonesManager::restoreAll()
460 LOGI("Restoring all zones");
464 for (auto& zone : mZones) {
466 // FIXME wait until zone is started and stable
467 // there is problem (with lxc-start) when starting zones too fast
468 // here or in zone.cpp after start
469 std::this_thread::sleep_for(std::chrono::milliseconds(500));
475 void ZonesManager::shutdownAll()
477 LOGI("Stopping all zones");
481 for (auto& zone : mZones) {
488 bool ZonesManager::isPaused(const std::string& zoneId)
491 return getZone(zoneId).isPaused();
494 bool ZonesManager::isRunning(const std::string& zoneId)
497 return getZone(zoneId).isRunning();
500 bool ZonesManager::isStopped(const std::string& zoneId)
503 return getZone(zoneId).isStopped();
506 std::string ZonesManager::getRunningForegroundZoneId()
509 auto iter = getRunningForegroundZoneIterator();
510 return iter == mZones.end() ? std::string() : get(iter).getId();
513 std::string ZonesManager::getNextToForegroundZoneId()
516 auto iter = getNextToForegroundZoneIterator();
517 return iter == mZones.end() ? std::string() : get(iter).getId();
520 ZonesManager::Zones::iterator ZonesManager::getRunningForegroundZoneIterator()
522 // assume mutex is locked
523 if (mActiveZoneId.empty()) {
526 auto iter = findZone(mActiveZoneId);
527 if (!get(iter).isRunning()) {
528 LOGW("Active zone " << mActiveZoneId << " is not running any more!");
534 ZonesManager::Zones::iterator ZonesManager::getNextToForegroundZoneIterator()
536 // assume mutex is locked
537 auto current = findZone(mActiveZoneId);
538 if (current == mZones.end()) {
540 return std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
543 return circularFindNext(mZones.begin(), mZones.end(), current, zoneIsRunning);
547 void ZonesManager::switchingSequenceMonitorNotify()
549 LOGI("switchingSequenceMonitorNotify() called");
553 auto next = getNextToForegroundZoneIterator();
555 if (next != mZones.end()) {
561 void ZonesManager::setZonesDetachOnExit()
565 mDetachOnExit = true;
567 for (auto& zone : mZones) {
568 zone->setDetachOnExit();
572 void ZonesManager::disconnectedCallback(const std::string& id)
574 LOGD("Client Disconnected: " << id);
577 Lock lock(mExclusiveIDMutex);
579 if (mExclusiveIDLock == id) {
580 mExclusiveIDLock = INVALID_CONNECTION_ID;
585 void ZonesManager::handleSwitchToDefaultCall(const std::string& /*caller*/,
586 api::MethodResultBuilder::Pointer result)
588 auto handler = [&, this] {
589 // get config of currently set zone and switch if switchToDefaultAfterTimeout is true
592 auto activeIter = findZone(mActiveZoneId);
593 auto defaultIter = findZone(mDynamicConfig.defaultId);
595 if (activeIter != mZones.end() &&
596 defaultIter != mZones.end() &&
597 get(activeIter).isSwitchToDefaultAfterTimeoutAllowed() &&
598 get(defaultIter).isRunning()) {
600 LOGI("Switching to default zone " << mDynamicConfig.defaultId);
601 focusInternal(defaultIter);
606 tryAddTask(handler, result, true);
609 void ZonesManager::handleCreateFileCall(const api::CreateFileIn& request,
610 api::MethodResultBuilder::Pointer result)
612 auto handler = [&, this] {
613 LOGI("CreateFile call");
617 auto srcIter = findZone(request.id);
618 if (srcIter == mZones.end()) {
619 LOGE("Zone '" << request.id << "' not found");
620 result->setError(api::ERROR_INVALID_ID, "Requested Zone was not found.");
623 Zone& srcZone = get(srcIter);
625 auto retValue = std::make_shared<api::CreateFileOut>();
627 retValue->fd = srcZone.createFile(request.path, request.flags, request.mode);
628 } catch(ZoneOperationException& e) {
629 result->setError(api::ERROR_CREATE_FILE_FAILED, "Unable to create file");
633 result->set(retValue);
636 tryAddTask(handler, result, true);
639 #ifdef DBUS_CONNECTION
640 void ZonesManager::handleProxyCall(const std::string& caller,
641 const std::string& target,
642 const std::string& targetBusName,
643 const std::string& targetObjectPath,
644 const std::string& targetInterface,
645 const std::string& targetMethod,
646 GVariant* parameters,
647 dbus::MethodResultBuilder::Pointer result)
649 auto handler = [&, this] {
650 if (!mProxyCallPolicy->isProxyCallAllowed(caller,
656 LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
657 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
658 result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
662 LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
663 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
665 auto asyncResultCallback = [result](dbus::AsyncMethodCallResult & asyncMethodCallResult) {
667 GVariant* targetResult = asyncMethodCallResult.get();
668 result->set(g_variant_new("(v)", targetResult));
669 } catch (dbus::DbusException& e) {
670 result->setError(api::ERROR_FORWARDED, e.what());
674 if (target != HOST_ID) {
675 result->setError(api::ERROR_INVALID_ID, "Unknown proxy call target");
679 mHostDbusConnection.proxyCallAsync(targetBusName,
684 asyncResultCallback);
687 // This call cannot be locked by lock/unlock queue
688 mWorker->addTaskAndWait(handler);
690 #endif //DBUS_CONNECTION
692 void ZonesManager::handleLockQueueCall(api::MethodResultBuilder::Pointer result)
694 Lock lock(mExclusiveIDMutex);
695 std::string id = result->getID();
697 LOGI("Lock Queue: " << id);
699 if (mExclusiveIDLock == id) {
700 result->setError(api::ERROR_QUEUE, "Queue already locked");
704 if (mExclusiveIDLock != INVALID_CONNECTION_ID) {
705 result->setError(api::ERROR_QUEUE, "Queue locked by another connection");
709 mExclusiveIDLock = id;
713 void ZonesManager::handleUnlockQueueCall(api::MethodResultBuilder::Pointer result)
715 Lock lock(mExclusiveIDMutex);
716 std::string id = result->getID();
718 LOGI("Unlock Queue: " << id);
720 if (mExclusiveIDLock == INVALID_CONNECTION_ID) {
721 result->setError(api::ERROR_QUEUE, "Queue not locked");
725 if (mExclusiveIDLock != id) {
726 result->setError(api::ERROR_QUEUE, "Queue locked by another connection");
730 mExclusiveIDLock = INVALID_CONNECTION_ID;
734 void ZonesManager::handleGetZoneIdsCall(api::MethodResultBuilder::Pointer result)
736 auto handler = [&, this] {
737 LOGI("GetZoneIds call");
741 auto zoneIds = std::make_shared<api::ZoneIds>();
742 for (const auto& zone : mZones) {
743 zoneIds->values.push_back(zone->getId());
746 result->set(zoneIds);
749 // This call cannot be locked by lock/unlock queue
750 mWorker->addTaskAndWait(handler);
753 void ZonesManager::handleGetActiveZoneIdCall(api::MethodResultBuilder::Pointer result)
755 auto handler = [&, this] {
756 LOGI("GetActiveZoneId call");
758 auto zoneId = std::make_shared<api::ZoneId>();
759 zoneId->value = getRunningForegroundZoneId();
763 // This call cannot be locked by lock/unlock queue
764 mWorker->addTaskAndWait(handler);
767 void ZonesManager::handleGetZoneInfoCall(const api::ZoneId& zoneId,
768 api::MethodResultBuilder::Pointer result)
770 auto handler = [&, this] {
771 LOGI("GetZoneInfo call");
775 auto iter = findZone(zoneId.value);
776 if (iter == mZones.end()) {
777 LOGE("No zone with id=" << zoneId.value);
778 result->setError(api::ERROR_INVALID_ID, "No such zone id");
782 Zone& zone = get(iter);
783 auto zoneInfo = std::make_shared<api::ZoneInfoOut>();
785 if (zone.isRunning()) {
786 zoneInfo->state = "RUNNING";
787 } else if (zone.isStopped()) {
788 zoneInfo->state = "STOPPED";
789 } else if (zone.isPaused()) {
790 zoneInfo->state = "FROZEN";
792 LOGE("Unrecognized state of zone id=" << zoneId.value);
793 result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone");
797 zoneInfo->id = zone.getId();
798 zoneInfo->vt = zone.getVT();
799 zoneInfo->rootPath = zone.getRootPath();
800 result->set(zoneInfo);
803 // This call cannot be locked by lock/unlock queue
804 mWorker->addTaskAndWait(handler);
807 void ZonesManager::handleSetNetdevAttrsCall(const api::SetNetDevAttrsIn& data,
808 api::MethodResultBuilder::Pointer result)
810 auto handler = [&, this] {
811 LOGI("SetNetdevAttrs call");
816 // TODO: Use vector<StringPair> instead of tuples
817 std::vector<std::tuple<std::string, std::string>> attrsAsTuples;
818 for(const auto& entry: data.attrs){
819 attrsAsTuples.push_back(std::make_tuple(entry.first, entry.second));
822 getZone(data.id).setNetdevAttrs(data.netDev, attrsAsTuples);
824 } catch (const InvalidZoneIdException&) {
825 LOGE("No zone with id=" << data.id);
826 result->setError(api::ERROR_INVALID_ID, "No such zone id");
827 } catch (const std::runtime_error& ex) {
828 LOGE("Can't set attributes: " << ex.what());
829 result->setError(api::ERROR_INTERNAL, ex.what());
833 tryAddTask(handler, result, true);
836 void ZonesManager::handleGetNetdevAttrsCall(const api::GetNetDevAttrsIn& data,
837 api::MethodResultBuilder::Pointer result)
839 auto handler = [&, this] {
840 LOGI("GetNetdevAttrs call");
844 auto netDevAttrs = std::make_shared<api::GetNetDevAttrs>();
845 const auto attrs = getZone(data.first).getNetdevAttrs(data.second);
847 for (size_t i = 0; i < attrs.size(); ++i) {
848 netDevAttrs->values.push_back({std::get<0>(attrs[i]), std::get<1>(attrs[i])});
850 result->set(netDevAttrs);
851 } catch (const InvalidZoneIdException&) {
852 LOGE("No zone with id=" << data.first);
853 result->setError(api::ERROR_INVALID_ID, "No such zone id");
854 } catch (const std::runtime_error& ex) {
855 LOGE("Can't set attributes: " << ex.what());
856 result->setError(api::ERROR_INTERNAL, ex.what());
860 tryAddTask(handler, result, true);
863 void ZonesManager::handleGetNetdevListCall(const api::ZoneId& zoneId,
864 api::MethodResultBuilder::Pointer result)
866 auto handler = [&, this] {
867 LOGI("GetNetdevList call");
871 auto netDevList = std::make_shared<api::NetDevList>();
872 netDevList->values = getZone(zoneId.value).getNetdevList();
873 result->set(netDevList);
874 } catch (const InvalidZoneIdException&) {
875 LOGE("No zone with id=" << zoneId.value);
876 result->setError(api::ERROR_INVALID_ID, "No such zone id");
877 } catch (const std::runtime_error& ex) {
878 LOGE("Can't set attributes: " << ex.what());
879 result->setError(api::ERROR_INTERNAL, ex.what());
883 tryAddTask(handler, result, true);
886 void ZonesManager::handleCreateNetdevVethCall(const api::CreateNetDevVethIn& data,
887 api::MethodResultBuilder::Pointer result)
889 auto handler = [&, this] {
890 LOGI("CreateNetdevVeth call");
895 getZone(data.id).createNetdevVeth(data.zoneDev, data.hostDev);
897 } catch (const InvalidZoneIdException&) {
898 LOGE("No zone with id=" << data.id);
899 result->setError(api::ERROR_INVALID_ID, "No such zone id");
900 } catch (const std::runtime_error& ex) {
901 LOGE("Can't create veth: " << ex.what());
902 result->setError(api::ERROR_INTERNAL, ex.what());
906 tryAddTask(handler, result, true);
909 void ZonesManager::handleCreateNetdevMacvlanCall(const api::CreateNetDevMacvlanIn& data,
910 api::MethodResultBuilder::Pointer result)
912 auto handler = [&, this] {
913 LOGI("CreateNetdevMacvlan call");
917 getZone(data.id).createNetdevMacvlan(data.zoneDev, data.hostDev, data.mode);
919 } catch (const InvalidZoneIdException&) {
920 LOGE("No zone with id=" << data.id);
921 result->setError(api::ERROR_INVALID_ID, "No such zone id");
922 } catch (const std::runtime_error& ex) {
923 LOGE("Can't create macvlan: " << ex.what());
924 result->setError(api::ERROR_INTERNAL, ex.what());
928 tryAddTask(handler, result, true);
931 void ZonesManager::handleCreateNetdevPhysCall(const api::CreateNetDevPhysIn& data,
932 api::MethodResultBuilder::Pointer result)
934 auto handler = [&, this] {
935 LOGI("CreateNetdevPhys call");
940 getZone(data.first).moveNetdev(data.second);
942 } catch (const InvalidZoneIdException&) {
943 LOGE("No zone with id=" << data.first);
944 result->setError(api::ERROR_INVALID_ID, "No such zone id");
945 } catch (const std::runtime_error& ex) {
946 LOGE("Can't create netdev: " << ex.what());
947 result->setError(api::ERROR_INTERNAL, ex.what());
951 tryAddTask(handler, result, true);
954 void ZonesManager::handleDestroyNetdevCall(const api::DestroyNetDevIn& data,
955 api::MethodResultBuilder::Pointer result)
957 auto handler = [&, this] {
958 LOGI("DestroyNetdev call");
963 getZone(data.first).destroyNetdev(data.second);
965 } catch (const InvalidZoneIdException&) {
966 LOGE("No zone with id=" << data.first);
967 result->setError(api::ERROR_INVALID_ID, "No such zone id");
968 } catch (const std::runtime_error& ex) {
969 LOGE("Can't create netdev: " << ex.what());
970 result->setError(api::ERROR_INTERNAL, ex.what());
974 tryAddTask(handler, result, true);
977 void ZonesManager::handleDeleteNetdevIpAddressCall(const api::DeleteNetdevIpAddressIn& data,
978 api::MethodResultBuilder::Pointer result)
980 auto handler = [&, this] {
981 LOGI("DelNetdevIpAddress call");
985 getZone(data.zone).deleteNetdevIpAddress(data.netdev, data.ip);
987 } catch (const InvalidZoneIdException&) {
988 LOGE("No zone with id=" << data.zone);
989 result->setError(api::ERROR_INVALID_ID, "No such zone id");
990 } catch (const std::runtime_error& ex) {
991 LOGE("Can't delete address: " << ex.what());
992 result->setError(api::ERROR_INTERNAL, ex.what());
996 tryAddTask(handler, result, true);
999 void ZonesManager::handleDeclareFileCall(const api::DeclareFileIn& data,
1000 api::MethodResultBuilder::Pointer result)
1002 auto handler = [&, this] {
1003 LOGI("DeclareFile call");
1007 auto declaration = std::make_shared<api::Declaration>();
1008 declaration->value = getZone(data.zone).declareFile(data.type, data.path, data.flags, data.mode);
1009 result->set(declaration);
1010 } catch (const InvalidZoneIdException&) {
1011 LOGE("No zone with id=" << data.zone);
1012 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1013 } catch (const config::ConfigException& ex) {
1014 LOGE("Can't declare file: " << ex.what());
1015 result->setError(api::ERROR_INTERNAL, "Internal error");
1019 tryAddTask(handler, result, true);
1022 void ZonesManager::handleDeclareMountCall(const api::DeclareMountIn& data,
1023 api::MethodResultBuilder::Pointer result)
1025 auto handler = [&, this] {
1026 LOGI("DeclareMount call");
1030 auto declaration = std::make_shared<api::Declaration>();
1031 declaration->value = getZone(data.zone).declareMount(data.source, data.target, data.type, data.flags, data.data);
1032 result->set(declaration);
1033 } catch (const InvalidZoneIdException&) {
1034 LOGE("No zone with id=" << data.zone);
1035 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1036 } catch (const config::ConfigException& ex) {
1037 LOGE("Can't declare mount: " << ex.what());
1038 result->setError(api::ERROR_INTERNAL, "Internal error");
1042 tryAddTask(handler, result, true);
1045 void ZonesManager::handleDeclareLinkCall(const api::DeclareLinkIn& data,
1046 api::MethodResultBuilder::Pointer result)
1048 auto handler = [&, this] {
1049 LOGI("DeclareLink call");
1053 auto declaration = std::make_shared<api::Declaration>();
1054 declaration->value = getZone(data.zone).declareLink(data.source, data.target);
1055 result->set(declaration);
1056 } catch (const InvalidZoneIdException&) {
1057 LOGE("No zone with id=" << data.zone);
1058 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1059 } catch (const config::ConfigException& ex) {
1060 LOGE("Can't declare link: " << ex.what());
1061 result->setError(api::ERROR_INTERNAL, "Internal error");
1065 tryAddTask(handler, result, true);
1068 void ZonesManager::handleGetDeclarationsCall(const api::ZoneId& zoneId,
1069 api::MethodResultBuilder::Pointer result)
1071 auto handler = [&, this] {
1072 LOGI("GetDeclarations call Id=" << zoneId.value);
1076 auto declarations = std::make_shared<api::Declarations>();
1077 declarations->values = getZone(zoneId.value).getDeclarations();
1078 result->set(declarations);
1079 } catch (const InvalidZoneIdException&) {
1080 LOGE("No zone with id=" << zoneId.value);
1081 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1082 } catch (const std::runtime_error& ex) {
1084 result->setError(api::ERROR_INTERNAL, ex.what());
1088 tryAddTask(handler, result, true);
1091 void ZonesManager::handleRemoveDeclarationCall(const api::RemoveDeclarationIn& data,
1092 api::MethodResultBuilder::Pointer result)
1094 auto handler = [&, this] {
1095 LOGI("RemoveDeclaration call Id=" << data.first);
1099 getZone(data.first).removeDeclaration(data.second);
1101 } catch (const InvalidZoneIdException&) {
1102 LOGE("No zone with id=" << data.first);
1103 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1104 } catch (const std::runtime_error& ex) {
1106 result->setError(api::ERROR_INTERNAL, ex.what());
1110 tryAddTask(handler, result, true);
1113 void ZonesManager::handleSetActiveZoneCall(const api::ZoneId& zoneId,
1114 api::MethodResultBuilder::Pointer result)
1116 auto handler = [&, this] {
1117 LOGI("SetActiveZone call; Id=" << zoneId.value );
1121 auto iter = findZone(zoneId.value);
1122 if (iter == mZones.end()) {
1123 LOGE("No zone with id=" << zoneId.value);
1124 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1128 if (!get(iter).isRunning()) {
1129 LOGE("Could not activate stopped or paused zone");
1130 result->setError(api::ERROR_ZONE_NOT_RUNNING,
1131 "Could not activate stopped or paused zone");
1135 focusInternal(iter);
1139 tryAddTask(handler, result, true);
1143 void ZonesManager::generateNewConfig(const std::string& id,
1144 const std::string& templatePath)
1146 const std::string dbPrefix = getZoneDbPrefix(id);
1147 ZoneDynamicConfig dynamicConfig;
1148 config::loadFromKVStoreWithJsonFile(mConfig.dbPath, templatePath, dynamicConfig, dbPrefix);
1150 // update mount point path
1151 dynamicConfig.runMountPoint = boost::regex_replace(dynamicConfig.runMountPoint,
1155 if (dynamicConfig.vt >= 0) {
1156 // generate first free VT number
1157 const int freeVT = getVTForNewZone();
1158 LOGD("VT number: " << freeVT);
1159 dynamicConfig.vt = freeVT;
1161 if (!dynamicConfig.ipv4Gateway.empty() && !dynamicConfig.ipv4.empty()) {
1162 // generate third IP octet for network config
1163 std::string thirdOctetStr = std::to_string(ZONE_IP_BASE_THIRD_OCTET + freeVT);
1164 LOGD("IP third octet: " << thirdOctetStr);
1165 dynamicConfig.ipv4Gateway = boost::regex_replace(dynamicConfig.ipv4Gateway,
1166 ZONE_IP_THIRD_OCTET_REGEX,
1168 dynamicConfig.ipv4 = boost::regex_replace(dynamicConfig.ipv4,
1169 ZONE_IP_THIRD_OCTET_REGEX,
1174 // save dynamic config
1175 config::saveToKVStore(mConfig.dbPath, dynamicConfig, dbPrefix);
1177 // save zone template path
1178 ZoneTemplatePathConfig templatePathConfig;
1179 templatePathConfig.zoneTemplatePath = templatePath;
1180 config::saveToKVStore(mConfig.dbPath, templatePathConfig, dbPrefix);
1184 int ZonesManager::getVTForNewZone()
1186 if (mConfig.availableVTs.empty()) {
1189 std::set<int> candidates(mConfig.availableVTs.begin(), mConfig.availableVTs.end());
1191 for (auto& zone : mZones) {
1192 candidates.erase(zone->getVT());
1194 if (candidates.empty()) {
1195 const std::string msg = "No free VT for zone";
1197 throw ZoneOperationException(msg);
1199 // return the smallest
1200 return *candidates.begin();
1203 void ZonesManager::createZone(const std::string& id,
1204 const std::string& templateName)
1206 if (id.empty() || !isalnum(id)) {
1207 const std::string msg = "Failed to add zone - invalid name.";
1209 throw InvalidZoneIdException(msg);
1212 if (find(prohibitedZonesNames.begin(), prohibitedZonesNames.end(), id) != prohibitedZonesNames.end()) {
1213 const std::string msg = "Cannot create " + id + " zone - name is not allowed!";
1215 throw InvalidZoneIdException(msg);
1218 LOGI("Creating zone " << id);
1222 // TODO: This solution is temporary. It utilizes direct access to config files when creating new
1223 // zones. Update this handler when config database will appear.
1224 namespace fs = boost::filesystem;
1226 // check if zone does not exist
1227 if (findZone(id) != mZones.end()) {
1228 const std::string msg = "Cannot create " + id + " zone - already exists!";
1230 throw InvalidZoneIdException(msg);
1233 if (fs::exists(fs::path(mConfig.zonesPath) / id)) {
1234 const std::string msg = "Cannot create " + id + " zone - file system already exists!";
1236 throw InvalidZoneIdException(msg);
1239 const std::string zonePathStr = utils::createFilePath(mConfig.zonesPath, id, "/");
1241 // copy zone image if config contains path to image
1242 LOGT("Image path: " << mConfig.zoneImagePath);
1243 if (!mConfig.zoneImagePath.empty()) {
1244 auto copyImageContentsWrapper = std::bind(&utils::copyImageContents,
1245 mConfig.zoneImagePath,
1248 if (!utils::launchAsRoot(copyImageContentsWrapper)) {
1249 const std::string msg = "Failed to copy zone image.";
1251 throw ZoneOperationException(msg);
1255 auto removeAllWrapper = [](const std::string & path) -> bool {
1257 LOGD("Removing copied data");
1258 fs::remove_all(fs::path(path));
1259 } catch (const std::exception& e) {
1260 LOGW("Failed to remove data: " << boost::diagnostic_information(e));
1265 std::string zoneTemplatePath = utils::createFilePath(mConfig.zoneTemplateDir,
1266 templateName + ".conf");
1269 LOGI("Generating config from " << zoneTemplatePath);
1270 generateNewConfig(id, zoneTemplatePath);
1271 } catch (std::runtime_error& e) {
1272 LOGE("Generate config failed: " << e.what());
1273 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1277 LOGT("Creating new zone");
1279 insertZone(id, zoneTemplatePath);
1280 } catch (std::runtime_error& e) {
1281 LOGE("Creating new zone failed: " << e.what());
1282 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1286 mDynamicConfig.zoneIds.push_back(id);
1287 saveDynamicConfig();
1291 void ZonesManager::handleCreateZoneCall(const api::CreateZoneIn& data,
1292 api::MethodResultBuilder::Pointer result)
1294 auto creator = [&, this] {
1296 createZone(data.first, data.second);
1298 } catch (const InvalidZoneIdException& e) {
1299 result->setError(api::ERROR_INVALID_ID, e.what());
1300 } catch (const std::exception& e) {
1301 result->setError(api::ERROR_INTERNAL, e.what());
1305 tryAddTask(creator, result, true);
1308 void ZonesManager::handleDestroyZoneCall(const api::ZoneId& zoneId,
1309 api::MethodResultBuilder::Pointer result)
1311 auto destroyer = [=] {
1313 LOGI("Destroying zone " << zoneId.value);
1314 destroyZone(zoneId.value);
1315 } catch (const InvalidZoneIdException&) {
1316 LOGE("Failed to destroy zone - no such zone id: " << zoneId.value);
1317 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1318 } catch (const std::runtime_error& e) {
1319 LOGE("Error during zone destruction: " << e.what());
1320 result->setError(api::ERROR_INTERNAL, "Failed to destroy zone");
1326 tryAddTask(destroyer, result, false);
1329 void ZonesManager::handleShutdownZoneCall(const api::ZoneId& zoneId,
1330 api::MethodResultBuilder::Pointer result)
1332 auto shutdown = [=] {
1333 LOGI("ShutdownZone call; Id=" << zoneId.value);
1336 LOGT("Shutdown zone " << zoneId.value);
1339 auto iter = findZone(zoneId.value);
1340 if (iter == mZones.end()) {
1341 LOGE("Failed to shutdown zone - no such zone id: " << zoneId.value);
1342 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1345 get(iter).stop(true);
1348 } catch (ZoneOperationException& e) {
1349 LOGE("Error during zone shutdown: " << e.what());
1350 result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone");
1355 tryAddTask(shutdown, result, false);
1358 void ZonesManager::handleStartZoneCall(const api::ZoneId& zoneId,
1359 api::MethodResultBuilder::Pointer result)
1361 auto startAsync = [=] {
1362 LOGI("StartZone call; Id=" << zoneId.value);
1365 LOGT("Start zone " << zoneId.value);
1368 auto iter = findZone(zoneId.value);
1369 if (iter == mZones.end()) {
1370 LOGE("Failed to start zone - no such zone id: " << zoneId.value);
1371 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1375 focusInternal(iter);
1377 } catch (const std::exception& e) {
1378 LOGE(zoneId.value << ": failed to start: " << e.what());
1379 result->setError(api::ERROR_INTERNAL, "Failed to start zone");
1382 tryAddTask(startAsync, result, false);
1385 void ZonesManager::handleLockZoneCall(const api::ZoneId& zoneId,
1386 api::MethodResultBuilder::Pointer result)
1388 auto handler = [&, this] {
1389 LOGI("LockZone call; Id=" << zoneId.value );
1393 auto iter = findZone(zoneId.value);
1394 if (iter == mZones.end()) {
1395 LOGE("Failed to lock zone - no such zone id: " << zoneId.value);
1396 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1400 Zone& zone = get(iter);
1401 if (!zone.isRunning()) {
1402 LOGE("Zone id=" << zoneId.value << " is not running.");
1403 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1409 zone.goBackground();// make sure it will be in background after unlock
1412 } catch (ZoneOperationException& e) {
1414 result->setError(api::ERROR_INTERNAL, e.what());
1421 tryAddTask(handler, result, true);
1424 void ZonesManager::handleUnlockZoneCall(const api::ZoneId& zoneId,
1425 api::MethodResultBuilder::Pointer result)
1427 auto handler = [&, this] {
1428 LOGI("UnlockZone call; Id=" << zoneId.value );
1432 auto iter = findZone(zoneId.value);
1433 if (iter == mZones.end()) {
1434 LOGE("Failed to unlock zone - no such zone id: " << zoneId.value);
1435 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1439 Zone& zone = get(iter);
1440 if (!zone.isPaused()) {
1441 LOGE("Zone id=" << zoneId.value << " is not paused.");
1442 result->setError(api::ERROR_INVALID_STATE, "Zone is not paused");
1446 LOGT("Unlock zone");
1449 } catch (ZoneOperationException& e) {
1451 result->setError(api::ERROR_INTERNAL, e.what());
1458 tryAddTask(handler, result, true);
1461 void ZonesManager::handleGrantDeviceCall(const api::GrantDeviceIn& data,
1462 api::MethodResultBuilder::Pointer result)
1464 auto handler = [&, this] {
1465 LOGI("GrantDevice call; id=" << data.id << "; dev=" << data.device);
1469 auto iter = findZone(data.id);
1470 if (iter == mZones.end()) {
1471 LOGE("Failed to grant device - no such zone id: " << data.id);
1472 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1476 Zone& zone = get(iter);
1477 if (!zone.isRunning() && !zone.isPaused()) {
1478 LOGE("Zone id=" << data.id << " is not running");
1479 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1483 std::string devicePath = "/dev/" + data.device;
1485 if (!lxc::isDevice(devicePath)) {
1486 LOGE("Failed to grant device - cannot acces device: " << data.device);
1487 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1491 // assume device node is created inside zone
1492 if (!lxc::setDeviceAccess(data.id, devicePath, true, data.flags)) {
1493 LOGE("Failed to grant device: " << data.device << " for zone: " << data.id);
1494 result->setError(api::ERROR_INTERNAL, "Cannot grant device");
1501 tryAddTask(handler, result, true);
1504 void ZonesManager::handleRevokeDeviceCall(const api::RevokeDeviceIn& data,
1505 api::MethodResultBuilder::Pointer result)
1507 auto handler = [&, this] {
1508 LOGI("RevokeDevice call; id=" << data.first << "; dev=" << data.second);
1512 auto iter = findZone(data.first);
1513 if (iter == mZones.end()) {
1514 LOGE("Failed to revoke device - no such zone id: " << data.first);
1515 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1519 Zone& zone = get(iter);
1520 if (!zone.isRunning() && !zone.isPaused()) {
1521 LOGE("Zone id=" << data.first << " is not running");
1522 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1525 std::string devicePath = "/dev/" + data.second;
1527 if (!lxc::isDevice(devicePath)) {
1528 LOGE("Failed to revoke device - cannot acces device: " << data.second);
1529 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1533 if (!lxc::setDeviceAccess(data.first, devicePath, false, 0)) {
1534 LOGE("Failed to revoke device: " << data.second << " for zone: " << data.first);
1535 result->setError(api::ERROR_INTERNAL, "Cannot revoke device");
1542 tryAddTask(handler, result, true);
1545 void ZonesManager::handleCleanUpZonesRootCall(api::MethodResultBuilder::Pointer result)
1547 auto handler = [&, this] {
1548 LOGI("CleanUpZonesRoot call");
1550 std::vector<std::string> zonesIds;
1552 for (const auto& zone : mZones) {
1553 zonesIds.push_back(zone->getId());
1555 cleanUpUnknownsFromRoot(mConfig.zonesPath, zonesIds, false);
1556 } catch (const std::exception& e) {
1557 result->setError(api::ERROR_INTERNAL, e.what());
1562 tryAddTask(handler, result, true);
1565 } // namespace vasum