Some synchronizations
[platform/core/security/vasum.git] / server / zones-manager.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Jan Olszak <j.olszak@samsung.com>
5  *
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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
17  */
18
19 /**
20  * @file
21  * @author  Jan Olszak (j.olszak@samsung.com)
22  * @brief   Definition of the class for managing zones
23  */
24
25 #include "config.hpp"
26
27 #include "host-dbus-definitions.hpp"
28 #include "common-dbus-definitions.hpp"
29 #include "zone-dbus-definitions.hpp"
30 #include "zones-manager.hpp"
31 #include "zone-admin.hpp"
32 #include "lxc/cgroup.hpp"
33 #include "exception.hpp"
34
35 #include "utils/paths.hpp"
36 #include "logger/logger.hpp"
37 #include "config/manager.hpp"
38 #include "dbus/exception.hpp"
39 #include "utils/fs.hpp"
40 #include "utils/img.hpp"
41 #include "utils/environment.hpp"
42
43 #include <boost/filesystem.hpp>
44 #include <boost/regex.hpp>
45 #include <boost/exception/diagnostic_information.hpp>
46 #include <cassert>
47 #include <string>
48 #include <climits>
49
50
51 namespace vasum {
52
53
54 namespace {
55
56 bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v)
57 {
58     for (const boost::regex& toMatch: v) {
59         if (boost::regex_match(str, toMatch)) {
60             return true;
61         }
62     }
63
64     return false;
65 }
66
67 const std::string DB_PREFIX = "daemon";
68 const std::string HOST_ID = "host";
69 const std::string ENABLED_FILE_NAME = "enabled";
70
71 const boost::regex ZONE_NAME_REGEX("~NAME~");
72 const boost::regex ZONE_IP_THIRD_OCTET_REGEX("~IP~");
73 const boost::regex ZONE_VT_REGEX("~VT~");
74
75 const unsigned int ZONE_IP_BASE_THIRD_OCTET = 100;
76 const unsigned int ZONE_VT_BASE = 1;
77
78 std::string getConfigName(const std::string& zoneId)
79 {
80     return "zones/" + zoneId + ".conf";
81 }
82
83 template<typename T>
84 void remove(std::vector<T>& v, const T& item)
85 {
86     // erase-remove idiom, ask google for explanation
87     v.erase(std::remove(v.begin(), v.end(), item), v.end());
88 }
89
90 } // namespace
91
92 ZonesManager::ZonesManager(const std::string& configPath)
93     : mWorker(utils::Worker::create())
94     , mDetachOnExit(false)
95 {
96     LOGD("Instantiating ZonesManager object...");
97
98     config::loadFromJsonFile(configPath, mConfig);
99     config::loadFromKVStoreWithJsonFile(mConfig.dbPath, configPath, mDynamicConfig, DB_PREFIX);
100
101     mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
102
103     using namespace std::placeholders;
104     mHostConnection.setProxyCallCallback(bind(&ZonesManager::handleProxyCall,
105                                               this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
106
107     mHostConnection.setGetZoneDbusesCallback(bind(&ZonesManager::handleGetZoneDbuses,
108                                                   this, _1));
109
110     mHostConnection.setGetZoneIdsCallback(bind(&ZonesManager::handleGetZoneIdsCall,
111                                                this, _1));
112
113     mHostConnection.setGetActiveZoneIdCallback(bind(&ZonesManager::handleGetActiveZoneIdCall,
114                                                     this, _1));
115
116     mHostConnection.setGetZoneInfoCallback(bind(&ZonesManager::handleGetZoneInfoCall,
117                                                 this, _1, _2));
118
119     mHostConnection.setDeclareFileCallback(bind(&ZonesManager::handleDeclareFileCall,
120                                                 this, _1, _2, _3, _4, _5, _6));
121
122     mHostConnection.setDeclareMountCallback(bind(&ZonesManager::handleDeclareMountCall,
123                                                  this, _1, _2, _3, _4, _5, _6, _7));
124
125     mHostConnection.setDeclareLinkCallback(bind(&ZonesManager::handleDeclareLinkCall,
126                                                 this, _1, _2, _3, _4));
127
128     mHostConnection.setSetActiveZoneCallback(bind(&ZonesManager::handleSetActiveZoneCall,
129                                                   this, _1, _2));
130
131     mHostConnection.setCreateZoneCallback(bind(&ZonesManager::handleCreateZoneCall,
132                                                this, _1, _2));
133
134     mHostConnection.setDestroyZoneCallback(bind(&ZonesManager::handleDestroyZoneCall,
135                                                 this, _1, _2));
136
137     mHostConnection.setShutdownZoneCallback(bind(&ZonesManager::handleShutdownZoneCall,
138                                                  this, _1, _2));
139
140     mHostConnection.setStartZoneCallback(bind(&ZonesManager::handleStartZoneCall,
141                                               this, _1, _2));
142
143     mHostConnection.setLockZoneCallback(bind(&ZonesManager::handleLockZoneCall,
144                                              this, _1, _2));
145
146     mHostConnection.setUnlockZoneCallback(bind(&ZonesManager::handleUnlockZoneCall,
147                                                this, _1, _2));
148
149     mHostConnection.setGrantDeviceCallback(bind(&ZonesManager::handleGrantDeviceCall,
150                                                 this, _1, _2, _3, _4));
151
152     mHostConnection.setRevokeDeviceCallback(bind(&ZonesManager::handleRevokeDeviceCall,
153                                                  this, _1, _2, _3));
154
155     for (const auto& zoneConfig : mDynamicConfig.zoneConfigs) {
156         createZone(utils::createFilePath(mConfig.zoneNewConfigPrefix, zoneConfig));
157     }
158
159     // check if default zone exists, throw ZoneOperationException if not found
160     if (!mConfig.defaultId.empty() && mZones.find(mConfig.defaultId) == mZones.end()) {
161         LOGE("Provided default zone ID " << mConfig.defaultId << " is invalid.");
162         throw ZoneOperationException("Provided default zone ID " + mConfig.defaultId +
163                                           " is invalid.");
164     }
165
166     LOGD("ZonesManager object instantiated");
167
168     if (mConfig.inputConfig.enabled) {
169         LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]");
170         mSwitchingSequenceMonitor.reset(
171                 new InputMonitor(mConfig.inputConfig,
172                                  std::bind(&ZonesManager::switchingSequenceMonitorNotify,
173                                            this)));
174     }
175
176
177 }
178
179 ZonesManager::~ZonesManager()
180 {
181     LOGD("Destroying ZonesManager object...");
182
183     if (!mDetachOnExit) {
184         try {
185             stopAll();
186         } catch (ServerException&) {
187             LOGE("Failed to stop all of the zones");
188         }
189     }
190     // wait for all tasks to complete
191     mWorker.reset();
192
193     LOGD("ZonesManager object destroyed");
194 }
195
196 void ZonesManager::saveDynamicConfig()
197 {
198     config::saveToKVStore(mConfig.dbPath, mDynamicConfig, DB_PREFIX);
199 }
200
201 void ZonesManager::createZone(const std::string& zoneConfigPath)
202 {
203     LOGT("Creating Zone " << zoneConfigPath);
204     std::unique_ptr<Zone> zone(new Zone(mWorker->createSubWorker(),
205                                         mConfig.zonesPath,
206                                         zoneConfigPath,
207                                         mConfig.lxcTemplatePrefix,
208                                         mConfig.runMountPointPrefix));
209     const std::string id = zone->getId();
210     if (id == HOST_ID) {
211         throw ZoneOperationException("Cannot use reserved zone ID");
212     }
213
214     using namespace std::placeholders;
215     zone->setNotifyActiveZoneCallback(bind(&ZonesManager::notifyActiveZoneHandler,
216                                            this, id, _1, _2));
217
218     zone->setDisplayOffCallback(bind(&ZonesManager::displayOffHandler,
219                                      this, id));
220
221     zone->setFileMoveRequestCallback(bind(&ZonesManager::handleZoneMoveFileRequest,
222                                           this, id, _1, _2, _3));
223
224     zone->setProxyCallCallback(bind(&ZonesManager::handleProxyCall,
225                                     this, id, _1, _2, _3, _4, _5, _6, _7));
226
227     zone->setDbusStateChangedCallback(bind(&ZonesManager::handleDbusStateChanged,
228                                            this, id, _1));
229
230     Lock lock(mMutex);
231
232     mZones.insert(ZoneMap::value_type(id, std::move(zone)));
233
234     // after zone is created successfully, put a file informing that zones are enabled
235     if (mZones.size() == 1) {
236         if (!utils::saveFileContent(
237                 utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME), "")) {
238             throw ZoneOperationException(ENABLED_FILE_NAME + ": cannot create.");
239         }
240     }
241 }
242
243 void ZonesManager::destroyZone(const std::string& zoneId)
244 {
245     Lock lock(mMutex);
246
247     auto it = mZones.find(zoneId);
248     if (it == mZones.end()) {
249         LOGE("Failed to destroy zone " << zoneId << ": no such zone");
250         throw ZoneOperationException("No such zone");
251     }
252
253     // TODO give back the focus
254     it->second->setDestroyOnExit();
255     mZones.erase(it);
256
257     if (mZones.size() == 0) {
258         if (!utils::removeFile(utils::createFilePath(mConfig.zonesPath, ENABLED_FILE_NAME))) {
259             LOGE("Failed to remove enabled file.");
260         }
261     }
262
263     // update dynamic config
264     remove(mDynamicConfig.zoneConfigs, getConfigName(zoneId));
265     saveDynamicConfig();
266 }
267
268 void ZonesManager::focus(const std::string& zoneId)
269 {
270     Lock lock(mMutex);
271
272     /* try to access the object first to throw immediately if it doesn't exist */
273     ZoneMap::mapped_type& foregroundZone = mZones.at(zoneId);
274
275     if (!foregroundZone->activateVT()) {
276         LOGE("Failed to activate zones VT. Aborting focus.");
277         return;
278     }
279
280     for (auto& zone : mZones) {
281         LOGD(zone.second->getId() << ": being sent to background");
282         zone.second->goBackground();
283     }
284     mConfig.foregroundId = foregroundZone->getId();
285     LOGD(mConfig.foregroundId << ": being sent to foreground");
286     foregroundZone->goForeground();
287 }
288
289 void ZonesManager::startAll()
290 {
291     LOGI("Starting all zones");
292
293     Lock lock(mMutex);
294
295     bool isForegroundFound = false;
296
297     for (auto& zone : mZones) {
298         zone.second->start();
299
300         if (zone.first == mConfig.foregroundId) {
301             isForegroundFound = true;
302             LOGI(zone.second->getId() << ": set as the foreground zone");
303             zone.second->goForeground();
304         }
305     }
306
307     if (!isForegroundFound) {
308         auto foregroundIterator = std::min_element(mZones.begin(), mZones.end(),
309                                                    [](ZoneMap::value_type &c1, ZoneMap::value_type &c2) {
310                                                        return c1.second->getPrivilege() < c2.second->getPrivilege();
311                                                    });
312
313         if (foregroundIterator != mZones.end()) {
314             mConfig.foregroundId = foregroundIterator->second->getId();
315             LOGI(mConfig.foregroundId << ": no foreground zone configured, setting one with highest priority");
316             foregroundIterator->second->goForeground();
317         }
318     }
319 }
320
321 void ZonesManager::stopAll()
322 {
323     LOGI("Stopping all zones");
324
325     Lock lock(mMutex);
326
327     for (auto& zone : mZones) {
328         zone.second->stop();
329     }
330 }
331
332 bool ZonesManager::isPaused(const std::string& zoneId)
333 {
334     Lock lock(mMutex);
335
336     auto iter = mZones.find(zoneId);
337     if (iter == mZones.end()) {
338         LOGE("No such zone id: " << zoneId);
339         throw ZoneOperationException("No such zone");
340     }
341
342     return iter->second->isPaused();
343 }
344
345 bool ZonesManager::isRunning(const std::string& zoneId)
346 {
347     Lock lock(mMutex);
348
349     auto iter = mZones.find(zoneId);
350     if (iter == mZones.end()) {
351         LOGE("No such zone id: " << zoneId);
352         throw ZoneOperationException("No such zone");
353     }
354     return iter->second->isRunning();
355 }
356
357 std::string ZonesManager::getRunningForegroundZoneId() const
358 {
359     Lock lock(mMutex);
360
361     for (auto& zone : mZones) {
362         if (zone.first == mConfig.foregroundId &&
363             zone.second->isRunning()) {
364             return zone.first;
365         }
366     }
367     return std::string();
368 }
369
370 std::string ZonesManager::getNextToForegroundZoneId()
371 {
372     Lock lock(mMutex);
373
374     // handles case where there is no next zone
375     if (mZones.size() < 2) {
376         return std::string();
377     }
378
379     for (auto it = mZones.begin(); it != mZones.end(); ++it) {
380         if (it->first == mConfig.foregroundId &&
381             it->second->isRunning()) {
382             auto nextIt = std::next(it);
383             if (nextIt != mZones.end()) {
384                 return nextIt->first;
385             }
386         }
387     }
388     return mZones.begin()->first;
389 }
390
391 void ZonesManager::switchingSequenceMonitorNotify()
392 {
393     LOGI("switchingSequenceMonitorNotify() called");
394
395     auto nextZoneId = getNextToForegroundZoneId();
396
397     if (!nextZoneId.empty()) {
398         focus(nextZoneId);
399     }
400 }
401
402
403 void ZonesManager::setZonesDetachOnExit()
404 {
405     Lock lock(mMutex);
406
407     mDetachOnExit = true;
408
409     for (auto& zone : mZones) {
410         zone.second->setDetachOnExit();
411     }
412 }
413
414 void ZonesManager::notifyActiveZoneHandler(const std::string& caller,
415                                            const std::string& application,
416                                            const std::string& message)
417 {
418     LOGI("notifyActiveZoneHandler(" << caller << ", " << application << ", " << message
419          << ") called");
420
421     Lock lock(mMutex);
422
423     try {
424         const std::string activeZone = getRunningForegroundZoneId();
425         if (!activeZone.empty() && caller != activeZone) {
426             mZones[activeZone]->sendNotification(caller, application, message);
427         }
428     } catch(const VasumException&) {
429         LOGE("Notification from " << caller << " hasn't been sent");
430     }
431 }
432
433 void ZonesManager::displayOffHandler(const std::string& /*caller*/)
434 {
435     // get config of currently set zone and switch if switchToDefaultAfterTimeout is true
436     Lock lock(mMutex);
437
438     const std::string activeZoneName = getRunningForegroundZoneId();
439     const auto& activeZone = mZones.find(activeZoneName);
440
441     if (activeZone != mZones.end() &&
442         activeZone->second->isSwitchToDefaultAfterTimeoutAllowed()) {
443         LOGI("Switching to default zone " << mConfig.defaultId);
444         focus(mConfig.defaultId);
445     }
446 }
447
448 void ZonesManager::handleZoneMoveFileRequest(const std::string& srcZoneId,
449                                              const std::string& dstZoneId,
450                                              const std::string& path,
451                                              dbus::MethodResultBuilder::Pointer result)
452 {
453     // TODO: this implementation is only a placeholder.
454     // There are too many unanswered questions and security concerns:
455     // 1. What about mount namespace, host might not see the source/destination
456     //    file. The file might be a different file from a host perspective.
457     // 2. Copy vs move (speed and security concerns over already opened FDs)
458     // 3. Access to source and destination files - DAC, uid/gig
459     // 4. Access to source and destintation files - MAC, smack
460     // 5. Destination file uid/gid assignment
461     // 6. Destination file smack label assignment
462     // 7. Verifiability of the source path
463
464     // NOTE: other possible implementations include:
465     // 1. Sending file descriptors opened directly in each zone through DBUS
466     //    using something like g_dbus_message_set_unix_fd_list()
467     // 2. VSM forking and calling setns(MNT) in each zone and opening files
468     //    by itself, then passing FDs to the main process
469     // Now when the main process has obtained FDs (by either of those methods)
470     // it can do the copying by itself.
471
472     LOGI("File move requested\n"
473          << "src: " << srcZoneId << "\n"
474          << "dst: " << dstZoneId << "\n"
475          << "path: " << path);
476
477     Lock lock(mMutex);
478
479     ZoneMap::const_iterator srcIter = mZones.find(srcZoneId);
480     if (srcIter == mZones.end()) {
481         LOGE("Source zone '" << srcZoneId << "' not found");
482         return;
483     }
484     Zone& srcZone = *srcIter->second;
485
486     ZoneMap::const_iterator dstIter = mZones.find(dstZoneId);
487     if (dstIter == mZones.end()) {
488         LOGE("Destination zone '" << dstZoneId << "' not found");
489         result->set(g_variant_new("(s)", api::zone::FILE_MOVE_DESTINATION_NOT_FOUND.c_str()));
490         return;
491     }
492     Zone& dstContanier = *dstIter->second;
493
494     if (srcZoneId == dstZoneId) {
495         LOGE("Cannot send a file to yourself");
496         result->set(g_variant_new("(s)", api::zone::FILE_MOVE_WRONG_DESTINATION.c_str()));
497         return;
498     }
499
500     if (!regexMatchVector(path, srcZone.getPermittedToSend())) {
501         LOGE("Source zone has no permissions to send the file: " << path);
502         result->set(g_variant_new("(s)", api::zone::FILE_MOVE_NO_PERMISSIONS_SEND.c_str()));
503         return;
504     }
505
506     if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
507         LOGE("Destination zone has no permissions to receive the file: " << path);
508         result->set(g_variant_new("(s)", api::zone::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str()));
509         return;
510     }
511
512     namespace fs = boost::filesystem;
513     std::string srcPath = fs::absolute(srcZoneId, mConfig.zonesPath).string() + path;
514     std::string dstPath = fs::absolute(dstZoneId, mConfig.zonesPath).string() + path;
515
516     if (!utils::moveFile(srcPath, dstPath)) {
517         LOGE("Failed to move the file: " << path);
518         result->set(g_variant_new("(s)", api::zone::FILE_MOVE_FAILED.c_str()));
519     } else {
520         result->set(g_variant_new("(s)", api::zone::FILE_MOVE_SUCCEEDED.c_str()));
521         try {
522             dstContanier.sendNotification(srcZoneId, path, api::zone::FILE_MOVE_SUCCEEDED);
523         } catch (ServerException&) {
524             LOGE("Notification to '" << dstZoneId << "' has not been sent");
525         }
526     }
527 }
528
529 void ZonesManager::handleProxyCall(const std::string& caller,
530                                    const std::string& target,
531                                    const std::string& targetBusName,
532                                    const std::string& targetObjectPath,
533                                    const std::string& targetInterface,
534                                    const std::string& targetMethod,
535                                    GVariant* parameters,
536                                    dbus::MethodResultBuilder::Pointer result)
537 {
538     if (!mProxyCallPolicy->isProxyCallAllowed(caller,
539                                               target,
540                                               targetBusName,
541                                               targetObjectPath,
542                                               targetInterface,
543                                               targetMethod)) {
544         LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
545                 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
546         result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
547         return;
548     }
549
550     LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
551             << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
552
553     auto asyncResultCallback = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) {
554         try {
555             GVariant* targetResult = asyncMethodCallResult.get();
556             result->set(g_variant_new("(v)", targetResult));
557         } catch (dbus::DbusException& e) {
558             result->setError(api::ERROR_FORWARDED, e.what());
559         }
560     };
561
562     if (target == HOST_ID) {
563         mHostConnection.proxyCallAsync(targetBusName,
564                                        targetObjectPath,
565                                        targetInterface,
566                                        targetMethod,
567                                        parameters,
568                                        asyncResultCallback);
569         return;
570     }
571
572     Lock lock(mMutex);
573
574     ZoneMap::const_iterator targetIter = mZones.find(target);
575     if (targetIter == mZones.end()) {
576         LOGE("Target zone '" << target << "' not found");
577         result->setError(api::ERROR_INVALID_ID, "Unknown proxy call target");
578         return;
579     }
580
581     Zone& targetZone = *targetIter->second;
582     targetZone.proxyCallAsync(targetBusName,
583                                    targetObjectPath,
584                                    targetInterface,
585                                    targetMethod,
586                                    parameters,
587                                    asyncResultCallback);
588 }
589
590 void ZonesManager::handleGetZoneDbuses(dbus::MethodResultBuilder::Pointer result) const
591 {
592     Lock lock(mMutex);
593
594     std::vector<GVariant*> entries;
595     for (auto& zone : mZones) {
596         GVariant* zoneId = g_variant_new_string(zone.first.c_str());
597         GVariant* dbusAddress = g_variant_new_string(zone.second->getDbusAddress().c_str());
598         GVariant* entry = g_variant_new_dict_entry(zoneId, dbusAddress);
599         entries.push_back(entry);
600     }
601     GVariant* dict = g_variant_new_array(G_VARIANT_TYPE("{ss}"), entries.data(), entries.size());
602     result->set(g_variant_new("(*)", dict));
603 }
604
605 void ZonesManager::handleDbusStateChanged(const std::string& zoneId,
606                                           const std::string& dbusAddress)
607 {
608     mHostConnection.signalZoneDbusState(zoneId, dbusAddress);
609 }
610
611 void ZonesManager::handleGetZoneIdsCall(dbus::MethodResultBuilder::Pointer result) const
612 {
613     Lock lock(mMutex);
614
615     std::vector<GVariant*> zoneIds;
616     for(auto& zone: mZones){
617         zoneIds.push_back(g_variant_new_string(zone.first.c_str()));
618     }
619
620     GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"),
621                                           zoneIds.data(),
622                                           zoneIds.size());
623     result->set(g_variant_new("(*)", array));
624 }
625
626 void ZonesManager::handleGetActiveZoneIdCall(dbus::MethodResultBuilder::Pointer result)
627 {
628     LOGI("GetActiveZoneId call");
629
630     Lock lock(mMutex);
631
632     if (!mConfig.foregroundId.empty() && mZones[mConfig.foregroundId]->isRunning()){
633         result->set(g_variant_new("(s)", mConfig.foregroundId.c_str()));
634     } else {
635         result->set(g_variant_new("(s)", ""));
636     }
637 }
638
639 void ZonesManager::handleGetZoneInfoCall(const std::string& id,
640                                          dbus::MethodResultBuilder::Pointer result)
641 {
642     LOGI("GetZoneInfo call");
643
644     Lock lock(mMutex);
645
646     if (mZones.count(id) == 0) {
647         LOGE("No zone with id=" << id);
648         result->setError(api::ERROR_INVALID_ID, "No such zone id");
649         return;
650     }
651     const auto& zone = mZones[id];
652     const char* state;
653     //TODO: Use the lookup map.
654     if (zone->isRunning()) {
655         state = "RUNNING";
656     } else if (zone->isStopped()) {
657         state = "STOPPED";
658     } else if (zone->isPaused()) {
659         state = "FROZEN";
660     } else {
661         LOGE("Unrecognized state of zone id=" << id);
662         result->setError(api::ERROR_INTERNAL, "Unrecognized state of zone");
663         return;
664     }
665
666     result->set(g_variant_new("((siss))",
667                               id.c_str(),
668                               zone->getVT(),
669                               state,
670                               zone->getRootPath().c_str()));
671 }
672
673 void ZonesManager::handleDeclareFileCall(const std::string& zone,
674                                          const int32_t& type,
675                                          const std::string& path,
676                                          const int32_t& flags,
677                                          const int32_t& mode,
678                                          dbus::MethodResultBuilder::Pointer result)
679 {
680     LOGI("DeclareFile call");
681
682     try {
683         Lock lock(mMutex);
684
685         mZones.at(zone)->declareFile(type, path, flags, mode);
686         result->setVoid();
687     } catch (const std::out_of_range&) {
688         LOGE("No zone with id=" << zone);
689         result->setError(api::ERROR_INVALID_ID, "No such zone id");
690     } catch (const config::ConfigException& ex) {
691         LOGE("Can't declare file: " << ex.what());
692         result->setError(api::ERROR_INTERNAL, "Internal error");
693     }
694 }
695
696 void ZonesManager::handleDeclareMountCall(const std::string& source,
697                                           const std::string& zone,
698                                           const std::string& target,
699                                           const std::string& type,
700                                           const uint64_t& flags,
701                                           const std::string& data,
702                                           dbus::MethodResultBuilder::Pointer result)
703 {
704     LOGI("DeclareMount call");
705
706     try {
707         Lock lock(mMutex);
708
709         mZones.at(zone)->declareMount(source, target, type, flags, data);
710         result->setVoid();
711     } catch (const std::out_of_range&) {
712         LOGE("No zone with id=" << zone);
713         result->setError(api::ERROR_INVALID_ID, "No such zone id");
714     } catch (const config::ConfigException& ex) {
715         LOGE("Can't declare mount: " << ex.what());
716         result->setError(api::ERROR_INTERNAL, "Internal error");
717     }
718 }
719
720 void ZonesManager::handleDeclareLinkCall(const std::string& source,
721                                          const std::string& zone,
722                                          const std::string& target,
723                                          dbus::MethodResultBuilder::Pointer result)
724 {
725     LOGI("DeclareLink call");
726     try {
727         Lock lock(mMutex);
728
729         mZones.at(zone)->declareLink(source, target);
730         result->setVoid();
731     } catch (const std::out_of_range&) {
732         LOGE("No zone with id=" << zone);
733         result->setError(api::ERROR_INVALID_ID, "No such zone id");
734     } catch (const config::ConfigException& ex) {
735         LOGE("Can't declare link: " << ex.what());
736         result->setError(api::ERROR_INTERNAL, "Internal error");
737     }
738 }
739
740 void ZonesManager::handleSetActiveZoneCall(const std::string& id,
741                                            dbus::MethodResultBuilder::Pointer result)
742 {
743     LOGI("SetActiveZone call; Id=" << id );
744
745     Lock lock(mMutex);
746
747     auto zone = mZones.find(id);
748     if (zone == mZones.end()){
749         LOGE("No zone with id=" << id );
750         result->setError(api::ERROR_INVALID_ID, "No such zone id");
751         return;
752     }
753
754     if (zone->second->isStopped()){
755         LOGE("Could not activate a stopped zone");
756         result->setError(api::host::ERROR_ZONE_STOPPED,
757                          "Could not activate a stopped zone");
758         return;
759     }
760
761     focus(id);
762     result->setVoid();
763 }
764
765
766 void ZonesManager::generateNewConfig(const std::string& id,
767                                      const std::string& templatePath,
768                                      const std::string& resultPath)
769 {
770     namespace fs = boost::filesystem;
771
772     if (fs::exists(resultPath)) {
773         LOGT(resultPath << " already exists, removing");
774         fs::remove(resultPath);
775     } else {
776         std::string resultFileDir = utils::dirName(resultPath);
777         if (!utils::createDirs(resultFileDir, fs::perms::owner_all |
778                                               fs::perms::group_read | fs::perms::group_exe |
779                                               fs::perms::others_read | fs::perms::others_exe)) {
780             LOGE("Unable to create directory for new config.");
781             throw ZoneOperationException("Unable to create directory for new config.");
782         }
783     }
784
785     std::string config;
786     if (!utils::readFileContent(templatePath, config)) {
787         LOGE("Failed to read template config file.");
788         throw ZoneOperationException("Failed to read template config file.");
789     }
790
791     std::string resultConfig = boost::regex_replace(config, ZONE_NAME_REGEX, id);
792
793     // generate third IP octet for network config
794     // TODO change algorithm after implementing removeZone
795     std::string thirdOctetStr = std::to_string(ZONE_IP_BASE_THIRD_OCTET + mZones.size() + 1);
796     LOGD("IP third octet: " << thirdOctetStr);
797     resultConfig = boost::regex_replace(resultConfig, ZONE_IP_THIRD_OCTET_REGEX, thirdOctetStr);
798
799     // generate first free VT number
800     // TODO change algorithm after implementing removeZone
801     std::string freeVT = std::to_string(ZONE_VT_BASE + mZones.size() + 1);
802     LOGD("VT number: " << freeVT);
803     resultConfig = boost::regex_replace(resultConfig, ZONE_VT_REGEX, freeVT);
804
805     if (!utils::saveFileContent(resultPath, resultConfig)) {
806         LOGE("Faield to save new config file.");
807         throw ZoneOperationException("Failed to save new config file.");
808     }
809
810     // restrict new config file so that only owner (vasum) can write it
811     fs::permissions(resultPath, fs::perms::owner_read | fs::perms::owner_write |
812                                 fs::perms::group_read |
813                                 fs::perms::others_read);
814 }
815
816 void ZonesManager::handleCreateZoneCall(const std::string& id,
817                                         dbus::MethodResultBuilder::Pointer result)
818 {
819     if (id.empty()) {
820         LOGE("Failed to add zone - invalid name.");
821         result->setError(api::ERROR_INVALID_ID, "Invalid name");
822         return;
823     }
824
825     LOGI("Creating zone " << id);
826
827     Lock lock(mMutex);
828
829     // TODO: This solution is temporary. It utilizes direct access to config files when creating new
830     // zones. Update this handler when config database will appear.
831     namespace fs = boost::filesystem;
832
833     // check if zone does not exist
834     if (mZones.find(id) != mZones.end()) {
835         LOGE("Cannot create " << id << " zone - already exists!");
836         result->setError(api::ERROR_INVALID_ID, "Already exists");
837         return;
838     }
839
840     const std::string zonePathStr = utils::createFilePath(mConfig.zonesPath, id, "/");
841
842     // copy zone image if config contains path to image
843     LOGT("Image path: " << mConfig.zoneImagePath);
844     if (!mConfig.zoneImagePath.empty()) {
845         auto copyImageContentsWrapper = std::bind(&utils::copyImageContents,
846                                                   mConfig.zoneImagePath,
847                                                   zonePathStr);
848
849         if (!utils::launchAsRoot(copyImageContentsWrapper)) {
850             LOGE("Failed to copy zone image.");
851             result->setError(api::ERROR_INTERNAL, "Failed to copy zone image.");
852             return;
853         }
854     }
855
856     // generate paths to new configuration files
857     std::string newConfigName = getConfigName(id);
858     std::string newConfigPath = utils::createFilePath(mConfig.zoneNewConfigPrefix, newConfigName);
859
860     auto removeAllWrapper = [](const std::string& path) -> bool {
861         try {
862             LOGD("Removing copied data");
863             fs::remove_all(fs::path(path));
864         } catch(const std::exception& e) {
865             LOGW("Failed to remove data: " << boost::diagnostic_information(e));
866         }
867         return true;
868     };
869
870     try {
871         LOGI("Generating config from " << mConfig.zoneTemplatePath << " to " << newConfigPath);
872         generateNewConfig(id, mConfig.zoneTemplatePath, newConfigPath);
873
874     } catch (VasumException& e) {
875         LOGE("Generate config failed: " << e.what());
876         utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
877         result->setError(api::ERROR_INTERNAL, "Failed to generate config");
878         return;
879     }
880
881     LOGT("Creating new zone");
882     try {
883         createZone(newConfigPath);
884     } catch (VasumException& e) {
885         LOGE("Creating new zone failed: " << e.what());
886         utils::launchAsRoot(std::bind(removeAllWrapper, zonePathStr));
887         result->setError(api::ERROR_INTERNAL, "Failed to create zone");
888         return;
889     }
890
891     mDynamicConfig.zoneConfigs.push_back(newConfigName);
892     saveDynamicConfig();
893
894     result->setVoid();
895 }
896
897 void ZonesManager::handleDestroyZoneCall(const std::string& id,
898                                          dbus::MethodResultBuilder::Pointer result)
899 {
900     Lock lock(mMutex);
901
902     if (mZones.find(id) == mZones.end()) {
903         LOGE("Failed to destroy zone - no such zone id: " << id);
904         result->setError(api::ERROR_INVALID_ID, "No such zone id");
905         return;
906     }
907
908     LOGI("Destroying zone " << id);
909
910     auto destroyer = [id, result, this] {
911         try {
912             destroyZone(id);
913         } catch (const VasumException& e) {
914             LOGE("Error during zone destruction: " << e.what());
915             result->setError(api::ERROR_INTERNAL, "Failed to destroy zone");
916             return;
917         }
918         result->setVoid();
919     };
920
921     mWorker->addTask(destroyer);
922 }
923
924 void ZonesManager::handleShutdownZoneCall(const std::string& id,
925                                           dbus::MethodResultBuilder::Pointer result)
926 {
927     LOGI("ShutdownZone call; Id=" << id );
928
929     Lock lock(mMutex);
930
931     if (mZones.find(id) == mZones.end()) {
932         LOGE("Failed to shutdown zone - no such zone id: " << id);
933         result->setError(api::ERROR_INVALID_ID, "No such zone id");
934         return;
935     }
936
937     LOGT("Shutdown zone " << id);
938
939     auto shutdown = [id, result, this] {
940         try {
941             mZones[id]->stop();
942         } catch (ZoneOperationException& e) {
943             LOGE("Error during zone shutdown: " << e.what());
944             result->setError(api::ERROR_INTERNAL, "Failed to shutdown zone");
945             return;
946         }
947         result->setVoid();
948     };
949
950     mWorker->addTask(shutdown);
951 }
952
953 void ZonesManager::handleStartZoneCall(const std::string& id,
954                                        dbus::MethodResultBuilder::Pointer result)
955 {
956     LOGI("StartZone call; Id=" << id );
957
958     Lock lock(mMutex);
959
960     if (mZones.find(id) == mZones.end()) {
961         LOGE("Failed to start zone - no such zone id: " << id);
962         result->setError(api::ERROR_INVALID_ID, "No such zone id");
963         return;
964     }
965
966     LOGT("Start zone " << id);
967
968     auto startAsync = [this, id, result]() {
969         try {
970             ZoneMap::mapped_type zone;
971             {
972                 Lock lock(mMutex);
973                 zone = mZones.at(id);
974             }
975             zone->start();
976             focus(id);
977             result->setVoid();
978         } catch (const std::exception& e) {
979             LOGE(id << ": failed to start: " << e.what());
980             result->setError(api::ERROR_INTERNAL, "Failed to start zone");
981         }
982     };
983     mWorker->addTask(startAsync);
984 }
985
986 void ZonesManager::handleLockZoneCall(const std::string& id,
987                                       dbus::MethodResultBuilder::Pointer result)
988 {
989     LOGI("LockZone call; Id=" << id );
990
991     Lock lock(mMutex);
992
993     auto iter = mZones.find(id);
994     if (iter == mZones.end()) {
995         LOGE("Failed to lock zone - no such zone id: " << id);
996         result->setError(api::ERROR_INVALID_ID, "No such zone id");
997         return;
998     }
999
1000     auto& zone = *iter->second;
1001     if (!zone.isRunning()) {
1002         LOGE("Zone id=" << id << " is not running.");
1003         result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1004         return;
1005     }
1006
1007     LOGT("Lock zone");
1008     try {
1009         zone.suspend();
1010     } catch (ZoneOperationException& e) {
1011         LOGE(e.what());
1012         result->setError(api::ERROR_INTERNAL, e.what());
1013         return;
1014     }
1015
1016     result->setVoid();
1017 }
1018
1019 void ZonesManager::handleUnlockZoneCall(const std::string& id,
1020                                         dbus::MethodResultBuilder::Pointer result)
1021 {
1022     LOGI("UnlockZone call; Id=" << id );
1023
1024     Lock lock(mMutex);
1025
1026     auto iter = mZones.find(id);
1027     if (iter == mZones.end()) {
1028         LOGE("Failed to unlock zone - no such zone id: " << id);
1029         result->setError(api::ERROR_INVALID_ID, "No such zone id");
1030         return;
1031     }
1032
1033     auto& zone = *iter->second;
1034     if (!zone.isPaused()) {
1035         LOGE("Zone id=" << id << " is not paused.");
1036         result->setError(api::ERROR_INVALID_STATE, "Zone is not paused");
1037         return;
1038     }
1039
1040     LOGT("Unlock zone");
1041     try {
1042         zone.resume();
1043     } catch (ZoneOperationException& e) {
1044         LOGE(e.what());
1045         result->setError(api::ERROR_INTERNAL, e.what());
1046         return;
1047     }
1048
1049     result->setVoid();
1050 }
1051
1052 void ZonesManager::handleGrantDeviceCall(const std::string& id,
1053                                          const std::string& device,
1054                                          uint32_t flags,
1055                                          dbus::MethodResultBuilder::Pointer result)
1056 {
1057     LOGI("GrantDevice call; id=" << id << "; dev=" << device);
1058
1059     Lock lock(mMutex);
1060
1061     auto iter = mZones.find(id);
1062     if (iter == mZones.end()) {
1063         LOGE("Failed to grant device - no such zone id: " << id);
1064         result->setError(api::ERROR_INVALID_ID, "No such zone id");
1065         return;
1066     }
1067
1068     auto& zone = *iter->second;
1069     if (!zone.isRunning() && !zone.isPaused()) {
1070         LOGE("Zone id=" << id << " is not running");
1071         result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1072         return;
1073     }
1074
1075     std::string devicePath = "/dev/" + device;
1076
1077     if (!lxc::isDevice(devicePath)) {
1078         LOGE("Failed to grant device - cannot acces device: " << device);
1079         result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1080         return;
1081     }
1082
1083     // assume device node is created inside zone
1084     if (!lxc::setDeviceAccess(id, devicePath, true, flags)) {
1085         LOGE("Failed to grant device: " << device << " for zone: " << id);
1086         result->setError(api::ERROR_INTERNAL, "Cannot grant device");
1087         return;
1088     }
1089
1090     result->setVoid();
1091 }
1092
1093 void ZonesManager::handleRevokeDeviceCall(const std::string& id,
1094                                           const std::string& device,
1095                                           dbus::MethodResultBuilder::Pointer result)
1096 {
1097     LOGI("RevokeDevice call; id=" << id << "; dev=" << device);
1098
1099     Lock lock(mMutex);
1100
1101     auto iter = mZones.find(id);
1102     if (iter == mZones.end()) {
1103         LOGE("Failed to revoke device - no such zone id: " << id);
1104         result->setError(api::ERROR_INVALID_ID, "No such zone id");
1105         return;
1106     }
1107
1108     auto& zone = *iter->second;
1109     if (!zone.isRunning() && !zone.isPaused()) {
1110         LOGE("Zone id=" << id << " is not running");
1111         result->setError(api::ERROR_INVALID_STATE, "Zone is not running");
1112         return;
1113     }
1114     std::string devicePath = "/dev/" + device;
1115
1116     if (!lxc::isDevice(devicePath)) {
1117         LOGE("Failed to revoke device - cannot acces device: " << device);
1118         result->setError(api::ERROR_FORBIDDEN, "Cannot access device");
1119         return;
1120     }
1121
1122     if (!lxc::setDeviceAccess(id, devicePath, false, 0)) {
1123         LOGE("Failed to revoke device: " << device << " for zone: " << id);
1124         result->setError(api::ERROR_INTERNAL, "Cannot revoke device");
1125         return;
1126     }
1127
1128     result->setVoid();
1129 }
1130
1131 } // namespace vasum