Allow SCS to launch without any container
[platform/core/security/vasum.git] / server / containers-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 containers
23  */
24
25 #include "config.hpp"
26
27 #include "host-dbus-definitions.hpp"
28 #include "common-dbus-definitions.hpp"
29 #include "container-dbus-definitions.hpp"
30 #include "containers-manager.hpp"
31 #include "container-admin.hpp"
32 #include "exception.hpp"
33
34 #include "utils/paths.hpp"
35 #include "logger/logger.hpp"
36 #include "config/manager.hpp"
37 #include "dbus/exception.hpp"
38 #include "utils/fs.hpp"
39 #include "utils/img.hpp"
40
41 #include <boost/filesystem.hpp>
42 #include <boost/regex.hpp>
43 #include <boost/uuid/uuid.hpp>
44 #include <boost/uuid/uuid_io.hpp>
45 #include <boost/uuid/uuid_generators.hpp>
46 #include <boost/exception/diagnostic_information.hpp>
47 #include <cassert>
48 #include <string>
49 #include <climits>
50
51
52 namespace security_containers {
53
54
55 namespace {
56
57 bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v)
58 {
59     for (const boost::regex& toMatch: v) {
60         if (boost::regex_match(str, toMatch)) {
61             return true;
62         }
63     }
64
65     return false;
66 }
67
68 const std::string HOST_ID = "host";
69 const std::string CONTAINER_TEMPLATE_CONFIG_PATH = "template.conf";
70 const std::string CONTAINER_TEMPLATE_LIBVIRT_CONFIG_PATH = "template.xml";
71 const std::string CONTAINER_TEMPLATE_LIBVIRT_NETWORK_PATH = "template-network.xml";
72 const std::string CONTAINER_TEMPLATE_LIBVIRT_NETWORK_FILTER_PATH = "template-nwfilter.xml";
73
74 const boost::regex CONTAINER_NAME_REGEX("~NAME~");
75 const boost::regex CONTAINER_UUID_REGEX("~UUID~");
76 const boost::regex CONTAINER_IP_THIRD_OCTET_REGEX("~IP~");
77
78 const unsigned int CONTAINER_IP_BASE_THIRD_OCTET = 100;
79
80 } // namespace
81
82 ContainersManager::ContainersManager(const std::string& managerConfigPath): mDetachOnExit(false)
83 {
84     LOGD("Instantiating ContainersManager object...");
85
86     mConfigPath = managerConfigPath;
87     config::loadFromFile(mConfigPath, mConfig);
88
89     mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
90
91     using namespace std::placeholders;
92     mHostConnection.setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
93                                               this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
94
95     mHostConnection.setGetContainerDbusesCallback(bind(
96                 &ContainersManager::handleGetContainerDbuses, this, _1));
97
98     mHostConnection.setGetContainerIdsCallback(bind(&ContainersManager::handleGetContainerIdsCall,
99                                                     this, _1));
100
101     mHostConnection.setGetActiveContainerIdCallback(bind(&ContainersManager::handleGetActiveContainerIdCall,
102                                                          this, _1));
103
104     mHostConnection.setSetActiveContainerCallback(bind(&ContainersManager::handleSetActiveContainerCall,
105                                                        this, _1, _2));
106
107     mHostConnection.setAddContainerCallback(bind(&ContainersManager::handleAddContainerCall,
108                                                            this, _1, _2));
109
110     for (auto& containerConfig : mConfig.containerConfigs) {
111         addContainer(containerConfig);
112     }
113
114     // check if default container exists, throw ContainerOperationException if not found
115     if (!mConfig.defaultId.empty() && mContainers.find(mConfig.defaultId) == mContainers.end()) {
116         LOGE("Provided default container ID " << mConfig.defaultId << " is invalid.");
117         throw ContainerOperationException("Provided default container ID " + mConfig.defaultId +
118                                           " is invalid.");
119     }
120
121     LOGD("ContainersManager object instantiated");
122
123     if (mConfig.inputConfig.enabled) {
124         LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]");
125         mSwitchingSequenceMonitor.reset(
126                 new InputMonitor(mConfig.inputConfig,
127                                  std::bind(&ContainersManager::switchingSequenceMonitorNotify,
128                                            this)));
129     }
130
131
132 }
133
134 ContainersManager::~ContainersManager()
135 {
136     LOGD("Destroying ContainersManager object...");
137
138     if (!mDetachOnExit) {
139         try {
140             stopAll();
141         } catch (ServerException&) {
142             LOGE("Failed to stop all of the containers");
143         }
144     }
145
146     LOGD("ContainersManager object destroyed");
147 }
148
149 void ContainersManager::addContainer(const std::string& containerConfig)
150 {
151     std::string baseConfigPath = utils::dirName(mConfigPath);
152     std::string containerConfigPath = utils::getAbsolutePath(containerConfig, baseConfigPath);
153
154     LOGT("Creating Container " << containerConfigPath);
155     std::unique_ptr<Container> c(new Container(containerConfigPath,
156                                                mConfig.runMountPointPrefix));
157     const std::string id = c->getId();
158     if (id == HOST_ID) {
159         throw ContainerOperationException("Cannot use reserved container ID");
160     }
161
162     using namespace std::placeholders;
163     c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
164                                              this, id, _1, _2));
165
166     c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
167                                   this, id));
168
169     c->setFileMoveRequestCallback(bind(&ContainersManager::handleContainerMoveFileRequest,
170                                             this, id, _1, _2, _3));
171
172     c->setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
173                                  this, id, _1, _2, _3, _4, _5, _6, _7));
174
175     c->setDbusStateChangedCallback(bind(&ContainersManager::handleDbusStateChanged,
176                                         this, id, _1));
177
178     mContainers.insert(ContainerMap::value_type(id, std::move(c)));
179 }
180
181 void ContainersManager::focus(const std::string& containerId)
182 {
183     /* try to access the object first to throw immediately if it doesn't exist */
184     ContainerMap::mapped_type& foregroundContainer = mContainers.at(containerId);
185
186     if (!foregroundContainer->activateVT()) {
187         LOGE("Failed to activate containers VT. Aborting focus.");
188         return;
189     }
190
191     for (auto& container : mContainers) {
192         LOGD(container.second->getId() << ": being sent to background");
193         container.second->goBackground();
194     }
195     mConfig.foregroundId = foregroundContainer->getId();
196     LOGD(mConfig.foregroundId << ": being sent to foreground");
197     foregroundContainer->goForeground();
198 }
199
200 void ContainersManager::startAll()
201 {
202     LOGI("Starting all containers");
203
204     bool isForegroundFound = false;
205
206     for (auto& container : mContainers) {
207         container.second->start();
208
209         if (container.first == mConfig.foregroundId) {
210             isForegroundFound = true;
211             LOGI(container.second->getId() << ": set as the foreground container");
212             container.second->goForeground();
213         }
214     }
215
216     if (!isForegroundFound) {
217         auto foregroundIterator = std::min_element(mContainers.begin(), mContainers.end(),
218                                                    [](ContainerMap::value_type &c1, ContainerMap::value_type &c2) {
219                                                        return c1.second->getPrivilege() < c2.second->getPrivilege();
220                                                    });
221
222         if (foregroundIterator != mContainers.end()) {
223             mConfig.foregroundId = foregroundIterator->second->getId();
224             LOGI(mConfig.foregroundId << ": no foreground container configured, setting one with highest priority");
225             foregroundIterator->second->goForeground();
226         }
227     }
228 }
229
230 void ContainersManager::stopAll()
231 {
232     LOGI("Stopping all containers");
233
234     for (auto& container : mContainers) {
235         container.second->stop();
236     }
237 }
238
239 std::string ContainersManager::getRunningForegroundContainerId()
240 {
241     for (auto& container : mContainers) {
242         if (container.first == mConfig.foregroundId &&
243             container.second->isRunning()) {
244             return container.first;
245         }
246     }
247     return std::string();
248 }
249
250 std::string ContainersManager::getNextToForegroundContainerId()
251 {
252     // handles case where there is no next container
253     if (mContainers.size() < 2) {
254         return std::string();
255     }
256
257     for (auto it = mContainers.begin(); it != mContainers.end(); ++it) {
258         if (it->first == mConfig.foregroundId &&
259             it->second->isRunning()) {
260             auto nextIt = std::next(it);
261             if (nextIt != mContainers.end()) {
262                 return nextIt->first;
263             }
264         }
265     }
266     return mContainers.begin()->first;
267 }
268
269 void ContainersManager::switchingSequenceMonitorNotify()
270 {
271     LOGI("switchingSequenceMonitorNotify() called");
272
273     auto nextContainerId = getNextToForegroundContainerId();
274
275     if (!nextContainerId.empty()) {
276         focus(nextContainerId);
277     }
278 }
279
280
281 void ContainersManager::setContainersDetachOnExit()
282 {
283     mDetachOnExit = true;
284
285     for (auto& container : mContainers) {
286         container.second->setDetachOnExit();
287     }
288 }
289
290 void ContainersManager::notifyActiveContainerHandler(const std::string& caller,
291                                                      const std::string& application,
292                                                      const std::string& message)
293 {
294     LOGI("notifyActiveContainerHandler(" << caller << ", " << application << ", " << message
295          << ") called");
296     try {
297         const std::string activeContainer = getRunningForegroundContainerId();
298         if (!activeContainer.empty() && caller != activeContainer) {
299             mContainers[activeContainer]->sendNotification(caller, application, message);
300         }
301     } catch(const SecurityContainersException&) {
302         LOGE("Notification from " << caller << " hasn't been sent");
303     }
304 }
305
306 void ContainersManager::displayOffHandler(const std::string& /*caller*/)
307 {
308     // get config of currently set container and switch if switchToDefaultAfterTimeout is true
309     const std::string activeContainerName = getRunningForegroundContainerId();
310     const auto& activeContainer = mContainers.find(activeContainerName);
311
312     if (activeContainer != mContainers.end() &&
313         activeContainer->second->isSwitchToDefaultAfterTimeoutAllowed()) {
314         LOGI("Switching to default container " << mConfig.defaultId);
315         focus(mConfig.defaultId);
316     }
317 }
318
319 void ContainersManager::handleContainerMoveFileRequest(const std::string& srcContainerId,
320                                                        const std::string& dstContainerId,
321                                                        const std::string& path,
322                                                        dbus::MethodResultBuilder::Pointer result)
323 {
324     // TODO: this implementation is only a placeholder.
325     // There are too many unanswered questions and security concerns:
326     // 1. What about mount namespace, host might not see the source/destination
327     //    file. The file might be a different file from a host perspective.
328     // 2. Copy vs move (speed and security concerns over already opened FDs)
329     // 3. Access to source and destination files - DAC, uid/gig
330     // 4. Access to source and destintation files - MAC, smack
331     // 5. Destination file uid/gid assignment
332     // 6. Destination file smack label assignment
333     // 7. Verifiability of the source path
334
335     // NOTE: other possible implementations include:
336     // 1. Sending file descriptors opened directly in each container through DBUS
337     //    using something like g_dbus_message_set_unix_fd_list()
338     // 2. SCS forking and calling setns(MNT) in each container and opening files
339     //    by itself, then passing FDs to the main process
340     // Now when the main process has obtained FDs (by either of those methods)
341     // it can do the copying by itself.
342
343     LOGI("File move requested\n"
344          << "src: " << srcContainerId << "\n"
345          << "dst: " << dstContainerId << "\n"
346          << "path: " << path);
347
348     ContainerMap::const_iterator srcIter = mContainers.find(srcContainerId);
349     if (srcIter == mContainers.end()) {
350         LOGE("Source container '" << srcContainerId << "' not found");
351         return;
352     }
353     Container& srcContainer = *srcIter->second;
354
355     ContainerMap::const_iterator dstIter = mContainers.find(dstContainerId);
356     if (dstIter == mContainers.end()) {
357         LOGE("Destination container '" << dstContainerId << "' not found");
358         result->set(g_variant_new("(s)", api::container::FILE_MOVE_DESTINATION_NOT_FOUND.c_str()));
359         return;
360     }
361     Container& dstContanier = *dstIter->second;
362
363     if (srcContainerId == dstContainerId) {
364         LOGE("Cannot send a file to yourself");
365         result->set(g_variant_new("(s)", api::container::FILE_MOVE_WRONG_DESTINATION.c_str()));
366         return;
367     }
368
369     if (!regexMatchVector(path, srcContainer.getPermittedToSend())) {
370         LOGE("Source container has no permissions to send the file: " << path);
371         result->set(g_variant_new("(s)", api::container::FILE_MOVE_NO_PERMISSIONS_SEND.c_str()));
372         return;
373     }
374
375     if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
376         LOGE("Destination container has no permissions to receive the file: " << path);
377         result->set(g_variant_new("(s)", api::container::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str()));
378         return;
379     }
380
381     namespace fs = boost::filesystem;
382     std::string srcPath = fs::absolute(srcContainerId, mConfig.containersPath).string() + path;
383     std::string dstPath = fs::absolute(dstContainerId, mConfig.containersPath).string() + path;
384
385     if (!utils::moveFile(srcPath, dstPath)) {
386         LOGE("Failed to move the file: " << path);
387         result->set(g_variant_new("(s)", api::container::FILE_MOVE_FAILED.c_str()));
388     } else {
389         result->set(g_variant_new("(s)", api::container::FILE_MOVE_SUCCEEDED.c_str()));
390         try {
391             dstContanier.sendNotification(srcContainerId, path, api::container::FILE_MOVE_SUCCEEDED);
392         } catch (ServerException&) {
393             LOGE("Notification to '" << dstContainerId << "' has not been sent");
394         }
395     }
396 }
397
398 void ContainersManager::handleProxyCall(const std::string& caller,
399                                         const std::string& target,
400                                         const std::string& targetBusName,
401                                         const std::string& targetObjectPath,
402                                         const std::string& targetInterface,
403                                         const std::string& targetMethod,
404                                         GVariant* parameters,
405                                         dbus::MethodResultBuilder::Pointer result)
406 {
407     if (!mProxyCallPolicy->isProxyCallAllowed(caller,
408                                               target,
409                                               targetBusName,
410                                               targetObjectPath,
411                                               targetInterface,
412                                               targetMethod)) {
413         LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
414                 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
415         result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
416         return;
417     }
418
419     LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
420             << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
421
422     auto asyncResultCallback = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) {
423         try {
424             GVariant* targetResult = asyncMethodCallResult.get();
425             result->set(g_variant_new("(v)", targetResult));
426         } catch (dbus::DbusException& e) {
427             result->setError(api::ERROR_FORWARDED, e.what());
428         }
429     };
430
431     if (target == HOST_ID) {
432         mHostConnection.proxyCallAsync(targetBusName,
433                                        targetObjectPath,
434                                        targetInterface,
435                                        targetMethod,
436                                        parameters,
437                                        asyncResultCallback);
438         return;
439     }
440
441     ContainerMap::const_iterator targetIter = mContainers.find(target);
442     if (targetIter == mContainers.end()) {
443         LOGE("Target container '" << target << "' not found");
444         result->setError(api::ERROR_UNKNOWN_ID, "Unknown proxy call target");
445         return;
446     }
447
448     Container& targetContainer = *targetIter->second;
449     targetContainer.proxyCallAsync(targetBusName,
450                                    targetObjectPath,
451                                    targetInterface,
452                                    targetMethod,
453                                    parameters,
454                                    asyncResultCallback);
455 }
456
457 void ContainersManager::handleGetContainerDbuses(dbus::MethodResultBuilder::Pointer result)
458 {
459     std::vector<GVariant*> entries;
460     for (auto& container : mContainers) {
461         GVariant* containerId = g_variant_new_string(container.first.c_str());
462         GVariant* dbusAddress = g_variant_new_string(container.second->getDbusAddress().c_str());
463         GVariant* entry = g_variant_new_dict_entry(containerId, dbusAddress);
464         entries.push_back(entry);
465     }
466     GVariant* dict = g_variant_new_array(G_VARIANT_TYPE("{ss}"), entries.data(), entries.size());
467     result->set(g_variant_new("(*)", dict));
468 }
469
470 void ContainersManager::handleDbusStateChanged(const std::string& containerId,
471                                                const std::string& dbusAddress)
472 {
473     mHostConnection.signalContainerDbusState(containerId, dbusAddress);
474 }
475
476 void ContainersManager::handleGetContainerIdsCall(dbus::MethodResultBuilder::Pointer result)
477 {
478     std::vector<GVariant*> containerIds;
479     for(auto& container: mContainers){
480         containerIds.push_back(g_variant_new_string(container.first.c_str()));
481     }
482
483     GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"),
484                                           containerIds.data(),
485                                           containerIds.size());
486     result->set(g_variant_new("(*)", array));
487 }
488
489 void ContainersManager::handleGetActiveContainerIdCall(dbus::MethodResultBuilder::Pointer result)
490 {
491     LOGI("GetActiveContainerId call");
492     if (mContainers[mConfig.foregroundId]->isRunning()){
493         result->set(g_variant_new("(s)", mConfig.foregroundId.c_str()));
494     } else {
495         result->set(g_variant_new("(s)", ""));
496     }
497 }
498
499 void ContainersManager::handleSetActiveContainerCall(const std::string& id,
500                                                      dbus::MethodResultBuilder::Pointer result)
501 {
502     LOGI("SetActiveContainer call; Id=" << id );
503     auto container = mContainers.find(id);
504     if (container == mContainers.end()){
505         LOGE("No container with id=" << id );
506         result->setError(api::ERROR_UNKNOWN_ID, "No such container id");
507         return;
508     }
509
510     if (container->second->isStopped()){
511         LOGE("Could not activate a stopped container");
512         result->setError(api::host::ERROR_CONTAINER_STOPPED,
513                          "Could not activate a stopped container");
514         return;
515     }
516
517     focus(id);
518     result->setVoid();
519 }
520
521
522 void ContainersManager::generateNewConfig(const std::string& id,
523                                           const std::string& templatePath,
524                                           const std::string& resultPath)
525 {
526     namespace fs = boost::filesystem;
527
528     std::string resultFileDir = utils::dirName(resultPath);
529     if (!fs::exists(resultFileDir)) {
530         if (!utils::createEmptyDir(resultFileDir)) {
531             LOGE("Unable to create directory for new config.");
532             throw ContainerOperationException("Unable to create directory for new config.");
533         }
534     }
535
536     fs::path resultFile(resultPath);
537     if (fs::exists(resultFile)) {
538         LOGT(resultPath << " already exists, removing");
539         fs::remove(resultFile);
540     }
541
542     std::string config;
543     if (!utils::readFileContent(templatePath, config)) {
544         LOGE("Failed to read template config file.");
545         throw ContainerOperationException("Failed to read template config file.");
546     }
547
548     std::string resultConfig = boost::regex_replace(config, CONTAINER_NAME_REGEX, id);
549
550     boost::uuids::uuid u = boost::uuids::random_generator()();
551     std::string uuidStr = to_string(u);
552     LOGD("uuid: " << uuidStr);
553     resultConfig = boost::regex_replace(resultConfig, CONTAINER_UUID_REGEX, uuidStr);
554
555     // generate third IP octet for network config
556     std::string thirdOctetStr = std::to_string(CONTAINER_IP_BASE_THIRD_OCTET + mContainers.size() + 1);
557     LOGD("ip_third_octet: " << thirdOctetStr);
558     resultConfig = boost::regex_replace(resultConfig, CONTAINER_IP_THIRD_OCTET_REGEX, thirdOctetStr);
559
560     if (!utils::saveFileContent(resultPath, resultConfig)) {
561         LOGE("Faield to save new config file.");
562         throw ContainerOperationException("Failed to save new config file.");
563     }
564
565     // restrict new config file so that only owner (security-containers) can write it
566     fs::permissions(resultPath, fs::perms::owner_all |
567                                 fs::perms::group_read |
568                                 fs::perms::others_read);
569 }
570
571 void ContainersManager::handleAddContainerCall(const std::string& id,
572                                                dbus::MethodResultBuilder::Pointer result)
573 {
574     LOGI("Adding container " << id);
575
576     // TODO: This solution is temporary. It utilizes direct access to config files when creating new
577     // containers. Update this handler when config database will appear.
578     namespace fs = boost::filesystem;
579
580     boost::system::error_code ec;
581     const std::string containerPathStr = utils::createFilePath(mConfig.containersPath, "/", id, "/");
582
583     // check if container does not exist
584     if (mContainers.find(id) != mContainers.end()) {
585         LOGE("Cannot create " << id << " container - already exists!");
586         result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
587                          "Cannot create " + id + " container - already exists!");
588         return;
589     }
590
591     // copy container image if config contains path to image
592     LOGT("image path: " << mConfig.containerImagePath);
593     if (!mConfig.containerImagePath.empty()) {
594         if (!utils::copyImageContents(mConfig.containerImagePath, containerPathStr)) {
595             LOGE("Failed to copy container image.");
596             result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
597                             "Failed to copy container image.");
598             return;
599         }
600     }
601
602     // generate paths to new configuration files
603     std::string baseDir = utils::dirName(mConfigPath);
604     std::string configDir = utils::getAbsolutePath(mConfig.containerNewConfigPrefix, baseDir);
605     std::string templateDir = utils::getAbsolutePath(mConfig.containerTemplatePath, baseDir);
606
607     std::string configPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_CONFIG_PATH);
608     std::string newConfigPath = utils::createFilePath(configDir, "/containers/", id + ".conf");
609     std::string libvirtConfigPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_CONFIG_PATH);
610     std::string newLibvirtConfigPath = utils::createFilePath(configDir, "/libvirt-config/", id + ".xml");
611     std::string libvirtNetworkPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_NETWORK_PATH);
612     std::string newLibvirtNetworkPath = utils::createFilePath(configDir, "/libvirt-config/", id + "-network.xml");
613     std::string libvirtNetworkFilterPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_NETWORK_FILTER_PATH);
614     std::string newLibvirtNetworkFilterPath = utils::createFilePath(configDir, "/libvirt-config/", id + "-nwfilter.xml");
615
616     auto removeAllWrapper = [](const std::string& path) {
617         try {
618             LOGD("Removing copied data");
619             fs::remove_all(fs::path(path));
620         } catch(const boost::exception& e) {
621             LOGW("Failed to remove data: " << boost::diagnostic_information(e));
622         }
623     };
624
625     try {
626         LOGI("Generating config from " << configPath << " to " << newConfigPath);
627         generateNewConfig(id, configPath, newConfigPath);
628
629         LOGI("Generating config from " << libvirtConfigPath << " to " << newLibvirtConfigPath);
630         generateNewConfig(id, libvirtConfigPath, newLibvirtConfigPath);
631
632         LOGI("Generating config from " << libvirtNetworkPath << " to " << newLibvirtNetworkPath);
633         generateNewConfig(id, libvirtNetworkPath, newLibvirtNetworkPath);
634
635         LOGI("Generating config from " << libvirtNetworkFilterPath << " to " << newLibvirtNetworkFilterPath);
636         generateNewConfig(id, libvirtNetworkFilterPath, newLibvirtNetworkFilterPath);
637     } catch (SecurityContainersException& e) {
638         LOGE(e.what());
639         removeAllWrapper(containerPathStr);
640         result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what());
641         return;
642     }
643
644     LOGT("Adding new container");
645     try {
646         addContainer(newConfigPath);
647     } catch (SecurityContainersException& e) {
648         LOGE(e.what());
649         removeAllWrapper(containerPathStr);
650         result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what());
651         return;
652     }
653
654     auto resultCallback = [result, containerPathStr, removeAllWrapper](bool succeeded) {
655         if (succeeded) {
656             result->setVoid();
657         } else {
658             LOGE("Failed to start container.");
659             removeAllWrapper(containerPathStr);
660             result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
661                              "Failed to start container.");
662         }
663     };
664     mContainers[id]->startAsync(resultCallback);
665 }
666
667 } // namespace security_containers