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 "host-dbus-definitions.hpp"
28 #include "common-dbus-definitions.hpp"
29 #include "zone-dbus-definitions.hpp"
30 #include "dynamic-config-scheme.hpp"
31 #include "zones-manager.hpp"
32 #include "zone-admin.hpp"
33 #include "lxc/cgroup.hpp"
34 #include "exception.hpp"
36 #include "utils/paths.hpp"
37 #include "logger/logger.hpp"
38 #include "config/manager.hpp"
39 #include "dbus/exception.hpp"
40 #include "utils/fs.hpp"
41 #include "utils/img.hpp"
42 #include "utils/environment.hpp"
43 #include "api/messages.hpp"
45 #include <boost/filesystem.hpp>
46 #include <boost/regex.hpp>
47 #include <boost/exception/diagnostic_information.hpp>
58 bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v)
60 for (const boost::regex& toMatch : v) {
61 if (boost::regex_match(str, toMatch)) {
69 const std::string HOST_ID = "host";
70 const std::string ENABLED_FILE_NAME = "enabled";
72 const boost::regex ZONE_NAME_REGEX("~NAME~");
73 const boost::regex ZONE_IP_THIRD_OCTET_REGEX("~IP~");
75 const unsigned int ZONE_IP_BASE_THIRD_OCTET = 100;
78 void remove(std::vector<T>& v, const T& item)
80 // erase-remove idiom, ask google for explanation
81 v.erase(std::remove(v.begin(), v.end(), item), v.end());
84 template<typename Iter, typename Predicate>
85 Iter circularFindNext(Iter begin, Iter end, Iter current, Predicate pred)
87 if (begin == end || current == end) {
90 for (Iter next = current;;) {
95 if (next == current) {
104 Zone& get(std::vector<std::unique_ptr<Zone>>::iterator iter)
109 bool zoneIsRunning(const std::unique_ptr<Zone>& zone) {
110 return zone->isRunning();
115 ZonesManager::ZonesManager(const std::string& configPath)
116 : mWorker(utils::Worker::create())
117 , mDetachOnExit(false)
119 LOGD("Instantiating ZonesManager object...");
121 config::loadFromJsonFile(configPath, mConfig);
122 config::loadFromKVStoreWithJsonFile(mConfig.dbPath,
127 mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
129 using namespace std::placeholders;
130 mHostConnection.setProxyCallCallback(bind(&ZonesManager::handleProxyCall,
131 this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
133 mHostConnection.setGetZoneDbusesCallback(bind(&ZonesManager::handleGetZoneDbusesCall,
136 mHostConnection.setGetZoneIdsCallback(bind(&ZonesManager::handleGetZoneIdsCall,
139 mHostConnection.setGetActiveZoneIdCallback(bind(&ZonesManager::handleGetActiveZoneIdCall,
142 mHostConnection.setGetZoneInfoCallback(bind(&ZonesManager::handleGetZoneInfoCall,
145 mHostConnection.setSetNetdevAttrsCallback(bind(&ZonesManager::handleSetNetdevAttrsCall,
148 mHostConnection.setGetNetdevAttrsCallback(bind(&ZonesManager::handleGetNetdevAttrsCall,
151 mHostConnection.setGetNetdevListCallback(bind(&ZonesManager::handleGetNetdevListCall,
154 mHostConnection.setCreateNetdevVethCallback(bind(&ZonesManager::handleCreateNetdevVethCall,
157 mHostConnection.setCreateNetdevMacvlanCallback(bind(&ZonesManager::handleCreateNetdevMacvlanCall,
160 mHostConnection.setCreateNetdevPhysCallback(bind(&ZonesManager::handleCreateNetdevPhysCall,
163 mHostConnection.setDestroyNetdevCallback(bind(&ZonesManager::handleDestroyNetdevCall,
166 mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall,
169 mHostConnection.setDeclareMountCallback(bind(&ZonesManager::handleDeclareMountCall,
172 mHostConnection.setDeclareLinkCallback(bind(&ZonesManager::handleDeclareLinkCall,
175 mHostConnection.setGetDeclarationsCallback(bind(&ZonesManager::handleGetDeclarationsCall,
178 mHostConnection.setRemoveDeclarationCallback(bind(&ZonesManager::handleRemoveDeclarationCall,
181 mHostConnection.setSetActiveZoneCallback(bind(&ZonesManager::handleSetActiveZoneCall,
184 mHostConnection.setCreateZoneCallback(bind(&ZonesManager::handleCreateZoneCall,
187 mHostConnection.setDestroyZoneCallback(bind(&ZonesManager::handleDestroyZoneCall,
190 mHostConnection.setShutdownZoneCallback(bind(&ZonesManager::handleShutdownZoneCall,
193 mHostConnection.setStartZoneCallback(bind(&ZonesManager::handleStartZoneCall,
196 mHostConnection.setLockZoneCallback(bind(&ZonesManager::handleLockZoneCall,
199 mHostConnection.setUnlockZoneCallback(bind(&ZonesManager::handleUnlockZoneCall,
202 mHostConnection.setGrantDeviceCallback(bind(&ZonesManager::handleGrantDeviceCall,
205 mHostConnection.setRevokeDeviceCallback(bind(&ZonesManager::handleRevokeDeviceCall,
208 for (const auto& zoneId : mDynamicConfig.zoneIds) {
209 insertZone(zoneId, getTemplatePathForExistingZone(zoneId));
214 LOGD("ZonesManager object instantiated");
216 if (mConfig.inputConfig.enabled) {
217 LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]");
218 mSwitchingSequenceMonitor.reset(
219 new InputMonitor(mConfig.inputConfig,
220 std::bind(&ZonesManager::switchingSequenceMonitorNotify,
227 ZonesManager::~ZonesManager()
229 LOGD("Destroying ZonesManager object...");
231 if (!mDetachOnExit) {
234 } catch (ServerException&) {
235 LOGE("Failed to shutdown all of the zones");
238 // wait for all tasks to complete
241 LOGD("ZonesManager object destroyed");
244 ZonesManager::Zones::iterator ZonesManager::findZone(const std::string& id)
246 return std::find_if(mZones.begin(), mZones.end(), [&id](const std::unique_ptr<Zone>& zone) {
247 return zone->getId() == id;
251 Zone& ZonesManager::getZone(const std::string& id)
253 auto iter = findZone(id);
254 if (iter == mZones.end()) {
255 throw InvalidZoneIdException("Zone id not found");
260 void ZonesManager::saveDynamicConfig()
262 config::saveToKVStore(mConfig.dbPath, mDynamicConfig, getVasumDbPrefix());
265 void ZonesManager::updateDefaultId()
267 // TODO add an api to change defaultId
268 if (mZones.empty() && mDynamicConfig.defaultId.empty()) {
269 LOGT("Keep empty defaultId");
272 if (findZone(mDynamicConfig.defaultId) != mZones.end()) {
273 LOGT("Keep " << mDynamicConfig.defaultId << " as defaultId");
278 if (mZones.empty()) {
279 mDynamicConfig.defaultId.clear();
280 LOGD("DefaultId cleared");
282 mDynamicConfig.defaultId = mZones.front()->getId();
283 LOGD("DefaultId changed to " << mDynamicConfig.defaultId);
288 std::string ZonesManager::getTemplatePathForExistingZone(const std::string& id)
290 ZoneTemplatePathConfig config;
291 config::loadFromKVStore(mConfig.dbPath, config, getZoneDbPrefix(id));
292 return config.zoneTemplatePath;
295 void ZonesManager::insertZone(const std::string& zoneId, const std::string& zoneTemplatePath)
297 if (zoneId == HOST_ID) {
298 throw InvalidZoneIdException("Cannot use reserved zone ID");
300 if (findZone(zoneId) != mZones.end()) {
301 throw InvalidZoneIdException("Zone already exists");
304 LOGT("Creating Zone " << zoneId);
305 std::unique_ptr<Zone> zone(new Zone(mWorker->createSubWorker(),
310 mConfig.lxcTemplatePrefix,
311 mConfig.runMountPointPrefix));
313 using namespace std::placeholders;
314 zone->setNotifyActiveZoneCallback(bind(&ZonesManager::handleNotifyActiveZoneCall,
315 this, zoneId, _1, _2, _3));
317 zone->setDisplayOffCallback(bind(&ZonesManager::handleDisplayOffCall,
320 zone->setFileMoveCallback(bind(&ZonesManager::handleFileMoveCall,
321 this, zoneId, _1, _2, _3));
323 zone->setProxyCallCallback(bind(&ZonesManager::handleProxyCall,
324 this, zoneId, _1, _2, _3, _4, _5, _6, _7));
326 zone->setDbusStateChangedCallback(bind(&ZonesManager::handleDbusStateChanged,
329 mZones.push_back(std::move(zone));
331 // after zone is created successfully, put a file informing that zones are enabled
332 if (mZones.size() == 1) {
333 if (!utils::saveFileContent(
334 utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME), "")) {
335 throw ZoneOperationException(ENABLED_FILE_NAME + ": cannot create.");
340 void ZonesManager::destroyZone(const std::string& zoneId)
344 auto iter = findZone(zoneId);
345 if (iter == mZones.end()) {
346 LOGE("Failed to destroy zone " << zoneId << ": no such zone");
347 throw InvalidZoneIdException("No such zone");
350 get(iter).setDestroyOnExit();
353 if (mZones.empty()) {
354 if (!utils::removeFile(utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME))) {
355 LOGE("Failed to remove enabled file.");
359 // update dynamic config
360 remove(mDynamicConfig.zoneIds, zoneId);
367 void ZonesManager::focus(const std::string& zoneId)
370 auto iter = findZone(zoneId);
374 void ZonesManager::focusInternal(Zones::iterator iter)
376 // assume mutex is locked
377 if (iter == mZones.end()) {
378 if (!mActiveZoneId.empty()) {
379 LOGI("Focus to: host");
380 // give back the focus to the host
381 // TODO switch to host vt
382 mActiveZoneId.clear();
387 Zone& zoneToFocus = get(iter);
388 const std::string idToFocus = zoneToFocus.getId();
390 if (idToFocus == mActiveZoneId) {
394 if (!zoneToFocus.isRunning()) {
395 LOGE("Can't focus not running zone " << idToFocus);
400 LOGI("Focus to: " << idToFocus);
402 if (!zoneToFocus.activateVT()) {
403 LOGE("Failed to activate zones VT");
407 for (auto& zone : mZones) {
408 if (zone->isRunning()) {
409 std::string id = zone->getId();
410 if (id == idToFocus) {
411 LOGD(id << ": being sent to foreground");
412 zone->goForeground();
414 LOGD(id << ": being sent to background");
415 zone->goBackground();
419 mActiveZoneId = idToFocus;
422 void ZonesManager::refocus()
424 // assume mutex is locked
426 // check if refocus is required
427 auto oldIter = findZone(mActiveZoneId);
428 if (oldIter != mZones.end() && get(oldIter).isRunning()) {
432 // try to refocus to defaultId
433 auto iter = findZone(mDynamicConfig.defaultId);
434 if (iter == mZones.end() || !get(iter).isRunning()) {
435 // focus to any running or to host if not found
436 iter = std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
441 void ZonesManager::restoreAll()
443 LOGI("Restoring all zones");
447 for (auto& zone : mZones) {
454 void ZonesManager::shutdownAll()
456 LOGI("Stopping all zones");
460 for (auto& zone : mZones) {
467 bool ZonesManager::isPaused(const std::string& zoneId)
470 return getZone(zoneId).isPaused();
473 bool ZonesManager::isRunning(const std::string& zoneId)
476 return getZone(zoneId).isRunning();
479 bool ZonesManager::isStopped(const std::string& zoneId)
482 return getZone(zoneId).isStopped();
485 std::string ZonesManager::getRunningForegroundZoneId()
488 auto iter = getRunningForegroundZoneIterator();
489 return iter == mZones.end() ? std::string() : get(iter).getId();
492 std::string ZonesManager::getNextToForegroundZoneId()
495 auto iter = getNextToForegroundZoneIterator();
496 return iter == mZones.end() ? std::string() : get(iter).getId();
499 ZonesManager::Zones::iterator ZonesManager::getRunningForegroundZoneIterator()
501 // assume mutex is locked
502 if (mActiveZoneId.empty()) {
505 auto iter = findZone(mActiveZoneId);
506 if (!get(iter).isRunning()) {
507 // Can zone change its state by itself?
508 // Maybe when it is shut down by itself? TODO check it
509 LOGW("Active zone " << mActiveZoneId << " is not running any more!");
516 ZonesManager::Zones::iterator ZonesManager::getNextToForegroundZoneIterator()
518 // assume mutex is locked
519 auto current = findZone(mActiveZoneId);
520 if (current == mZones.end()) {
522 return std::find_if(mZones.begin(), mZones.end(), zoneIsRunning);
525 return circularFindNext(mZones.begin(), mZones.end(), current, zoneIsRunning);
529 void ZonesManager::switchingSequenceMonitorNotify()
531 LOGI("switchingSequenceMonitorNotify() called");
535 auto next = getNextToForegroundZoneIterator();
537 if (next != mZones.end()) {
543 void ZonesManager::setZonesDetachOnExit()
547 mDetachOnExit = true;
549 for (auto& zone : mZones) {
550 zone->setDetachOnExit();
554 void ZonesManager::handleNotifyActiveZoneCall(const std::string& caller,
555 const std::string& application,
556 const std::string& message,
557 api::MethodResultBuilder::Pointer result)
559 LOGI("handleNotifyActiveZoneCall(" << caller << ", " << application << ", " << message
565 auto iter = getRunningForegroundZoneIterator();
566 if (iter != mZones.end() && caller != get(iter).getId()) {
567 get(iter).sendNotification(caller, application, message);
570 } catch (const VasumException&) {
571 LOGE("Notification from " << caller << " hasn't been sent");
572 result->setError(api::ERROR_INTERNAL, "Notification hasn't been sent");
576 void ZonesManager::handleDisplayOffCall(const std::string& /*caller*/)
578 // get config of currently set zone and switch if switchToDefaultAfterTimeout is true
581 auto activeIter = findZone(mActiveZoneId);
582 auto defaultIter = findZone(mDynamicConfig.defaultId);
584 if (activeIter != mZones.end() &&
585 defaultIter != mZones.end() &&
586 get(activeIter).isSwitchToDefaultAfterTimeoutAllowed() &&
587 get(defaultIter).isRunning()) {
589 LOGI("Switching to default zone " << mDynamicConfig.defaultId);
590 focusInternal(defaultIter);
594 void ZonesManager::handleFileMoveCall(const std::string& srcZoneId,
595 const std::string& dstZoneId,
596 const std::string& path,
597 api::MethodResultBuilder::Pointer result)
599 // TODO: this implementation is only a placeholder.
600 // There are too many unanswered questions and security concerns:
601 // 1. What about mount namespace, host might not see the source/destination
602 // file. The file might be a different file from a host perspective.
603 // 2. Copy vs move (speed and security concerns over already opened FDs)
604 // 3. Access to source and destination files - DAC, uid/gig
605 // 4. Access to source and destintation files - MAC, smack
606 // 5. Destination file uid/gid assignment
607 // 6. Destination file smack label assignment
608 // 7. Verifiability of the source path
610 // NOTE: other possible implementations include:
611 // 1. Sending file descriptors opened directly in each zone through DBUS
612 // using something like g_dbus_message_set_unix_fd_list()
613 // 2. VSM forking and calling setns(MNT) in each zone and opening files
614 // by itself, then passing FDs to the main process
615 // Now when the main process has obtained FDs (by either of those methods)
616 // it can do the copying by itself.
618 LOGI("File move requested\n"
619 << "src: " << srcZoneId << "\n"
620 << "dst: " << dstZoneId << "\n"
621 << "path: " << path);
625 auto srcIter = findZone(srcZoneId);
626 if (srcIter == mZones.end()) {
627 LOGE("Source zone '" << srcZoneId << "' not found");
630 Zone& srcZone = get(srcIter);
632 auto status = std::make_shared<api::FileMoveRequestStatus>();
634 auto dstIter = findZone(dstZoneId);
635 if (dstIter == mZones.end()) {
636 LOGE("Destination zone '" << dstZoneId << "' not found");
637 status->value = api::zone::FILE_MOVE_DESTINATION_NOT_FOUND;
641 Zone& dstContanier = get(dstIter);
643 if (srcZoneId == dstZoneId) {
644 LOGE("Cannot send a file to yourself");
645 status->value = api::zone::FILE_MOVE_WRONG_DESTINATION;
650 if (!regexMatchVector(path, srcZone.getPermittedToSend())) {
651 LOGE("Source zone has no permissions to send the file: " << path);
652 status->value = api::zone::FILE_MOVE_NO_PERMISSIONS_SEND;
657 if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
658 LOGE("Destination zone has no permissions to receive the file: " << path);
659 status->value = api::zone::FILE_MOVE_NO_PERMISSIONS_RECEIVE;
664 namespace fs = boost::filesystem;
665 std::string srcPath = fs::absolute(srcZoneId, mConfig.zonesPath).string() + path;
666 std::string dstPath = fs::absolute(dstZoneId, mConfig.zonesPath).string() + path;
668 if (!utils::moveFile(srcPath, dstPath)) {
669 LOGE("Failed to move the file: " << path);
670 status->value = api::zone::FILE_MOVE_FAILED;
673 status->value = api::zone::FILE_MOVE_SUCCEEDED;
676 dstContanier.sendNotification(srcZoneId, path, api::zone::FILE_MOVE_SUCCEEDED);
677 } catch (ServerException&) {
678 LOGE("Notification to '" << dstZoneId << "' has not been sent");
683 void ZonesManager::handleProxyCall(const std::string& caller,
684 const std::string& target,
685 const std::string& targetBusName,
686 const std::string& targetObjectPath,
687 const std::string& targetInterface,
688 const std::string& targetMethod,
689 GVariant* parameters,
690 dbus::MethodResultBuilder::Pointer result)
692 if (!mProxyCallPolicy->isProxyCallAllowed(caller,
698 LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
699 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
700 result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
704 LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
705 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
707 auto asyncResultCallback = [result](dbus::AsyncMethodCallResult & asyncMethodCallResult) {
709 GVariant* targetResult = asyncMethodCallResult.get();
710 result->set(g_variant_new("(v)", targetResult));
711 } catch (dbus::DbusException& e) {
712 result->setError(api::ERROR_FORWARDED, e.what());
716 if (target == HOST_ID) {
717 mHostConnection.proxyCallAsync(targetBusName,
722 asyncResultCallback);
728 auto targetIter = findZone(target);
729 if (targetIter == mZones.end()) {
730 LOGE("Target zone '" << target << "' not found");
731 result->setError(api::ERROR_INVALID_ID, "Unknown proxy call target");
735 Zone& targetZone = get(targetIter);
736 targetZone.proxyCallAsync(targetBusName,
741 asyncResultCallback);
744 void ZonesManager::handleGetZoneDbusesCall(api::MethodResultBuilder::Pointer result)
748 auto dbuses = std::make_shared<api::Dbuses>();
749 for (auto& zone : mZones) {
750 dbuses->values.push_back({zone->getId(), zone->getDbusAddress()});
755 void ZonesManager::handleDbusStateChanged(const std::string& zoneId,
756 const std::string& dbusAddress)
758 mHostConnection.signalZoneDbusState(zoneId, dbusAddress);
761 void ZonesManager::handleGetZoneIdsCall(api::MethodResultBuilder::Pointer result)
765 auto zoneIds = std::make_shared<api::ZoneIds>();
766 for (const auto& zone : mZones) {
767 zoneIds->values.push_back(zone->getId());
770 result->set(zoneIds);
773 void ZonesManager::handleGetActiveZoneIdCall(api::MethodResultBuilder::Pointer result)
775 LOGI("GetActiveZoneId call");
776 auto zoneId = std::make_shared<api::ZoneId>();
777 zoneId->value = getRunningForegroundZoneId();
781 void ZonesManager::handleGetZoneInfoCall(const api::ZoneId& zoneId,
782 api::MethodResultBuilder::Pointer result)
784 LOGI("GetZoneInfo call");
788 auto iter = findZone(zoneId.value);
789 if (iter == mZones.end()) {
790 LOGE("No zone with id=" << zoneId.value);
791 result->setError(api::ERROR_INVALID_ID, "No such zone id");
795 Zone& zone = get(iter);
796 auto zoneInfo = std::make_shared<api::ZoneInfoOut>();
798 if (zone.isRunning()) {
799 zoneInfo->state = "RUNNING";
800 } else if (zone.isStopped()) {
801 zoneInfo->state = "STOPPED";
802 } else if (zone.isPaused()) {
803 zoneInfo->state = "FROZEN";
805 LOGE("Unrecognized state of zone id=" << zoneId.value);
806 result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone");
810 result->set(zoneInfo);
813 void ZonesManager::handleSetNetdevAttrsCall(const api::SetNetDevAttrsIn& data,
814 api::MethodResultBuilder::Pointer result)
816 LOGI("SetNetdevAttrs call");
820 // TODO: Use vector<StringPair> instead of tuples
821 std::vector<std::tuple<std::string, std::string>> attrsAsTuples;
822 for(const auto& entry: data.attrs){
823 attrsAsTuples.push_back(std::make_tuple(entry.first, entry.second));
826 getZone(data.id).setNetdevAttrs(data.netDev, attrsAsTuples);
828 } catch (const InvalidZoneIdException&) {
829 LOGE("No zone with id=" << data.id);
830 result->setError(api::ERROR_INVALID_ID, "No such zone id");
831 } catch (const VasumException& ex) {
832 LOGE("Can't set attributes: " << ex.what());
833 result->setError(api::ERROR_INTERNAL, ex.what());
837 void ZonesManager::handleGetNetdevAttrsCall(const api::GetNetDevAttrsIn& data,
838 api::MethodResultBuilder::Pointer result)
840 LOGI("GetNetdevAttrs call");
843 auto netDevAttrs = std::make_shared<api::GetNetDevAttrs>();
844 const auto attrs = getZone(data.first).getNetdevAttrs(data.second);
846 for (size_t i = 0; i < attrs.size(); ++i) {
847 netDevAttrs->values.push_back({std::get<0>(attrs[i]), std::get<1>(attrs[i])});
849 result->set(netDevAttrs);
850 } catch (const InvalidZoneIdException&) {
851 LOGE("No zone with id=" << data.first);
852 result->setError(api::ERROR_INVALID_ID, "No such zone id");
853 } catch (const VasumException& ex) {
854 LOGE("Can't set attributes: " << ex.what());
855 result->setError(api::ERROR_INTERNAL, ex.what());
859 void ZonesManager::handleGetNetdevListCall(const api::ZoneId& zoneId,
860 api::MethodResultBuilder::Pointer result)
862 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 VasumException& ex) {
872 LOGE("Can't set attributes: " << ex.what());
873 result->setError(api::ERROR_INTERNAL, ex.what());
877 void ZonesManager::handleCreateNetdevVethCall(const api::CreateNetDevVethIn& data,
878 api::MethodResultBuilder::Pointer result)
880 LOGI("CreateNetdevVeth call");
884 getZone(data.id).createNetdevVeth(data.zoneDev, data.hostDev);
886 } catch (const InvalidZoneIdException&) {
887 LOGE("No zone with id=" << data.id);
888 result->setError(api::ERROR_INVALID_ID, "No such zone id");
889 } catch (const VasumException& ex) {
890 LOGE("Can't create veth: " << ex.what());
891 result->setError(api::ERROR_INTERNAL, ex.what());
895 void ZonesManager::handleCreateNetdevMacvlanCall(const api::CreateNetDevMacvlanIn& data,
896 api::MethodResultBuilder::Pointer result)
898 LOGI("CreateNetdevMacvlan call");
901 getZone(data.id).createNetdevMacvlan(data.zoneDev, data.hostDev, data.mode);
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 VasumException& ex) {
907 LOGE("Can't create macvlan: " << ex.what());
908 result->setError(api::ERROR_INTERNAL, ex.what());
912 void ZonesManager::handleCreateNetdevPhysCall(const api::CreateNetDevPhysIn& data,
913 api::MethodResultBuilder::Pointer result)
915 LOGI("CreateNetdevPhys call");
919 getZone(data.first).moveNetdev(data.second);
921 } catch (const InvalidZoneIdException&) {
922 LOGE("No zone with id=" << data.first);
923 result->setError(api::ERROR_INVALID_ID, "No such zone id");
924 } catch (const VasumException& ex) {
925 LOGE("Can't create netdev: " << ex.what());
926 result->setError(api::ERROR_INTERNAL, ex.what());
930 void ZonesManager::handleDestroyNetdevCall(const api::DestroyNetDevIn& data,
931 api::MethodResultBuilder::Pointer result)
933 LOGI("DestroyNetdev call");
937 getZone(data.first).destroyNetdev(data.second);
939 } catch (const InvalidZoneIdException&) {
940 LOGE("No zone with id=" << data.first);
941 result->setError(api::ERROR_INVALID_ID, "No such zone id");
942 } catch (const VasumException& ex) {
943 LOGE("Can't create netdev: " << ex.what());
944 result->setError(api::ERROR_INTERNAL, ex.what());
948 void ZonesManager::handleDeclareFileCall(const api::DeclareFileIn& data,
949 api::MethodResultBuilder::Pointer result)
951 LOGI("DeclareFile call");
955 auto declaration = std::make_shared<api::Declaration>();
956 declaration->value = getZone(data.zone).declareFile(data.type, data.path, data.flags, data.mode);
957 result->set(declaration);
958 } catch (const InvalidZoneIdException&) {
959 LOGE("No zone with id=" << data.zone);
960 result->setError(api::ERROR_INVALID_ID, "No such zone id");
961 } catch (const config::ConfigException& ex) {
962 LOGE("Can't declare file: " << ex.what());
963 result->setError(api::ERROR_INTERNAL, "Internal error");
967 void ZonesManager::handleDeclareMountCall(const api::DeclareMountIn& data,
968 api::MethodResultBuilder::Pointer result)
970 LOGI("DeclareMount call");
974 auto declaration = std::make_shared<api::Declaration>();
975 declaration->value = getZone(data.zone).declareMount(data.source, data.target, data.type, data.flags, data.data);
976 result->set(declaration);
977 } catch (const InvalidZoneIdException&) {
978 LOGE("No zone with id=" << data.zone);
979 result->setError(api::ERROR_INVALID_ID, "No such zone id");
980 } catch (const config::ConfigException& ex) {
981 LOGE("Can't declare mount: " << ex.what());
982 result->setError(api::ERROR_INTERNAL, "Internal error");
986 void ZonesManager::handleDeclareLinkCall(const api::DeclareLinkIn& data,
987 api::MethodResultBuilder::Pointer result)
989 LOGI("DeclareLink call");
992 auto declaration = std::make_shared<api::Declaration>();
993 declaration->value = getZone(data.zone).declareLink(data.source, data.target);
994 result->set(declaration);
995 } catch (const InvalidZoneIdException&) {
996 LOGE("No zone with id=" << data.zone);
997 result->setError(api::ERROR_INVALID_ID, "No such zone id");
998 } catch (const config::ConfigException& ex) {
999 LOGE("Can't declare link: " << ex.what());
1000 result->setError(api::ERROR_INTERNAL, "Internal error");
1004 void ZonesManager::handleGetDeclarationsCall(const api::ZoneId& zoneId,
1005 api::MethodResultBuilder::Pointer result)
1007 LOGI("GetDeclarations call Id=" << zoneId.value);
1010 auto declarations = std::make_shared<api::Declarations>();
1011 declarations->values = getZone(zoneId.value).getDeclarations();
1012 result->set(declarations);
1013 } catch (const InvalidZoneIdException&) {
1014 LOGE("No zone with id=" << zoneId.value);
1015 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1016 } catch (const VasumException& ex) {
1018 result->setError(api::ERROR_INTERNAL, ex.what());
1022 void ZonesManager::handleRemoveDeclarationCall(const api::RemoveDeclarationIn& data,
1023 api::MethodResultBuilder::Pointer result)
1025 LOGI("RemoveDeclaration call Id=" << data.first);
1028 getZone(data.first).removeDeclaration(data.second);
1030 } catch (const InvalidZoneIdException&) {
1031 LOGE("No zone with id=" << data.first);
1032 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1033 } catch (const VasumException& ex) {
1035 result->setError(api::ERROR_INTERNAL, ex.what());
1039 void ZonesManager::handleSetActiveZoneCall(const api::ZoneId& zoneId,
1040 api::MethodResultBuilder::Pointer result)
1042 LOGI("SetActiveZone call; Id=" << zoneId.value );
1046 auto iter = findZone(zoneId.value);
1047 if (iter == mZones.end()) {
1048 LOGE("No zone with id=" << zoneId.value);
1049 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1053 if (!get(iter).isRunning()) {
1054 LOGE("Could not activate stopped or paused zone");
1055 result->setError(api::host::ERROR_ZONE_NOT_RUNNING,
1056 "Could not activate stopped or paused zone");
1060 focusInternal(iter);
1065 void ZonesManager::generateNewConfig(const std::string& id,
1066 const std::string& templatePath)
1068 const std::string dbPrefix = getZoneDbPrefix(id);
1069 ZoneDynamicConfig dynamicConfig;
1070 config::loadFromKVStoreWithJsonFile(mConfig.dbPath, templatePath, dynamicConfig, dbPrefix);
1072 // update mount point path
1073 dynamicConfig.runMountPoint = boost::regex_replace(dynamicConfig.runMountPoint,
1077 // generate first free VT number
1078 const int freeVT = getVTForNewZone();
1079 LOGD("VT number: " << freeVT);
1080 dynamicConfig.vt = freeVT;
1082 // generate third IP octet for network config
1083 std::string thirdOctetStr = std::to_string(ZONE_IP_BASE_THIRD_OCTET + freeVT);
1084 LOGD("IP third octet: " << thirdOctetStr);
1085 dynamicConfig.ipv4Gateway = boost::regex_replace(dynamicConfig.ipv4Gateway,
1086 ZONE_IP_THIRD_OCTET_REGEX,
1088 dynamicConfig.ipv4 = boost::regex_replace(dynamicConfig.ipv4,
1089 ZONE_IP_THIRD_OCTET_REGEX,
1092 // save dynamic config
1093 config::saveToKVStore(mConfig.dbPath, dynamicConfig, dbPrefix);
1095 // save zone template path
1096 ZoneTemplatePathConfig templatePathConfig;
1097 templatePathConfig.zoneTemplatePath = templatePath;
1098 config::saveToKVStore(mConfig.dbPath, templatePathConfig, dbPrefix);
1102 int ZonesManager::getVTForNewZone()
1104 if (mConfig.availableVTs.empty()) {
1107 std::set<int> candidates(mConfig.availableVTs.begin(), mConfig.availableVTs.end());
1109 for (auto& zone : mZones) {
1110 candidates.erase(zone->getVT());
1112 if (candidates.empty()) {
1113 LOGE("No free VT for zone");
1114 throw ZoneOperationException("No free VT for zone");
1116 // return the smallest
1117 return *candidates.begin();
1120 void ZonesManager::createZone(const std::string& id,
1121 const std::string& templateName)
1123 if (id.empty()) { // TODO validate id (no spaces, slashes etc)
1124 LOGE("Failed to add zone - invalid name.");
1125 throw InvalidZoneIdException("Invalid name");
1128 LOGI("Creating zone " << id);
1132 // TODO: This solution is temporary. It utilizes direct access to config files when creating new
1133 // zones. Update this handler when config database will appear.
1134 namespace fs = boost::filesystem;
1136 // check if zone does not exist
1137 if (findZone(id) != mZones.end()) {
1138 LOGE("Cannot create " << id << " zone - already exists!");
1139 throw InvalidZoneIdException("Already exists");
1142 const std::string zonePathStr = utils::createFilePath(mConfig.zonesPath, id, "/");
1144 // copy zone image if config contains path to image
1145 LOGT("Image path: " << mConfig.zoneImagePath);
1146 if (!mConfig.zoneImagePath.empty()) {
1147 auto copyImageContentsWrapper = std::bind(&utils::copyImageContents,
1148 mConfig.zoneImagePath,
1151 if (!utils::launchAsRoot(copyImageContentsWrapper)) {
1152 LOGE("Failed to copy zone image.");
1153 throw ZoneOperationException("Failed to copy zone image.");
1157 auto removeAllWrapper = [](const std::string & path) -> bool {
1159 LOGD("Removing copied data");
1160 fs::remove_all(fs::path(path));
1161 } catch (const std::exception& e) {
1162 LOGW("Failed to remove data: " << boost::diagnostic_information(e));
1167 std::string zoneTemplatePath = utils::createFilePath(mConfig.zoneTemplateDir,
1168 templateName + ".conf");
1171 LOGI("Generating config from " << zoneTemplatePath);
1172 generateNewConfig(id, zoneTemplatePath);
1173 } catch (VasumException& e) {
1174 LOGE("Generate config failed: " << e.what());
1175 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1179 LOGT("Creating new zone");
1181 insertZone(id, zoneTemplatePath);
1182 } catch (VasumException& e) {
1183 LOGE("Creating new zone failed: " << e.what());
1184 utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
1188 mDynamicConfig.zoneIds.push_back(id);
1189 saveDynamicConfig();
1193 void ZonesManager::handleCreateZoneCall(const api::CreateZoneIn& data,
1194 api::MethodResultBuilder::Pointer result)
1197 createZone(data.first, data.second);
1199 } catch (const InvalidZoneIdException& e) {
1200 result->setError(api::ERROR_INVALID_ID, "Existing or invalid zone id");
1201 } catch (const VasumException& e) {
1202 result->setError(api::ERROR_INTERNAL, "Failed to create zone");
1206 void ZonesManager::handleDestroyZoneCall(const api::ZoneId& zoneId,
1207 api::MethodResultBuilder::Pointer result)
1209 auto destroyer = [zoneId, result, this] {
1211 LOGI("Destroying zone " << zoneId.value);
1212 destroyZone(zoneId.value);
1213 } catch (const InvalidZoneIdException&) {
1214 LOGE("Failed to destroy zone - no such zone id: " << zoneId.value);
1215 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1216 } catch (const VasumException& e) {
1217 LOGE("Error during zone destruction: " << e.what());
1218 result->setError(api::ERROR_INTERNAL, "Failed to destroy zone");
1224 mWorker->addTask(destroyer);
1227 void ZonesManager::handleShutdownZoneCall(const api::ZoneId& zoneId,
1228 api::MethodResultBuilder::Pointer result)
1230 LOGI("ShutdownZone call; Id=" << zoneId.value);
1232 auto shutdown = [zoneId, result, this] {
1234 LOGT("Shutdown zone " << zoneId.value);
1237 auto iter = findZone(zoneId.value);
1238 if (iter == mZones.end()) {
1239 LOGE("Failed to shutdown zone - no such zone id: " << zoneId.value);
1240 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1243 get(iter).stop(true);
1246 } catch (ZoneOperationException& e) {
1247 LOGE("Error during zone shutdown: " << e.what());
1248 result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone");
1253 mWorker->addTask(shutdown);
1256 void ZonesManager::handleStartZoneCall(const api::ZoneId& zoneId,
1257 api::MethodResultBuilder::Pointer result)
1259 LOGI("StartZone call; Id=" << zoneId.value);
1261 auto startAsync = [this, zoneId, result]() {
1263 LOGT("Start zone " << zoneId.value);
1266 auto iter = findZone(zoneId.value);
1267 if (iter == mZones.end()) {
1268 LOGE("Failed to start zone - no such zone id: " << zoneId.value);
1269 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1273 focusInternal(iter);
1275 } catch (const std::exception& e) {
1276 LOGE(zoneId.value << ": failed to start: " << e.what());
1277 result->setError(api::ERROR_INTERNAL, "Failed to start zone");
1280 mWorker->addTask(startAsync);
1283 void ZonesManager::handleLockZoneCall(const api::ZoneId& zoneId,
1284 api::MethodResultBuilder::Pointer result)
1286 LOGI("LockZone call; Id=" << zoneId.value );
1290 auto iter = findZone(zoneId.value);
1291 if (iter == mZones.end()) {
1292 LOGE("Failed to lock zone - no such zone id: " << zoneId.value);
1293 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1297 Zone& zone = get(iter);
1298 if (!zone.isRunning()) {
1299 LOGE("Zone id=" << zoneId.value << " is not running.");
1300 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1306 zone.goBackground();// make sure it will be in background after unlock
1309 } catch (ZoneOperationException& e) {
1311 result->setError(api::ERROR_INTERNAL, e.what());
1318 void ZonesManager::handleUnlockZoneCall(const api::ZoneId& zoneId,
1319 api::MethodResultBuilder::Pointer result)
1321 LOGI("UnlockZone call; Id=" << zoneId.value );
1325 auto iter = findZone(zoneId.value);
1326 if (iter == mZones.end()) {
1327 LOGE("Failed to unlock zone - no such zone id: " << zoneId.value);
1328 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1332 Zone& zone = get(iter);
1333 if (!zone.isPaused()) {
1334 LOGE("Zone id=" << zoneId.value << " is not paused.");
1335 result->setError(api::ERROR_INVALID_STATE, "Zone is not paused");
1339 LOGT("Unlock zone");
1342 } catch (ZoneOperationException& e) {
1344 result->setError(api::ERROR_INTERNAL, e.what());
1351 void ZonesManager::handleGrantDeviceCall(const api::GrantDeviceIn& data,
1352 api::MethodResultBuilder::Pointer result)
1354 LOGI("GrantDevice call; id=" << data.id << "; dev=" << data.device);
1358 auto iter = findZone(data.id);
1359 if (iter == mZones.end()) {
1360 LOGE("Failed to grant device - no such zone id: " << data.id);
1361 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1365 Zone& zone = get(iter);
1366 if (!zone.isRunning() && !zone.isPaused()) {
1367 LOGE("Zone id=" << data.id << " is not running");
1368 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1372 std::string devicePath = "/dev/" + data.device;
1374 if (!lxc::isDevice(devicePath)) {
1375 LOGE("Failed to grant device - cannot acces device: " << data.device);
1376 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1380 // assume device node is created inside zone
1381 if (!lxc::setDeviceAccess(data.id, devicePath, true, data.flags)) {
1382 LOGE("Failed to grant device: " << data.device << " for zone: " << data.id);
1383 result->setError(api::ERROR_INTERNAL, "Cannot grant device");
1390 void ZonesManager::handleRevokeDeviceCall(const api::RevokeDeviceIn& data,
1391 api::MethodResultBuilder::Pointer result)
1393 LOGI("RevokeDevice call; id=" << data.first << "; dev=" << data.second);
1397 auto iter = findZone(data.first);
1398 if (iter == mZones.end()) {
1399 LOGE("Failed to revoke device - no such zone id: " << data.first);
1400 result->setError(api::ERROR_INVALID_ID, "No such zone id");
1404 Zone& zone = get(iter);
1405 if (!zone.isRunning() && !zone.isPaused()) {
1406 LOGE("Zone id=" << data.first << " is not running");
1407 result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1410 std::string devicePath = "/dev/" + data.second;
1412 if (!lxc::isDevice(devicePath)) {
1413 LOGE("Failed to revoke device - cannot acces device: " << data.second);
1414 result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1418 if (!lxc::setDeviceAccess(data.first, devicePath, false, 0)) {
1419 LOGE("Failed to revoke device: " << data.second << " for zone: " << data.first);
1420 result->setError(api::ERROR_INTERNAL, "Cannot revoke device");
1427 } // namespace vasum