Add API to create new containers
[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 (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     for (auto& container : mContainers) {
187         LOGD(container.second->getId() << ": being sent to background");
188         container.second->goBackground();
189     }
190     mConfig.foregroundId = foregroundContainer->getId();
191     LOGD(mConfig.foregroundId << ": being sent to foreground");
192     foregroundContainer->goForeground();
193 }
194
195 void ContainersManager::startAll()
196 {
197     LOGI("Starting all containers");
198
199     bool isForegroundFound = false;
200
201     for (auto& container : mContainers) {
202         container.second->start();
203
204         if (container.first == mConfig.foregroundId) {
205             isForegroundFound = true;
206             LOGI(container.second->getId() << ": set as the foreground container");
207             container.second->goForeground();
208         }
209     }
210
211     if (!isForegroundFound) {
212         auto foregroundIterator = std::min_element(mContainers.begin(), mContainers.end(),
213                                                    [](ContainerMap::value_type &c1, ContainerMap::value_type &c2) {
214                                                        return c1.second->getPrivilege() < c2.second->getPrivilege();
215                                                    });
216
217         mConfig.foregroundId = foregroundIterator->second->getId();
218         LOGI(mConfig.foregroundId << ": no foreground container configured, setting one with highest priority");
219         foregroundIterator->second->goForeground();
220     }
221 }
222
223 void ContainersManager::stopAll()
224 {
225     LOGI("Stopping all containers");
226
227     for (auto& container : mContainers) {
228         container.second->stop();
229     }
230 }
231
232 std::string ContainersManager::getRunningForegroundContainerId()
233 {
234     for (auto& container : mContainers) {
235         if (container.first == mConfig.foregroundId &&
236             container.second->isRunning()) {
237             return container.first;
238         }
239     }
240     return std::string();
241 }
242
243 void ContainersManager::switchingSequenceMonitorNotify()
244 {
245     LOGI("switchingSequenceMonitorNotify() called");
246     // TODO: implement
247 }
248
249
250 void ContainersManager::setContainersDetachOnExit()
251 {
252     mDetachOnExit = true;
253
254     for (auto& container : mContainers) {
255         container.second->setDetachOnExit();
256     }
257 }
258
259 void ContainersManager::notifyActiveContainerHandler(const std::string& caller,
260                                                      const std::string& application,
261                                                      const std::string& message)
262 {
263     LOGI("notifyActiveContainerHandler(" << caller << ", " << application << ", " << message
264          << ") called");
265     try {
266         const std::string activeContainer = getRunningForegroundContainerId();
267         if (!activeContainer.empty() && caller != activeContainer) {
268             mContainers[activeContainer]->sendNotification(caller, application, message);
269         }
270     } catch(const SecurityContainersException&) {
271         LOGE("Notification from " << caller << " hasn't been sent");
272     }
273 }
274
275 void ContainersManager::displayOffHandler(const std::string& /*caller*/)
276 {
277     // get config of currently set container and switch if switchToDefaultAfterTimeout is true
278     const std::string activeContainerName = getRunningForegroundContainerId();
279     const auto& activeContainer = mContainers.find(activeContainerName);
280
281     if (activeContainer != mContainers.end() &&
282         activeContainer->second->isSwitchToDefaultAfterTimeoutAllowed()) {
283         LOGI("Switching to default container " << mConfig.defaultId);
284         focus(mConfig.defaultId);
285     }
286 }
287
288 void ContainersManager::handleContainerMoveFileRequest(const std::string& srcContainerId,
289                                                        const std::string& dstContainerId,
290                                                        const std::string& path,
291                                                        dbus::MethodResultBuilder::Pointer result)
292 {
293     // TODO: this implementation is only a placeholder.
294     // There are too many unanswered questions and security concerns:
295     // 1. What about mount namespace, host might not see the source/destination
296     //    file. The file might be a different file from a host perspective.
297     // 2. Copy vs move (speed and security concerns over already opened FDs)
298     // 3. Access to source and destination files - DAC, uid/gig
299     // 4. Access to source and destintation files - MAC, smack
300     // 5. Destination file uid/gid assignment
301     // 6. Destination file smack label assignment
302     // 7. Verifiability of the source path
303
304     // NOTE: other possible implementations include:
305     // 1. Sending file descriptors opened directly in each container through DBUS
306     //    using something like g_dbus_message_set_unix_fd_list()
307     // 2. SCS forking and calling setns(MNT) in each container and opening files
308     //    by itself, then passing FDs to the main process
309     // Now when the main process has obtained FDs (by either of those methods)
310     // it can do the copying by itself.
311
312     LOGI("File move requested\n"
313          << "src: " << srcContainerId << "\n"
314          << "dst: " << dstContainerId << "\n"
315          << "path: " << path);
316
317     ContainerMap::const_iterator srcIter = mContainers.find(srcContainerId);
318     if (srcIter == mContainers.end()) {
319         LOGE("Source container '" << srcContainerId << "' not found");
320         return;
321     }
322     Container& srcContainer = *srcIter->second;
323
324     ContainerMap::const_iterator dstIter = mContainers.find(dstContainerId);
325     if (dstIter == mContainers.end()) {
326         LOGE("Destination container '" << dstContainerId << "' not found");
327         result->set(g_variant_new("(s)", api::container::FILE_MOVE_DESTINATION_NOT_FOUND.c_str()));
328         return;
329     }
330     Container& dstContanier = *dstIter->second;
331
332     if (srcContainerId == dstContainerId) {
333         LOGE("Cannot send a file to yourself");
334         result->set(g_variant_new("(s)", api::container::FILE_MOVE_WRONG_DESTINATION.c_str()));
335         return;
336     }
337
338     if (!regexMatchVector(path, srcContainer.getPermittedToSend())) {
339         LOGE("Source container has no permissions to send the file: " << path);
340         result->set(g_variant_new("(s)", api::container::FILE_MOVE_NO_PERMISSIONS_SEND.c_str()));
341         return;
342     }
343
344     if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
345         LOGE("Destination container has no permissions to receive the file: " << path);
346         result->set(g_variant_new("(s)", api::container::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str()));
347         return;
348     }
349
350     namespace fs = boost::filesystem;
351     std::string srcPath = fs::absolute(srcContainerId, mConfig.containersPath).string() + path;
352     std::string dstPath = fs::absolute(dstContainerId, mConfig.containersPath).string() + path;
353
354     if (!utils::moveFile(srcPath, dstPath)) {
355         LOGE("Failed to move the file: " << path);
356         result->set(g_variant_new("(s)", api::container::FILE_MOVE_FAILED.c_str()));
357     } else {
358         result->set(g_variant_new("(s)", api::container::FILE_MOVE_SUCCEEDED.c_str()));
359         try {
360             dstContanier.sendNotification(srcContainerId, path, api::container::FILE_MOVE_SUCCEEDED);
361         } catch (ServerException&) {
362             LOGE("Notification to '" << dstContainerId << "' has not been sent");
363         }
364     }
365 }
366
367 void ContainersManager::handleProxyCall(const std::string& caller,
368                                         const std::string& target,
369                                         const std::string& targetBusName,
370                                         const std::string& targetObjectPath,
371                                         const std::string& targetInterface,
372                                         const std::string& targetMethod,
373                                         GVariant* parameters,
374                                         dbus::MethodResultBuilder::Pointer result)
375 {
376     if (!mProxyCallPolicy->isProxyCallAllowed(caller,
377                                               target,
378                                               targetBusName,
379                                               targetObjectPath,
380                                               targetInterface,
381                                               targetMethod)) {
382         LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
383                 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
384         result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
385         return;
386     }
387
388     LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
389             << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
390
391     auto asyncResultCallback = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) {
392         try {
393             GVariant* targetResult = asyncMethodCallResult.get();
394             result->set(g_variant_new("(v)", targetResult));
395         } catch (dbus::DbusException& e) {
396             result->setError(api::ERROR_FORWARDED, e.what());
397         }
398     };
399
400     if (target == HOST_ID) {
401         mHostConnection.proxyCallAsync(targetBusName,
402                                        targetObjectPath,
403                                        targetInterface,
404                                        targetMethod,
405                                        parameters,
406                                        asyncResultCallback);
407         return;
408     }
409
410     ContainerMap::const_iterator targetIter = mContainers.find(target);
411     if (targetIter == mContainers.end()) {
412         LOGE("Target container '" << target << "' not found");
413         result->setError(api::ERROR_UNKNOWN_ID, "Unknown proxy call target");
414         return;
415     }
416
417     Container& targetContainer = *targetIter->second;
418     targetContainer.proxyCallAsync(targetBusName,
419                                    targetObjectPath,
420                                    targetInterface,
421                                    targetMethod,
422                                    parameters,
423                                    asyncResultCallback);
424 }
425
426 void ContainersManager::handleGetContainerDbuses(dbus::MethodResultBuilder::Pointer result)
427 {
428     std::vector<GVariant*> entries;
429     for (auto& container : mContainers) {
430         GVariant* containerId = g_variant_new_string(container.first.c_str());
431         GVariant* dbusAddress = g_variant_new_string(container.second->getDbusAddress().c_str());
432         GVariant* entry = g_variant_new_dict_entry(containerId, dbusAddress);
433         entries.push_back(entry);
434     }
435     GVariant* dict = g_variant_new_array(G_VARIANT_TYPE("{ss}"), entries.data(), entries.size());
436     result->set(g_variant_new("(*)", dict));
437 }
438
439 void ContainersManager::handleDbusStateChanged(const std::string& containerId,
440                                                const std::string& dbusAddress)
441 {
442     mHostConnection.signalContainerDbusState(containerId, dbusAddress);
443 }
444
445 void ContainersManager::handleGetContainerIdsCall(dbus::MethodResultBuilder::Pointer result)
446 {
447     std::vector<GVariant*> containerIds;
448     for(auto& container: mContainers){
449         containerIds.push_back(g_variant_new_string(container.first.c_str()));
450     }
451
452     GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"),
453                                           containerIds.data(),
454                                           containerIds.size());
455     result->set(g_variant_new("(*)", array));
456 }
457
458 void ContainersManager::handleGetActiveContainerIdCall(dbus::MethodResultBuilder::Pointer result)
459 {
460     LOGI("GetActiveContainerId call");
461     if (mContainers[mConfig.foregroundId]->isRunning()){
462         result->set(g_variant_new("(s)", mConfig.foregroundId.c_str()));
463     } else {
464         result->set(g_variant_new("(s)", ""));
465     }
466 }
467
468 void ContainersManager::handleSetActiveContainerCall(const std::string& id,
469                                                      dbus::MethodResultBuilder::Pointer result)
470 {
471     LOGI("SetActiveContainer call; Id=" << id );
472     auto container = mContainers.find(id);
473     if (container == mContainers.end()){
474         LOGE("No container with id=" << id );
475         result->setError(api::ERROR_UNKNOWN_ID, "No such container id");
476         return;
477     }
478
479     if (container->second->isStopped()){
480         LOGE("Could not activate a stopped container");
481         result->setError(api::host::ERROR_CONTAINER_STOPPED,
482                          "Could not activate a stopped container");
483         return;
484     }
485
486     focus(id);
487     result->setVoid();
488 }
489
490
491 void ContainersManager::generateNewConfig(const std::string& id,
492                                           const std::string& templatePath,
493                                           const std::string& resultPath)
494 {
495     namespace fs = boost::filesystem;
496
497     std::string resultFileDir = utils::dirName(resultPath);
498     if (!fs::exists(resultFileDir)) {
499         if (!utils::createEmptyDir(resultFileDir)) {
500             LOGE("Unable to create directory for new config.");
501             throw ContainerOperationException("Unable to create directory for new config.");
502         }
503     }
504
505     fs::path resultFile(resultPath);
506     if (fs::exists(resultFile)) {
507         LOGT(resultPath << " already exists, removing");
508         fs::remove(resultFile);
509     }
510
511     std::string config;
512     if (!utils::readFileContent(templatePath, config)) {
513         LOGE("Failed to read template config file.");
514         throw ContainerOperationException("Failed to read template config file.");
515     }
516
517     std::string resultConfig = boost::regex_replace(config, CONTAINER_NAME_REGEX, id);
518
519     boost::uuids::uuid u = boost::uuids::random_generator()();
520     std::string uuidStr = to_string(u);
521     LOGD("uuid: " << uuidStr);
522     resultConfig = boost::regex_replace(resultConfig, CONTAINER_UUID_REGEX, uuidStr);
523
524     // generate third IP octet for network config
525     std::string thirdOctetStr = std::to_string(CONTAINER_IP_BASE_THIRD_OCTET + mContainers.size() + 1);
526     LOGD("ip_third_octet: " << thirdOctetStr);
527     resultConfig = boost::regex_replace(resultConfig, CONTAINER_IP_THIRD_OCTET_REGEX, thirdOctetStr);
528
529     if (!utils::saveFileContent(resultPath, resultConfig)) {
530         LOGE("Faield to save new config file.");
531         throw ContainerOperationException("Failed to save new config file.");
532     }
533
534     // restrict new config file so that only owner (security-containers) can write it
535     fs::permissions(resultPath, fs::perms::owner_all |
536                                 fs::perms::group_read |
537                                 fs::perms::others_read);
538 }
539
540 void ContainersManager::handleAddContainerCall(const std::string& id,
541                                                dbus::MethodResultBuilder::Pointer result)
542 {
543     LOGI("Adding container " << id);
544
545     // TODO: This solution is temporary. It utilizes direct access to config files when creating new
546     // containers. Update this handler when config database will appear.
547     namespace fs = boost::filesystem;
548
549     boost::system::error_code ec;
550     const std::string containerPathStr = utils::createFilePath(mConfig.containersPath, "/", id, "/");
551
552     // check if container does not exist
553     if (mContainers.find(id) != mContainers.end()) {
554         LOGE("Cannot create " << id << " container - already exists!");
555         result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
556                          "Cannot create " + id + " container - already exists!");
557         return;
558     }
559
560     // copy container image if config contains path to image
561     LOGT("image path: " << mConfig.containerImagePath);
562     if (!mConfig.containerImagePath.empty()) {
563         if (!utils::copyImageContents(mConfig.containerImagePath, containerPathStr)) {
564             LOGE("Failed to copy container image.");
565             result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
566                             "Failed to copy container image.");
567             return;
568         }
569     }
570
571     // generate paths to new configuration files
572     std::string baseDir = utils::dirName(mConfigPath);
573     std::string configDir = utils::getAbsolutePath(mConfig.containerNewConfigPrefix, baseDir);
574     std::string templateDir = utils::getAbsolutePath(mConfig.containerTemplatePath, baseDir);
575
576     std::string configPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_CONFIG_PATH);
577     std::string newConfigPath = utils::createFilePath(configDir, "/containers/", id + ".conf");
578     std::string libvirtConfigPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_CONFIG_PATH);
579     std::string newLibvirtConfigPath = utils::createFilePath(configDir, "/libvirt-config/", id + ".xml");
580     std::string libvirtNetworkPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_NETWORK_PATH);
581     std::string newLibvirtNetworkPath = utils::createFilePath(configDir, "/libvirt-config/", id + "-network.xml");
582     std::string libvirtNetworkFilterPath = utils::createFilePath(templateDir, "/", CONTAINER_TEMPLATE_LIBVIRT_NETWORK_FILTER_PATH);
583     std::string newLibvirtNetworkFilterPath = utils::createFilePath(configDir, "/libvirt-config/", id + "-nwfilter.xml");
584
585     auto removeAllWrapper = [](const std::string& path) {
586         try {
587             LOGD("Removing copied data");
588             fs::remove_all(fs::path(path));
589         } catch(const boost::exception& e) {
590             LOGW("Failed to remove data: " << boost::diagnostic_information(e));
591         }
592     };
593
594     try {
595         LOGI("Generating config from " << configPath << " to " << newConfigPath);
596         generateNewConfig(id, configPath, newConfigPath);
597
598         LOGI("Generating config from " << libvirtConfigPath << " to " << newLibvirtConfigPath);
599         generateNewConfig(id, libvirtConfigPath, newLibvirtConfigPath);
600
601         LOGI("Generating config from " << libvirtNetworkPath << " to " << newLibvirtNetworkPath);
602         generateNewConfig(id, libvirtNetworkPath, newLibvirtNetworkPath);
603
604         LOGI("Generating config from " << libvirtNetworkFilterPath << " to " << newLibvirtNetworkFilterPath);
605         generateNewConfig(id, libvirtNetworkFilterPath, newLibvirtNetworkFilterPath);
606     } catch (SecurityContainersException& e) {
607         LOGE(e.what());
608         removeAllWrapper(containerPathStr);
609         result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what());
610         return;
611     }
612
613     LOGT("Adding new container");
614     try {
615         addContainer(newConfigPath);
616     } catch (SecurityContainersException& e) {
617         LOGE(e.what());
618         removeAllWrapper(containerPathStr);
619         result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED, e.what());
620         return;
621     }
622
623     auto resultCallback = [result, containerPathStr, removeAllWrapper](bool succeeded) {
624         if (succeeded) {
625             result->setVoid();
626         } else {
627             LOGE("Failed to start container.");
628             removeAllWrapper(containerPathStr);
629             result->setError(api::host::ERROR_CONTAINER_CREATE_FAILED,
630                              "Failed to start container.");
631         }
632     };
633     mContainers[id]->startAsync(resultCallback);
634 }
635
636 } // namespace security_containers