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/exception/diagnostic_information.hpp>
51 #ifdef USE_BOOST_REGEX
52 #include <boost/regex.hpp>
53 namespace rgx = boost;
65 const std::string HOST_ID = "host";
66 const std::string ENABLED_FILE_NAME = "enabled";
68 const rgx::regex ZONE_NAME_REGEX("~NAME~");
69 const rgx::regex ZONE_IP_THIRD_OCTET_REGEX("~IP~");
71 const unsigned int ZONE_IP_BASE_THIRD_OCTET = 100;
73 const std::vector<std::string> prohibitedZonesNames{
79 void remove(std::vector<T>& v, const T& item)
81 // erase-remove idiom, ask google for explanation
82 v.erase(std::remove(v.begin(), v.end(), item), v.end());
85 template<typename Iter, typename Predicate>
86 Iter circularFindNext(Iter begin, Iter end, Iter current, Predicate pred)
88 if (begin == end || current == end) {
91 for (Iter next = current;;) {
96 if (next == current) {
105 Zone& get(std::vector<std::unique_ptr<Zone>>::iterator iter)
110 bool zoneIsRunning(const std::unique_ptr<Zone>& zone) {
111 return zone->isRunning();
114 bool isalnum(const std::string& str)
116 for (const auto& c : str) {
117 if (!std::isalnum(c)) {
124 void cleanUpUnknownsFromRoot(const boost::filesystem::path& zonesPath,
125 const std::vector<std::string>& zoneIds,
128 namespace fs = boost::filesystem;
129 const auto end = fs::directory_iterator();
131 std::set<std::string> knowns(zoneIds.begin(), zoneIds.end());
132 knowns.insert(prohibitedZonesNames.begin(), prohibitedZonesNames.end());
134 // Remove all directories that start with '.'
135 for (auto zoneDir = fs::directory_iterator(zonesPath); zoneDir != end; ++zoneDir) {
136 if (zoneDir->path().filename().string()[0] == '.') {
138 fs::remove_all(zoneDir->path());
139 LOGI("Remove directory entry: " << *zoneDir);
141 LOGI("Remove directory entry (dry run): " << *zoneDir);
146 for (auto zoneDir = fs::directory_iterator(zonesPath); zoneDir != end; ++zoneDir) {
147 const auto zoneIt = knowns.find(zoneDir->path().filename().string());
148 if (zoneIt == knowns.end()) {
150 const std::string filename = '.' + zoneDir->path().filename().string();
151 fs::path newName = zoneDir->path().parent_path() / filename;
153 fs::rename(zoneDir->path(), newName);
154 fs::remove_all(newName);
155 LOGI("Remove directory entry: " << *zoneDir);
157 LOGI("Remove directory entry (dry run): " << *zoneDir);
166 ZonesManager::ZonesManager(ipc::epoll::EventPoll& eventPoll, const std::string& configPath)
168 , mWorker(utils::Worker::create())
169 , mDetachOnExit(false)
170 , mExclusiveIDLock(INVALID_CONNECTION_ID)
171 , mHostIPCConnection(eventPoll, this)
172 #ifdef DBUS_CONNECTION
173 , mHostDbusConnection(this)
176 LOGD("Instantiating ZonesManager object...");
178 config::loadFromJsonFile(configPath, mConfig);
179 config::loadFromKVStoreWithJsonFile(mConfig.dbPath,
184 if (mConfig.inputConfig.enabled) {
185 LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]");
186 mSwitchingSequenceMonitor.reset(new InputMonitor(eventPoll, mConfig.inputConfig, this));
190 ZonesManager::~ZonesManager()
192 LOGD("Destroying ZonesManager object...");
196 void ZonesManager::start()
200 LOGD("Starting ZonesManager");
204 cleanUpUnknownsFromRoot(mConfig.zonesPath, mDynamicConfig.zoneIds, !mConfig.cleanUpZonesPath);
206 #ifdef DBUS_CONNECTION
207 using namespace std::placeholders;
208 mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
209 mHostDbusConnection.setProxyCallCallback(std::bind(&ZonesManager::handleProxyCall,
210 this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
211 #endif //DBUS_CONNECTION
213 for (const auto& zoneId : mDynamicConfig.zoneIds) {
214 insertZone(zoneId, getTemplatePathForExistingZone(zoneId));
219 LOGD("ZonesManager object instantiated");
221 if (mConfig.inputConfig.enabled) {
222 LOGI("Starting input monitor ");
223 mSwitchingSequenceMonitor->start();
226 // After everything's initialized start to respond to clients' requests
227 mHostIPCConnection.start();
230 void ZonesManager::stop(bool wait)
233 LOGD("Stopping ZonesManager");
239 if (!mDetachOnExit) {
242 } catch (ServerException&) {
243 LOGE("Failed to shutdown all of the zones");
247 // wait for all tasks to complete
249 mHostIPCConnection.stop(wait);
250 if (mConfig.inputConfig.enabled) {
251 LOGI("Stopping input monitor ");
252 mSwitchingSequenceMonitor->stop();
257 bool ZonesManager::isRunning()
260 return mIsRunning || mHostIPCConnection.isRunning();
263 ZonesManager::Zones::iterator ZonesManager::findZone(const std::string& id)
265 return std::find_if(mZones.begin(), mZones.end(), [&id](const std::unique_ptr<Zone>& zone) {
266 return zone->getId() == id;
270 Zone& ZonesManager::getZone(const std::string& id)
272 auto iter = findZone(id);
273 if (iter == mZones.end()) {
274 throw InvalidZoneIdException("Zone id not found");
279 void ZonesManager::saveDynamicConfig()
281 config::saveToKVStore(mConfig.dbPath, mDynamicConfig, getVasumDbPrefix());
284 void ZonesManager::updateDefaultId()
286 // TODO add an api to change defaultId
287 if (mZones.empty() && mDynamicConfig.defaultId.empty()) {
288 LOGT("Keep empty defaultId");
291 if (findZone(mDynamicConfig.defaultId) != mZones.end()) {
292 LOGT("Keep " << mDynamicConfig.defaultId << " as defaultId");
297 if (mZones.empty()) {
298 mDynamicConfig.defaultId.clear();
299 LOGD("DefaultId cleared");
301 mDynamicConfig.defaultId = mZones.front()->getId();
302 LOGD("DefaultId changed to " << mDynamicConfig.defaultId);
307 std::string ZonesManager::getTemplatePathForExistingZone(const std::string& id)
309 ZoneTemplatePathConfig config;
310 config::loadFromKVStore(mConfig.dbPath, config, getZoneDbPrefix(id));
311 return config.zoneTemplatePath;
314 void ZonesManager::insertZone(const std::string& zoneId, const std::string& zoneTemplatePath)
316 if (zoneId == HOST_ID) {
317 throw InvalidZoneIdException("Cannot use reserved zone ID");
319 if (findZone(zoneId) != mZones.end()) {
320 throw InvalidZoneIdException("Zone already exists");
323 LOGT("Creating Zone " << zoneId);
324 std::unique_ptr<Zone> zone(new Zone(zoneId,
328 mConfig.zoneTemplateDir,
329 mConfig.runMountPointPrefix));
331 mZones.push_back(std::move(zone));
333 // after zone is created successfully, put a file informing that zones are enabled
334 if (mZones.size() == 1) {
335 if (!utils::saveFileContent(
336 utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME), "")) {
337 throw ZoneOperationException(ENABLED_FILE_NAME + ": cannot create.");
342 void ZonesManager::tryAddTask(const utils::Worker::Task& task, api::MethodResultBuilder::Pointer result, bool wait)
345 Lock lock(mExclusiveIDMutex);
347 if (mExclusiveIDLock != INVALID_CONNECTION_ID &&
348 mExclusiveIDLock != result->getID()) {
349 result->setError(api::ERROR_QUEUE, "Queue is locked by another client");
355 mWorker->addTaskAndWait(task);
357 mWorker->addTask(task);
361 void ZonesManager::destroyZone(const std::string& zoneId)
365 auto iter = findZone(zoneId);
366 if (iter == mZones.end()) {
367 const std::string msg = "Failed to destroy zone " + zoneId + ": no such zone";
369 throw InvalidZoneIdException(msg);
372 get(iter).setDestroyOnExit();
375 if (mZones.empty()) {
376 if (!utils::removeFile(utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME))) {
377 LOGE("Failed to remove enabled file.");
381 // update dynamic config
382 remove(mDynamicConfig.zoneIds, zoneId);
389 void ZonesManager::focus(const std::string& zoneId)
392 auto iter = findZone(zoneId);
396 void ZonesManager::focusInternal(Zones::iterator iter)
398 // assume mutex is locked
399 if (iter == mZones.end()) {
400 if (!mActiveZoneId.empty()) {
401 if (mConfig.hostVT > 0) {
402 LOGI("Focus to: host");
403 utils::activateVT(mConfig.hostVT);
405 mActiveZoneId.clear();
410 Zone& zoneToFocus = get(iter);
411 const std::string& idToFocus = zoneToFocus.getId();
413 if (idToFocus == mActiveZoneId) {
417 if (!zoneToFocus.isRunning()) {
418 LOGE("Can't focus not running zone " << idToFocus);
423 LOGI("Focus to: " << idToFocus);
425 if (!zoneToFocus.activateVT()) {
426 LOGE("Failed to activate zones VT");
430 for (auto& zone : mZones) {
431 if (zone->isRunning()) {
432 std::string id = zone->getId();
433 if (id == idToFocus) {
434 LOGD(id << ": being sent to foreground");
435 zone->goForeground();
437 LOGD(id << ": being sent to background");
438 zone->goBackground();
442 mActiveZoneId = idToFocus;
445 void ZonesManager::refocus()
447 // assume mutex is locked
449 // check if refocus is required
450 auto oldIter = findZone(mActiveZoneId);
451 if (oldIter != mZones.end() && get(oldIter).isRunning()) {
455 // try to refocus to defaultId
456 auto iter = findZone(mDynamicConfig.defaultId);
457 if (iter == mZones.end() || !get(iter).isRunning()) {
458 // focus to any running or to host if not found
459 iter = std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
464 void ZonesManager::restoreAll()
466 LOGI("Restoring all zones");
470 for (auto& zone : mZones) {
472 // FIXME wait until zone is started and stable
473 // there is problem (with lxc-start) when starting zones too fast
474 // here or in zone.cpp after start
475 std::this_thread::sleep_for(std::chrono::milliseconds(500));
481 void ZonesManager::shutdownAll()
483 LOGI("Stopping all zones");
487 for (auto& zone : mZones) {
494 bool ZonesManager::isPaused(const std::string& zoneId)
497 return getZone(zoneId).isPaused();
500 bool ZonesManager::isRunning(const std::string& zoneId)
503 return getZone(zoneId).isRunning();
506 bool ZonesManager::isStopped(const std::string& zoneId)
509 return getZone(zoneId).isStopped();
512 std::string ZonesManager::getRunningForegroundZoneId()
515 auto iter = getRunningForegroundZoneIterator();
516 return iter == mZones.end() ? std::string() : get(iter).getId();
519 std::string ZonesManager::getNextToForegroundZoneId()
522 auto iter = getNextToForegroundZoneIterator();
523 return iter == mZones.end() ? std::string() : get(iter).getId();
526 ZonesManager::Zones::iterator ZonesManager::getRunningForegroundZoneIterator()
528 // assume mutex is locked
529 if (mActiveZoneId.empty()) {
532 auto iter = findZone(mActiveZoneId);
533 if (!get(iter).isRunning()) {
534 LOGW("Active zone " << mActiveZoneId << " is not running any more!");
540 ZonesManager::Zones::iterator ZonesManager::getNextToForegroundZoneIterator()
542 // assume mutex is locked
543 auto current = findZone(mActiveZoneId);
544 if (current == mZones.end()) {
546 return std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
549 return circularFindNext(mZones.begin(), mZones.end(), current, zoneIsRunning);
553 void ZonesManager::switchingSequenceMonitorNotify()
555 LOGI("switchingSequenceMonitorNotify() called");
559 auto next = getNextToForegroundZoneIterator();
561 if (next != mZones.end()) {
567 void ZonesManager::setZonesDetachOnExit()
571 mDetachOnExit = true;
573 for (auto& zone : mZones) {
574 zone->setDetachOnExit();
578 void ZonesManager::disconnectedCallback(const std::string& id)
580 LOGD("Client Disconnected: " << id);
583 Lock lock(mExclusiveIDMutex);
585 if (mExclusiveIDLock == id) {
586 mExclusiveIDLock = INVALID_CONNECTION_ID;
591 void ZonesManager::handleSwitchToDefaultCall(const std::string& /*caller*/,
592 api::MethodResultBuilder::Pointer result)
594 auto handler = [&, this] {
595 // get config of currently set zone and switch if switchToDefaultAfterTimeout is true
598 auto activeIter = findZone(mActiveZoneId);
599 auto defaultIter = findZone(mDynamicConfig.defaultId);
601 if (activeIter != mZones.end() &&
602 defaultIter != mZones.end() &&
603 get(activeIter).isSwitchToDefaultAfterTimeoutAllowed() &&
604 get(defaultIter).isRunning()) {
606 LOGI("Switching to default zone " << mDynamicConfig.defaultId);
607 focusInternal(defaultIter);
612 tryAddTask(handler, result, true);
615 void ZonesManager::handleCreateFileCall(const api::CreateFileIn& request,
616 api::MethodResultBuilder::Pointer result)
618 auto handler = [&, this] {
619 LOGI("CreateFile call");
623 auto srcIter = findZone(request.id);
624 if (srcIter == mZones.end()) {
625 LOGE("Zone '" << request.id << "' not found");
626 result->setError(api::ERROR_INVALID_ID, "Requested Zone was not found.");
629 Zone& srcZone = get(srcIter);
631 auto retValue = std::make_shared<api::CreateFileOut>();
633 retValue->fd = srcZone.createFile(request.path, request.flags, request.mode);
634 } catch(ZoneOperationException& e) {
635 result->setError(api::ERROR_CREATE_FILE_FAILED, "Unable to create file");
639 result->set(retValue);
642 tryAddTask(handler, result, true);
645 #ifdef DBUS_CONNECTION
646 void ZonesManager::handleProxyCall(const std::string& caller,
647 const std::string& target,
648 const std::string& targetBusName,
649 const std::string& targetObjectPath,
650 const std::string& targetInterface,
651 const std::string& targetMethod,
652 GVariant* parameters,
653 dbus::MethodResultBuilder::Pointer result)
655 auto handler = [&, this] {
656 if (!mProxyCallPolicy->isProxyCallAllowed(caller,
662 LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
663 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
664 result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
668 LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
669 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
671 auto asyncResultCallback = [result](dbus::AsyncMethodCallResult & asyncMethodCallResult) {
673 GVariant* targetResult = asyncMethodCallResult.get();
674 result->set(g_variant_new("(v)", targetResult));
675 } catch (dbus::DbusException& e) {
676 result->setError(api::ERROR_FORWARDED, e.what());
680 if (target != HOST_ID) {
681 result->setError(api::ERROR_INVALID_ID, "Unknown proxy call target");
685 mHostDbusConnection.proxyCallAsync(targetBusName,
690 asyncResultCallback);
693 // This call cannot be locked by lock/unlock queue
694 mWorker->addTaskAndWait(handler);
696 #endif //DBUS_CONNECTION
698 void ZonesManager::handleLockQueueCall(api::MethodResultBuilder::Pointer result)
700 Lock lock(mExclusiveIDMutex);
701 std::string id = result->getID();
703 LOGI("Lock Queue: " << id);
705 if (mExclusiveIDLock == id) {
706 result->setError(api::ERROR_QUEUE, "Queue already locked");
710 if (mExclusiveIDLock != INVALID_CONNECTION_ID) {
711 result->setError(api::ERROR_QUEUE, "Queue locked by another connection");
715 mExclusiveIDLock = id;
719 void ZonesManager::handleUnlockQueueCall(api::MethodResultBuilder::Pointer result)
721 Lock lock(mExclusiveIDMutex);
722 std::string id = result->getID();
724 LOGI("Unlock Queue: " << id);
726 if (mExclusiveIDLock == INVALID_CONNECTION_ID) {
727 result->setError(api::ERROR_QUEUE, "Queue not locked");
731 if (mExclusiveIDLock != id) {
732 result->setError(api::ERROR_QUEUE, "Queue locked by another connection");
736 mExclusiveIDLock = INVALID_CONNECTION_ID;
740 void ZonesManager::handleGetZoneIdsCall(api::MethodResultBuilder::Pointer result)
742 auto handler = [&, this] {
743 LOGI("GetZoneIds call");
747 auto zoneIds = std::make_shared<api::ZoneIds>();
748 for (const auto& zone : mZones) {
749 zoneIds->values.push_back(zone->getId());
752 result->set(zoneIds);
755 // This call cannot be locked by lock/unlock queue
756 mWorker->addTaskAndWait(handler);
759 void ZonesManager::handleGetActiveZoneIdCall(api::MethodResultBuilder::Pointer result)
761 auto handler = [&, this] {
762 LOGI("GetActiveZoneId call");
764 auto zoneId = std::make_shared<api::ZoneId>();
765 zoneId->value = getRunningForegroundZoneId();
769 // This call cannot be locked by lock/unlock queue
770 mWorker->addTaskAndWait(handler);
773 void ZonesManager::handleGetZoneInfoCall(const api::ZoneId& zoneId,
774 api::MethodResultBuilder::Pointer result)
776 auto handler = [&, this] {
777 LOGI("GetZoneInfo call");
781 auto iter = findZone(zoneId.value);
782 if (iter == mZones.end()) {
783 LOGE("No zone with id=" << zoneId.value);
784 result->setError(api::ERROR_INVALID_ID, "No such zone id");
788 Zone& zone = get(iter);
789 auto zoneInfo = std::make_shared<api::ZoneInfoOut>();
791 if (zone.isRunning()) {
792 zoneInfo->state = "RUNNING";
793 } else if (zone.isStopped()) {
794 zoneInfo->state = "STOPPED";
795 } else if (zone.isPaused()) {
796 zoneInfo->state = "FROZEN";
798 LOGE("Unrecognized state of zone id=" << zoneId.value);
799 result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone");
803 zoneInfo->id = zone.getId();
804 zoneInfo->vt = zone.getVT();
805 zoneInfo->rootPath = zone.getRootPath();
806 result->set(zoneInfo);
809 // This call cannot be locked by lock/unlock queue
810 mWorker->addTaskAndWait(handler);
813 void ZonesManager::handleSetNetdevAttrsCall(const api::SetNetDevAttrsIn& data,
814 api::MethodResultBuilder::Pointer result)
816 auto handler = [&, this] {
817 LOGI("SetNetdevAttrs call");
822 // TODO: Use vector<StringPair> instead of tuples
823 std::vector<std::tuple<std::string, std::string>> attrsAsTuples;
824 for(const auto& entry: data.attrs){
825 attrsAsTuples.push_back(std::make_tuple(entry.first, entry.second));
828 getZone(data.id).setNetdevAttrs(data.netDev, attrsAsTuples);
830 } catch (const InvalidZoneIdException&) {
831 LOGE("No zone with id=" << data.id);
832 result->setError(api::ERROR_INVALID_ID, "No such zone id");
833 } catch (const std::runtime_error& ex) {
834 LOGE("Can't set attributes: " << ex.what());
835 result->setError(api::ERROR_INTERNAL, ex.what());
839 tryAddTask(handler, result, true);
842 void ZonesManager::handleGetNetdevAttrsCall(const api::GetNetDevAttrsIn& data,
843 api::MethodResultBuilder::Pointer result)
845 auto handler = [&, this] {
846 LOGI("GetNetdevAttrs call");
850 auto netDevAttrs = std::make_shared<api::GetNetDevAttrs>();
851 const auto attrs = getZone(data.first).getNetdevAttrs(data.second);
853 for (size_t i = 0; i < attrs.size(); ++i) {
854 netDevAttrs->values.push_back({std::get<0>(attrs[i]), std::get<1>(attrs[i])});
856 result->set(netDevAttrs);
857 } catch (const InvalidZoneIdException&) {
858 LOGE("No zone with id=" << data.first);
859 result->setError(api::ERROR_INVALID_ID, "No such zone id");
860 } catch (const std::runtime_error& ex) {
861 LOGE("Can't set attributes: " << ex.what());
862 result->setError(api::ERROR_INTERNAL, ex.what());
866 tryAddTask(handler, result, true);
869 void ZonesManager::handleGetNetdevListCall(const api::ZoneId& zoneId,
870 api::MethodResultBuilder::Pointer result)
872 auto handler = [&, this] {
873 LOGI("GetNetdevList call");
877 auto netDevList = std::make_shared<api::NetDevList>();
878 netDevList->values = getZone(zoneId.value).getNetdevList();
879 result->set(netDevList);
880 } catch (const InvalidZoneIdException&) {
881 LOGE("No zone with id=" << zoneId.value);
882 result->setError(api::ERROR_INVALID_ID, "No such zone id");
883 } catch (const std::runtime_error& ex) {
884 LOGE("Can't set attributes: " << ex.what());
885 result->setError(api::ERROR_INTERNAL, ex.what());
889 tryAddTask(handler, result, true);
892 void ZonesManager::handleCreateNetdevVethCall(const api::CreateNetDevVethIn& data,
893 api::MethodResultBuilder::Pointer result)
895 auto handler = [&, this] {
896 LOGI("CreateNetdevVeth call");
901 getZone(data.id).createNetdevVeth(data.zoneDev, data.hostDev);
903 } catch (const InvalidZoneIdException&) {
904 LOGE("No zone with id=" << data.id);
905 result->setError(api::ERROR_INVALID_ID, "No such zone id");
906 } catch (const std::runtime_error& ex) {
907 LOGE("Can't create veth: " << ex.what());
908 result->setError(api::ERROR_INTERNAL, ex.what());
912 tryAddTask(handler, result, true);
915 void ZonesManager::handleCreateNetdevMacvlanCall(const api::CreateNetDevMacvlanIn& data,
916 api::MethodResultBuilder::Pointer result)
918 auto handler = [&, this] {
919 LOGI("CreateNetdevMacvlan call");
923 getZone(data.id).createNetdevMacvlan(data.zoneDev, data.hostDev, data.mode);
925 } catch (const InvalidZoneIdException&) {
926 LOGE("No zone with id=" << data.id);
927 result->setError(api::ERROR_INVALID_ID, "No such zone id");
928 } catch (const std::runtime_error& ex) {
929 LOGE("Can't create macvlan: " << ex.what());
930 result->setError(api::ERROR_INTERNAL, ex.what());
934 tryAddTask(handler, result, true);
937 void ZonesManager::handleCreateNetdevPhysCall(const api::CreateNetDevPhysIn& data,
938 api::MethodResultBuilder::Pointer result)
940 auto handler = [&, this] {
941 LOGI("CreateNetdevPhys call");
946 getZone(data.first).moveNetdev(data.second);
948 } catch (const InvalidZoneIdException&) {
949 LOGE("No zone with id=" << data.first);
950 result->setError(api::ERROR_INVALID_ID, "No such zone id");
951 } catch (const std::runtime_error& ex) {
952 LOGE("Can't create netdev: " << ex.what());
953 result->setError(api::ERROR_INTERNAL, ex.what());
957 tryAddTask(handler, result, true);
960 void ZonesManager::handleDestroyNetdevCall(const api::DestroyNetDevIn& data,
961 api::MethodResultBuilder::Pointer result)
963 auto handler = [&, this] {
964 LOGI("DestroyNetdev call");
969 getZone(data.first).destroyNetdev(data.second);
971 } catch (const InvalidZoneIdException&) {
972 LOGE("No zone with id=" << data.first);
973 result->setError(api::ERROR_INVALID_ID, "No such zone id");
974 } catch (const std::runtime_error& ex) {
975 LOGE("Can't create netdev: " << ex.what());
976 result->setError(api::ERROR_INTERNAL, ex.what());
980 tryAddTask(handler, result, true);
983 void ZonesManager::handleDeleteNetdevIpAddressCall(const api::DeleteNetdevIpAddressIn& data,
984 api::MethodResultBuilder::Pointer result)
986 auto handler = [&, this] {
987 LOGI("DelNetdevIpAddress call");
991 getZone(data.zone).deleteNetdevIpAddress(data.netdev, data.ip);
993 } catch (const InvalidZoneIdException&) {
994 LOGE("No zone with id=" << data.zone);
995 result->setError(api::ERROR_INVALID_ID, "No such zone id");
996 } catch (const std::runtime_error& ex) {
997 LOGE("Can't delete address: " << ex.what());
998 result->setError(api::ERROR_INTERNAL, ex.what());
1002 tryAddTask(handler, result, true);
1005 void ZonesManager::handleDeclareFileCall(const api::DeclareFileIn& data,
1006 api::MethodResultBuilder::Pointer result)
1008 auto handler = [&, this] {
1009 LOGI("DeclareFile call");
1013 auto declaration = std::make_shared<api::Declaration>();
1014 declaration->value = getZone(data.zone).declareFile(data.type, data.path, data.flags, data.mode);
1015 result->set(declaration);
1016 } catch (const InvalidZoneIdException&) {
1017 LOGE("No zone with id=" << data.zone);
1018 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1019 } catch (const config::ConfigException& ex) {
1020 LOGE("Can't declare file: " << ex.what());
1021 result->setError(api::ERROR_INTERNAL, "Internal error");
1025 tryAddTask(handler, result, true);
1028 void ZonesManager::handleDeclareMountCall(const api::DeclareMountIn& data,
1029 api::MethodResultBuilder::Pointer result)
1031 auto handler = [&, this] {
1032 LOGI("DeclareMount call");
1036 auto declaration = std::make_shared<api::Declaration>();
1037 declaration->value = getZone(data.zone).declareMount(data.source, data.target, data.type, data.flags, data.data);
1038 result->set(declaration);
1039 } catch (const InvalidZoneIdException&) {
1040 LOGE("No zone with id=" << data.zone);
1041 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1042 } catch (const config::ConfigException& ex) {
1043 LOGE("Can't declare mount: " << ex.what());
1044 result->setError(api::ERROR_INTERNAL, "Internal error");
1048 tryAddTask(handler, result, true);
1051 void ZonesManager::handleDeclareLinkCall(const api::DeclareLinkIn& data,
1052 api::MethodResultBuilder::Pointer result)
1054 auto handler = [&, this] {
1055 LOGI("DeclareLink call");
1059 auto declaration = std::make_shared<api::Declaration>();
1060 declaration->value = getZone(data.zone).declareLink(data.source, data.target);
1061 result->set(declaration);
1062 } catch (const InvalidZoneIdException&) {
1063 LOGE("No zone with id=" << data.zone);
1064 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1065 } catch (const config::ConfigException& ex) {
1066 LOGE("Can't declare link: " << ex.what());
1067 result->setError(api::ERROR_INTERNAL, "Internal error");
1071 tryAddTask(handler, result, true);
1074 void ZonesManager::handleGetDeclarationsCall(const api::ZoneId& zoneId,
1075 api::MethodResultBuilder::Pointer result)
1077 auto handler = [&, this] {
1078 LOGI("GetDeclarations call Id=" << zoneId.value);
1082 auto declarations = std::make_shared<api::Declarations>();
1083 declarations->values = getZone(zoneId.value).getDeclarations();
1084 result->set(declarations);
1085 } catch (const InvalidZoneIdException&) {
1086 LOGE("No zone with id=" << zoneId.value);
1087 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1088 } catch (const std::runtime_error& ex) {
1090 result->setError(api::ERROR_INTERNAL, ex.what());
1094 tryAddTask(handler, result, true);
1097 void ZonesManager::handleRemoveDeclarationCall(const api::RemoveDeclarationIn& data,
1098 api::MethodResultBuilder::Pointer result)
1100 auto handler = [&, this] {
1101 LOGI("RemoveDeclaration call Id=" << data.first);
1105 getZone(data.first).removeDeclaration(data.second);
1107 } catch (const InvalidZoneIdException&) {
1108 LOGE("No zone with id=" << data.first);
1109 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1110 } catch (const std::runtime_error& ex) {
1112 result->setError(api::ERROR_INTERNAL, ex.what());
1116 tryAddTask(handler, result, true);
1119 void ZonesManager::handleSetActiveZoneCall(const api::ZoneId& zoneId,
1120 api::MethodResultBuilder::Pointer result)
1122 auto handler = [&, this] {
1123 LOGI("SetActiveZone call; Id=" << zoneId.value );
1127 auto iter = findZone(zoneId.value);
1128 if (iter == mZones.end()) {
1129 LOGE("No zone with id=" << zoneId.value);
1130 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1134 if (!get(iter).isRunning()) {
1135 LOGE("Could not activate stopped or paused zone");
1136 result->setError(api::ERROR_ZONE_NOT_RUNNING,
1137 "Could not activate stopped or paused zone");
1141 focusInternal(iter);
1145 tryAddTask(handler, result, true);
1149 void ZonesManager::generateNewConfig(const std::string& id,
1150 const std::string& templatePath)
1152 const std::string dbPrefix = getZoneDbPrefix(id);
1153 ZoneDynamicConfig dynamicConfig;
1154 config::loadFromKVStoreWithJsonFile(mConfig.dbPath, templatePath, dynamicConfig, dbPrefix);
1156 // update mount point path
1157 dynamicConfig.runMountPoint = rgx::regex_replace(dynamicConfig.runMountPoint,
1161 if (dynamicConfig.vt >= 0) {
1162 // generate first free VT number
1163 const int freeVT = getVTForNewZone();
1164 LOGD("VT number: " << freeVT);
1165 dynamicConfig.vt = freeVT;
1167 if (!dynamicConfig.ipv4Gateway.empty() && !dynamicConfig.ipv4.empty()) {
1168 // generate third IP octet for network config
1169 std::string thirdOctetStr = std::to_string(ZONE_IP_BASE_THIRD_OCTET + freeVT);
1170 LOGD("IP third octet: " << thirdOctetStr);
1171 dynamicConfig.ipv4Gateway = rgx::regex_replace(dynamicConfig.ipv4Gateway,
1172 ZONE_IP_THIRD_OCTET_REGEX,
1174 dynamicConfig.ipv4 = rgx::regex_replace(dynamicConfig.ipv4,
1175 ZONE_IP_THIRD_OCTET_REGEX,
1180 // save dynamic config
1181 config::saveToKVStore(mConfig.dbPath, dynamicConfig, dbPrefix);
1183 // save zone template path
1184 ZoneTemplatePathConfig templatePathConfig;
1185 templatePathConfig.zoneTemplatePath = templatePath;
1186 config::saveToKVStore(mConfig.dbPath, templatePathConfig, dbPrefix);
1190 int ZonesManager::getVTForNewZone()
1192 if (mConfig.availableVTs.empty()) {
1195 std::set<int> candidates(mConfig.availableVTs.begin(), mConfig.availableVTs.end());
1197 for (auto& zone : mZones) {
1198 candidates.erase(zone->getVT());
1200 if (candidates.empty()) {
1201 const std::string msg = "No free VT for zone";
1203 throw ZoneOperationException(msg);
1205 // return the smallest
1206 return *candidates.begin();
1209 void ZonesManager::createZone(const std::string& id,
1210 const std::string& templateName)
1212 if (id.empty() || !isalnum(id)) {
1213 const std::string msg = "Failed to add zone - invalid name.";
1215 throw InvalidZoneIdException(msg);
1218 if (find(prohibitedZonesNames.begin(), prohibitedZonesNames.end(), id) != prohibitedZonesNames.end()) {
1219 const std::string msg = "Cannot create " + id + " zone - name is not allowed!";
1221 throw InvalidZoneIdException(msg);
1224 LOGI("Creating zone " << id);
1228 // TODO: This solution is temporary. It utilizes direct access to config files when creating new
1229 // zones. Update this handler when config database will appear.
1230 namespace fs = boost::filesystem;
1232 // check if zone does not exist
1233 if (findZone(id) != mZones.end()) {
1234 const std::string msg = "Cannot create " + id + " zone - already exists!";
1236 throw InvalidZoneIdException(msg);
1239 if (fs::exists(fs::path(mConfig.zonesPath) / id)) {
1240 const std::string msg = "Cannot create " + id + " zone - file system already exists!";
1242 throw InvalidZoneIdException(msg);
1245 const std::string zonePathStr = utils::createFilePath(mConfig.zonesPath, id, "/");
1247 // copy zone image if config contains path to image
1248 LOGT("Image path: " << mConfig.zoneImagePath);
1249 if (!mConfig.zoneImagePath.empty()) {
1250 auto copyImageContentsWrapper = std::bind(&utils::copyImageContents,
1251 mConfig.zoneImagePath,
1254 if (!utils::launchAsRoot(copyImageContentsWrapper)) {
1255 const std::string msg = "Failed to copy zone image.";
1257 throw ZoneOperationException(msg);
1261 auto removeAllWrapper = [](const std::string & path) -> bool {
1263 LOGD("Removing copied data");
1264 fs::remove_all(fs::path(path));
1265 } catch (const std::exception& e) {
1266 LOGW("Failed to remove data: " << boost::diagnostic_information(e));
1271 std::string zoneTemplatePath = utils::createFilePath(mConfig.zoneTemplateDir,
1272 templateName + ".conf");
1275 LOGI("Generating config from " << zoneTemplatePath);
1276 generateNewConfig(id, zoneTemplatePath);
1277 } catch (std::runtime_error& e) {
1278 LOGE("Generate config failed: " << e.what());
1279 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1283 LOGT("Creating new zone");
1285 insertZone(id, zoneTemplatePath);
1286 } catch (std::runtime_error& e) {
1287 LOGE("Creating new zone failed: " << e.what());
1288 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1292 mDynamicConfig.zoneIds.push_back(id);
1293 saveDynamicConfig();
1297 void ZonesManager::handleCreateZoneCall(const api::CreateZoneIn& data,
1298 api::MethodResultBuilder::Pointer result)
1300 auto creator = [&, this] {
1302 createZone(data.first, data.second);
1304 } catch (const InvalidZoneIdException& e) {
1305 result->setError(api::ERROR_INVALID_ID, e.what());
1306 } catch (const std::exception& e) {
1307 result->setError(api::ERROR_INTERNAL, e.what());
1311 tryAddTask(creator, result, true);
1314 void ZonesManager::handleDestroyZoneCall(const api::ZoneId& zoneId,
1315 api::MethodResultBuilder::Pointer result)
1317 auto destroyer = [=] {
1319 LOGI("Destroying zone " << zoneId.value);
1320 destroyZone(zoneId.value);
1321 } catch (const InvalidZoneIdException&) {
1322 LOGE("Failed to destroy zone - no such zone id: " << zoneId.value);
1323 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1324 } catch (const std::runtime_error& e) {
1325 LOGE("Error during zone destruction: " << e.what());
1326 result->setError(api::ERROR_INTERNAL, "Failed to destroy zone");
1332 tryAddTask(destroyer, result, false);
1335 void ZonesManager::handleShutdownZoneCall(const api::ZoneId& zoneId,
1336 api::MethodResultBuilder::Pointer result)
1338 auto shutdown = [=] {
1339 LOGI("ShutdownZone call; Id=" << zoneId.value);
1342 LOGT("Shutdown zone " << zoneId.value);
1345 auto iter = findZone(zoneId.value);
1346 if (iter == mZones.end()) {
1347 LOGE("Failed to shutdown zone - no such zone id: " << zoneId.value);
1348 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1351 get(iter).stop(true);
1354 } catch (ZoneOperationException& e) {
1355 LOGE("Error during zone shutdown: " << e.what());
1356 result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone");
1361 tryAddTask(shutdown, result, false);
1364 void ZonesManager::handleStartZoneCall(const api::ZoneId& zoneId,
1365 api::MethodResultBuilder::Pointer result)
1367 auto startAsync = [=] {
1368 LOGI("StartZone call; Id=" << zoneId.value);
1371 LOGT("Start zone " << zoneId.value);
1374 auto iter = findZone(zoneId.value);
1375 if (iter == mZones.end()) {
1376 LOGE("Failed to start zone - no such zone id: " << zoneId.value);
1377 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1381 focusInternal(iter);
1383 } catch (const std::exception& e) {
1384 LOGE(zoneId.value << ": failed to start: " << e.what());
1385 result->setError(api::ERROR_INTERNAL, "Failed to start zone");
1388 tryAddTask(startAsync, result, false);
1391 void ZonesManager::handleLockZoneCall(const api::ZoneId& zoneId,
1392 api::MethodResultBuilder::Pointer result)
1394 auto handler = [&, this] {
1395 LOGI("LockZone call; Id=" << zoneId.value );
1399 auto iter = findZone(zoneId.value);
1400 if (iter == mZones.end()) {
1401 LOGE("Failed to lock zone - no such zone id: " << zoneId.value);
1402 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1406 Zone& zone = get(iter);
1407 if (!zone.isRunning()) {
1408 LOGE("Zone id=" << zoneId.value << " is not running.");
1409 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1415 zone.goBackground();// make sure it will be in background after unlock
1418 } catch (ZoneOperationException& e) {
1420 result->setError(api::ERROR_INTERNAL, e.what());
1427 tryAddTask(handler, result, true);
1430 void ZonesManager::handleUnlockZoneCall(const api::ZoneId& zoneId,
1431 api::MethodResultBuilder::Pointer result)
1433 auto handler = [&, this] {
1434 LOGI("UnlockZone call; Id=" << zoneId.value );
1438 auto iter = findZone(zoneId.value);
1439 if (iter == mZones.end()) {
1440 LOGE("Failed to unlock zone - no such zone id: " << zoneId.value);
1441 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1445 Zone& zone = get(iter);
1446 if (!zone.isPaused()) {
1447 LOGE("Zone id=" << zoneId.value << " is not paused.");
1448 result->setError(api::ERROR_INVALID_STATE, "Zone is not paused");
1452 LOGT("Unlock zone");
1455 } catch (ZoneOperationException& e) {
1457 result->setError(api::ERROR_INTERNAL, e.what());
1464 tryAddTask(handler, result, true);
1467 void ZonesManager::handleGrantDeviceCall(const api::GrantDeviceIn& data,
1468 api::MethodResultBuilder::Pointer result)
1470 auto handler = [&, this] {
1471 LOGI("GrantDevice call; id=" << data.id << "; dev=" << data.device);
1475 auto iter = findZone(data.id);
1476 if (iter == mZones.end()) {
1477 LOGE("Failed to grant device - no such zone id: " << data.id);
1478 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1482 Zone& zone = get(iter);
1483 if (!zone.isRunning() && !zone.isPaused()) {
1484 LOGE("Zone id=" << data.id << " is not running");
1485 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1489 std::string devicePath = "/dev/" + data.device;
1491 if (!lxc::isDevice(devicePath)) {
1492 LOGE("Failed to grant device - cannot acces device: " << data.device);
1493 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1497 // assume device node is created inside zone
1498 if (!lxc::setDeviceAccess(data.id, devicePath, true, data.flags)) {
1499 LOGE("Failed to grant device: " << data.device << " for zone: " << data.id);
1500 result->setError(api::ERROR_INTERNAL, "Cannot grant device");
1507 tryAddTask(handler, result, true);
1510 void ZonesManager::handleRevokeDeviceCall(const api::RevokeDeviceIn& data,
1511 api::MethodResultBuilder::Pointer result)
1513 auto handler = [&, this] {
1514 LOGI("RevokeDevice call; id=" << data.first << "; dev=" << data.second);
1518 auto iter = findZone(data.first);
1519 if (iter == mZones.end()) {
1520 LOGE("Failed to revoke device - no such zone id: " << data.first);
1521 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1525 Zone& zone = get(iter);
1526 if (!zone.isRunning() && !zone.isPaused()) {
1527 LOGE("Zone id=" << data.first << " is not running");
1528 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1531 std::string devicePath = "/dev/" + data.second;
1533 if (!lxc::isDevice(devicePath)) {
1534 LOGE("Failed to revoke device - cannot acces device: " << data.second);
1535 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1539 if (!lxc::setDeviceAccess(data.first, devicePath, false, 0)) {
1540 LOGE("Failed to revoke device: " << data.second << " for zone: " << data.first);
1541 result->setError(api::ERROR_INTERNAL, "Cannot revoke device");
1548 tryAddTask(handler, result, true);
1551 void ZonesManager::handleCleanUpZonesRootCall(api::MethodResultBuilder::Pointer result)
1553 auto handler = [&, this] {
1554 LOGI("CleanUpZonesRoot call");
1556 std::vector<std::string> zonesIds;
1558 for (const auto& zone : mZones) {
1559 zonesIds.push_back(zone->getId());
1561 cleanUpUnknownsFromRoot(mConfig.zonesPath, zonesIds, false);
1562 } catch (const std::exception& e) {
1563 result->setError(api::ERROR_INTERNAL, e.what());
1568 tryAddTask(handler, result, true);
1571 } // namespace vasum