softwarecontainer  0.18.0-739e8d7 2017-05-04
container.cpp
1 /*
2  * Copyright (C) 2016-2017 Pelagicore AB
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
9  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
11  * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
12  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
13  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  *
17  * For further information see LICENSE
18  */
19 
20 
21 #include <vector>
22 #include <fstream>
23 #include <unistd.h>
24 
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <sys/mount.h>
28 
29 // rlimit stuff
30 #include <sys/time.h>
31 #include <sys/resource.h>
32 
33 #include <lxc/lxccontainer.h>
34 #include <lxc/version.h>
35 
36 #include <pwd.h>
37 #include <grp.h>
38 
39 #include <errno.h>
40 #include <string.h>
41 
42 #include <stdio.h>
43 #include <mntent.h>
44 
45 #include "container.h"
46 #include "filecleanuphandler.h"
47 
48 #include <libgen.h>
49 
50 #include "softwarecontainererror.h"
51 
52 namespace softwarecontainer {
53 
54 static constexpr const char *LXC_CONTAINERS_ROOT_CONFIG_ITEM = "lxc.lxcpath";
55 
56 std::vector<const char *> Container::s_LXCContainerStates;
57 const char *Container::s_LXCRoot;
58 
59 void Container::init_lxc()
60 {
61  static bool bInitialized = false;
62  if (!bInitialized) {
63  int stateCount = lxc_get_wait_states(nullptr);
64  s_LXCContainerStates.resize(stateCount);
65  lxc_get_wait_states(s_LXCContainerStates.data());
66  if ((int)LXCContainerState::ELEMENT_COUNT != s_LXCContainerStates.size()) {
67  log_error() << "Internal SC/LXC state mis-match, fatal error";
68  } else {
69  bInitialized = true;
70  s_LXCRoot = lxc_get_global_config_item(LXC_CONTAINERS_ROOT_CONFIG_ITEM);
71  }
72  }
73 }
74 
75 Container::Container(const std::string id,
76  const std::string &configFile,
77  const std::string &containerRoot,
78  bool writeBufferEnabled,
79  int shutdownTimeout) :
80  m_configFile(configFile),
81  m_id(id),
82  m_containerRoot(containerRoot),
83  m_writeBufferEnabled(writeBufferEnabled),
84  m_shutdownTimeout(shutdownTimeout)
85 {
86  init_lxc();
87  log_debug() << "Container constructed with " << id;
88 }
89 
90 Container::~Container()
91 {
92  if (m_container != nullptr) {
93  // These will check the current state
94 
95  if (m_state >= ContainerState::STARTED) {
96  shutdown();
97  }
98 
99  if (m_state >= ContainerState::CREATED) {
100  destroy();
101  }
102 
103  // Any existing container
104  lxc_container_put(m_container);
105  m_container = nullptr;
106  }
107 }
108 
110 {
111  if (m_state < ContainerState::PREPARED) {
112  std::string gatewayDir = gatewaysDir();
113  std::unique_ptr<CreateDir> createDirInstance = std::unique_ptr<CreateDir>(new CreateDir());
114  if (!createDirInstance->createDirectory(gatewayDir)) {
115  log_error() << "Could not create gateway directory "
116  << gatewayDir << ": " << strerror(errno);
117  return false;
118  }
119 
120  if (!createSharedMountPoint(gatewayDir)) {
121  log_error() << "Could not create shared mount point for dir: " << gatewayDir;
122  return false;
123  }
124  m_createDirList.push_back(std::move(createDirInstance));
125  m_state = ContainerState::PREPARED;
126  }
127  return true;
128 }
129 
130 std::string Container::toString()
131 {
132  std::stringstream ss;
133  ss << "LXC " << id() << " ";
134  if (m_container != nullptr) {
135  ss << "id: " << id()
136  << " / state:" << m_container->state(m_container)
137  << " / initPID:" << m_container->init_pid(m_container)
138  << " / LastError: " << m_container->error_string
139  << " / To connect to this container : lxc-attach -n " << id();
140  }
141 
142  return ss.str();
143 }
144 
146 {
147  if (m_state >= ContainerState::CREATED) {
148  log_warning() << "Container already created";
149  return false;
150  }
151 
152  log_debug() << "Creating container " << toString();
153 
154  const char *containerID = id();
155  if (strlen(containerID) == 0) {
156  log_error() << "ContainerID cannot be empty";
157  return false;
158  }
159 
160  setenv("GATEWAY_DIR", gatewaysDir().c_str(), true);
161  log_debug() << "GATEWAY_DIR : " << Glib::getenv("GATEWAY_DIR");
162 
163  auto configFile = m_configFile.c_str();
164  log_debug() << "Config file : " << configFile;
165  log_debug() << "Template : " << LXCTEMPLATE;
166  log_debug() << "creating container with ID : " << containerID;
167 
168  // Creating a new LXC pointer
169  // After this point all failures should do rollback
170  m_container = lxc_container_new(containerID, nullptr);
171  if (!m_container) {
172  log_error() << "Error creating a new container";
173  return rollbackCreate();
174  }
175 
176  if (m_container->is_defined(m_container)) {
177  log_error() << "ContainerID '" << containerID << "' is already in use.";
178  return rollbackCreate();
179  }
180  log_debug() << "Successfully created container struct";
181 
182  if (!m_container->load_config(m_container, configFile)) {
183  log_error() << "Error loading container config";
184  return rollbackCreate();
185 
186  }
187  log_debug() << "Successfully loaded container config";
188 
189  // File system stuff
190  m_rootFSPath = buildPath(s_LXCRoot, containerID, "rootfs");
191 
192  if (m_writeBufferEnabled) {
193  const std::string rootFSPathLower = m_containerRoot + "/rootfs-lower";
194  const std::string rootFSPathUpper = m_containerRoot + "/rootfs-upper";
195  const std::string rootFSPathWork = m_containerRoot + "/rootfs-work";
196 
197  overlayMount(rootFSPathLower, rootFSPathUpper, rootFSPathWork, m_rootFSPath);
198  log_debug() << "Write buffer enabled, lowerdir=" << rootFSPathLower
199  << ", upperdir=" << rootFSPathUpper
200  << ", workdir=" << rootFSPathWork
201  << ", dst=" << m_rootFSPath;
202  } else {
203  log_debug() << "WriteBuffer disabled, dst=" << m_rootFSPath;
204  }
205 
206  // Set the LXC template
207  int flags = 0;
208  std::vector<char *> argv;
209  if (!m_container->create(m_container, LXCTEMPLATE, nullptr, nullptr, flags, &argv[0])) {
210  log_error() << "Error creating container";
211  m_rootFSPath.assign("");
212  return rollbackCreate();
213  }
214 
215  // Everything went fine, set state and return successfully.
216  m_state = ContainerState::CREATED;
217  log_debug() << "Container created. RootFS: " << m_rootFSPath;
218 
219  return true;
220 }
221 
222 bool Container::rollbackCreate() {
223  if (m_container) {
224  lxc_container_put(m_container);
225  m_container = nullptr;
226  }
227  return false;
228 }
229 
230 bool Container::ensureContainerRunning()
231 {
232  if (ContainerState::FROZEN == m_state) {
233  log_error() << "Container is frozen, does not run";
234  return false;
235  }
236 
237  if (m_state < ContainerState::STARTED) {
238  log_error() << "Container is not in state STARTED, state is " << ((int)m_state);
239  log_error() << logging::getStackTrace();
240  return false;
241  }
242 
243  if (!m_container->is_running(m_container)) {
244  return waitForState(LXCContainerState::RUNNING);
245  }
246 
247  return true;
248 }
249 
250 bool Container::waitForState(LXCContainerState state, int timeout)
251 {
252  const char* currentState = m_container->state(m_container);
253  if (strcmp(currentState, toString(state))) {
254  log_debug() << "Waiting for container to change from " << currentState
255  << " to state : " << toString(state);
256  bool expired = m_container->wait(m_container, toString(state), timeout);
257  if (expired) {
258  log_error() << "Container did not reach" << toString(state) << " in time";
259  return false;
260  }
261  }
262  return true;
263 }
264 
265 bool Container::start(pid_t *pid)
266 {
267  if (m_state < ContainerState::CREATED) {
268  log_warning() << "Trying to start container that isn't created. Please create the container first";
269  return false;
270  }
271 
272  if (pid == nullptr) {
273  log_error() << "Supplied pid argument is nullptr";
274  return false;
275  }
276 
277  // For some reason the LXC start function does not work out
278  log_debug() << "Starting container";
279 
280  char commandEnv[] = "env";
281  char commandSleep[] = "/bin/sleep";
282  char commandSleepTime[] = "100000000";
283  char* const args[] = { commandEnv, commandSleep, commandSleepTime, nullptr };
284 
285  if (!m_container->start(m_container, false, args)) {
286  log_error() << "Error starting container";
287  return false;
288  }
289 
290  log_debug() << "Container started: " << toString();
291  *pid = m_container->init_pid(m_container);
292  m_state = ContainerState::STARTED;
293 
294  if (!ensureContainerRunning()) {
295  log_error() << "Container started but is not running";
296  return false;
297  }
298 
299  log_info() << "To connect to this container : lxc-attach -n " << id();
300  return true;
301 }
302 
303 int Container::unlimitCoreDump()
304 {
305  struct rlimit rlim;
306  if (getrlimit(RLIMIT_CORE, &rlim) != 0) {
307  return errno;
308  }
309 
310  // Set this to the maximum allowed value (ulimit -c unlimited if root)
311  rlim.rlim_cur = rlim.rlim_max;
312  if (setrlimit(RLIMIT_CORE, &rlim) != 0) {
313  return errno;
314  }
315 
316  return 0;
317 }
318 
319 bool Container::setCgroupItem(std::string subsys, std::string value)
320 {
321  return m_container->set_cgroup_item(m_container, subsys.c_str(), value.c_str());
322 }
323 
324 int Container::executeInContainerEntryFunction(void *param)
325 {
326  int canCoreDump = unlimitCoreDump();
327  if (canCoreDump != 0) {
328  return canCoreDump;
329  }
330 
331  ExecFunction *function = (ExecFunction *) param;
332  return (*function)();
333 }
334 
335 bool Container::executeSync(ExecFunction function,
336  pid_t *pid,
337  const EnvironmentVariables &variables,
338  int stdin_var,
339  int stdout_var,
340  int stderr_var)
341 {
342  bool execResult = execute(function, pid, variables, stdin_var, stdout_var, stderr_var);
343 
344  // Make sure the container execution finishes and was successful
345  int status = waitForProcessTermination(*pid);
346  return execResult && (0 == status);
347 }
348 
349 bool Container::execute(ExecFunction function,
350  pid_t *pid,
351  const EnvironmentVariables &variables,
352  int stdin,
353  int stdout,
354  int stderr)
355 {
356  if (pid == nullptr) {
357  log_error() << "Supplied pid argument is nullptr";
358  return false;
359  }
360 
361  if (!ensureContainerRunning()) {
362  log_error() << "Container is not running or in bad state, can't execute";
363  return false;
364  }
365 
366  lxc_attach_options_t options = LXC_ATTACH_OPTIONS_DEFAULT;
367  options.stdin_fd = stdin;
368  options.stdout_fd = stdout;
369  options.stderr_fd = stderr;
370 
371  options.uid = ROOT_UID;
372  options.gid = ROOT_UID;
373 
374  // List of vars to use when executing the function
375  EnvironmentVariables actualVariables = variables;
376 
377  // Add the variables set by gateways, variables passed with the 'variables' argument
378  // will take precedence over previously set variables.
379  for (auto &var : m_gatewayEnvironmentVariables) {
380  if (variables.count(var.first) != 0) {
381  if (m_gatewayEnvironmentVariables.at(var.first) != variables.at(var.first)) {
382  // Inform user that GW config will be overridden, it might be unintentionally done
383  log_info() << "Variable \"" << var.first
384  << "\" set by gateway will be overwritten with the value: \""
385  << variables.at(var.first) << "\"";
386  }
387  actualVariables[var.first] = variables.at(var.first);
388  } else {
389  // The variable was not set again, just keep the original value set by GW
390  actualVariables[var.first] = var.second;
391  }
392  }
393 
394  log_debug() << "Starting function in container " << toString();
395 
396  // prepare array of env variable strings to be set when launching the process in the container
397  std::vector<std::string> strings;
398  for (auto &var : actualVariables) {
399  strings.push_back(var.first + "=" + var.second);
400  }
401 
402  const size_t stringCount = strings.size() + 1;
403  const char **envVariablesArray = new const char* [stringCount];
404  for (size_t i = 0; i < strings.size(); i++) {
405  envVariablesArray[i] = strings[i].c_str();
406  log_debug() << "Passing env variable: " << strings[i];
407  }
408  // Null terminate
409  envVariablesArray[strings.size()] = nullptr;
410  options.extra_env_vars = (char **)envVariablesArray;
411 
412  // Do the actual attach call to the container
413  int attach_res = m_container->attach(m_container,
414  &Container::executeInContainerEntryFunction,
415  &function,
416  &options,
417  pid);
418 
419  delete[] envVariablesArray;
420  if (attach_res == 0) {
421  log_info() << " Attached PID: " << *pid;
422  return true;
423  } else {
424  log_error() << "Attach call to LXC container failed: " << std::string(strerror(errno));
425  return false;
426  }
427 }
428 
429 bool Container::execute(const std::string &commandLine, pid_t *pid,
430  const EnvironmentVariables &variables,
431  const std::string &workingDirectory,
432  int stdin, int stdout, int stderr)
433 {
434  if (!ensureContainerRunning()) {
435  log_error() << "Container is not running or in bad state, can't attach";
436  return false;
437  }
438 
439  if (pid == nullptr) {
440  log_error() << "Supplied pid argument is nullptr";
441  return false;
442  }
443 
444  log_debug() << "Attach " << commandLine ;
445 
446  std::vector<std::string> executeCommandVec = Glib::shell_parse_argv(commandLine);
447  std::vector<char *> args;
448 
449  for (size_t i = 0; i < executeCommandVec.size(); i++) {
450  executeCommandVec[i].c_str(); // ensure the string is null-terminated. not sure thas is required.
451  auto s = &executeCommandVec[i][0];
452  args.push_back(s);
453  }
454 
455  // We need a null terminated array
456  args.push_back(nullptr);
457 
458  bool result = execute([&] () {
459  if (workingDirectory.length() != 0) {
460  auto ret = chdir(workingDirectory.c_str());
461  if (ret != 0) {
462  log_error() << "Error when changing current directory : "
463  << strerror(errno);
464  }
465  }
466  execvp(args[0], args.data());
467  return 1;
468  }, pid, variables, stdin, stdout, stderr);
469  if (!result) {
470  log_error() << "Could not execute in container";
471  return false;
472  }
473 
474  return true;
475 }
476 
478 {
479  bool ret = true;
480  if (m_state >= ContainerState::STARTED) {
481  log_debug() << "Stopping the container";
482  if (m_container->stop(m_container)) {
483  log_debug() << "Container stopped, waiting for stop state";
484  waitForState(LXCContainerState::STOPPED);
485  } else {
486  log_error() << "Unable to stop container";
487  ret = false;
488  }
489  } else {
490  log_error() << "Can't stop container that has not been started";
491  ret = false;
492  }
493 
494  return ret;
495 }
496 
498 {
499  return shutdown(m_shutdownTimeout);
500 }
501 
502 bool Container::shutdown(unsigned int timeout)
503 {
504  if (m_state < ContainerState::STARTED) {
505  log_error() << "Trying to shutdown container that has not been started. Aborting";
506  return false;
507  }
508 
509  log_debug() << "Shutting down container " << toString() << " pid: "
510  << m_container->init_pid(m_container);
511 
512  if (m_container->init_pid(m_container) != INVALID_PID) {
513  kill(m_container->init_pid(m_container), SIGTERM);
514  }
515 
516  // Shutdown with timeout
517  bool success = m_container->shutdown(m_container, timeout);
518  if (!success) {
519  log_warning() << "Failed to cleanly shutdown container, forcing stop" << toString();
520  if(!stop()) {
521  log_error() << "Failed to force stop the container" << toString();
522  return false;
523  }
524  }
525 
526  m_state = ContainerState::CREATED;
527  return true;
528 }
529 
531 {
532  return destroy(m_shutdownTimeout);
533 }
534 
535 bool Container::destroy(unsigned int timeout)
536 {
537  if (m_state < ContainerState::CREATED) {
538  log_error() << "Trying to destroy container that has not been created. Aborting destroy";
539  return false;
540  }
541 
542  if (m_state >= ContainerState::STARTED) {
543  if (!shutdown(timeout)) {
544  log_error() << "Could not shutdown container. Aborting destroy";
545  return false;
546  }
547  }
548 
549 
550  // The container can not be destroyed unless the rootfs is unmounted
551  if (m_writeBufferEnabled)
552  {
553  log_debug() << "Unmounting the overlay rootfs";
554  if(-1 == umount(m_rootFSPath.c_str())) {
555  log_error() << "Unmounting the overlay rootfs failed: " << strerror(errno);
556  return false;
557  }
558  }
559 
560  // Destroy it!
561  bool success = m_container->destroy(m_container);
562  if (!success) {
563  log_error() << "Failed to destroy the container " << toString();
564  return false;
565  }
566 
567  m_state = ContainerState::DESTROYED;
568  return true;
569 }
570 
571 bool Container::bindMountInContainer(const std::string &pathInHost,
572  const std::string &pathInContainer,
573  bool readOnly)
574 {
575  if (!existsInFileSystem(pathInHost)) {
576  log_error() << "Path on host does not exist: " << pathInHost;
577  return false;
578  }
579 
580  // Check that there is nothing already mounted on the target path
581  pid_t pid = INVALID_PID;
582  bool checkIfAlreadyMountpoint = executeSync([pathInContainer] () {
583  FILE *mountsfile = setmntent("/proc/mounts", "r");
584  struct mntent *mounts;
585  while ((mounts = getmntent(mountsfile)) != nullptr) {
586  std::string mountDir(mounts->mnt_dir);
587  if (0 == mountDir.compare(pathInContainer)) {
588  return -1;
589  }
590  }
591  return 0;
592  }, &pid);
593 
594  if (!checkIfAlreadyMountpoint) {
595  log_error() << pathInContainer << " is already mounted to.";
596  return false;
597  }
598 
599  // Create a file to mount to in gateways
600  std::string filePart = baseName(pathInContainer);
601  std::string tempPath = buildPath(gatewaysDir(), filePart);
602  bool pathIsDirectory = false;
603  std::unique_ptr<CreateDir> createDirInstance = std::unique_ptr<CreateDir>(new CreateDir());
604  //
605  // If the path is a directory, we create the tempPath (which adds a cleanup handler).
606  //
607  // If it is a file, we touch the file and add a cleanup handler for it.
608  //
609  if (isDirectory(pathInHost)) {
610  pathIsDirectory = true;
611  log_debug() << "Path on host (" << pathInHost << ") is directory, mounting as a directory";
612 
613  log_debug() << "Creating folder : " << tempPath;
614  if (!createDirInstance->createDirectory(tempPath)) {
615  log_error() << "Could not create folder " << tempPath;
616  return false;
617  }
618  } else {
619  // This goes for sockets, fifos etc as well.
620  log_debug() << "Path on host (" << pathInHost << ") is not a directory, "
621  << "mounting assuming it behaves like a file";
622 
623  if (!pathInList(tempPath)) {
624  if (!touch(tempPath)) {
625  log_error() << "Could not create file " << tempPath;
626  return false;
627  }
628  }
629  }
630 
631  if (!bindMountCore(pathInHost, pathInContainer, tempPath, readOnly)) {
632  // bindMountInContainer operation is not successful. Clean mounted tempPath.
633  MountCleanUpHandler rollback{tempPath};
634  rollback.clean();
635  return false;
636  }
637 
638  // bindMountInContainer succeed. Add cleanup for mounted tempPath
639  m_createDirList.push_back(std::move(createDirInstance));
640  if (!pathIsDirectory) {
641  m_cleanupHandlers.emplace_back(new FileCleanUpHandler(tempPath));
642  }
643 
644  m_cleanupHandlers.emplace_back(new MountCleanUpHandler(tempPath));
645  return true;
646 }
647 
648 bool Container::bindMountCore(const std::string &pathInHost,
649  const std::string &pathInContainer,
650  const std::string &tempDirInContainerOnHost,
651  bool readonly)
652 {
653  if (!ensureContainerRunning()) {
654  log_error() << "Container is not running or in bad state, can't bind-mount folder";
655  return false;
656  }
657 
658  if (pathInContainer.front() != '/') {
659  log_error() << "Provided path '" << pathInContainer << "' is not absolute!";
660  return false;
661  }
662 
663  // Bind mount to /gateways
664  if (!bindMount(pathInHost,
665  tempDirInContainerOnHost,
666  m_containerRoot,
667  readonly,
668  m_writeBufferEnabled)) {
669  log_error() << "Could not bind mount " << pathInHost << " to " << tempDirInContainerOnHost;
670  return false;
671  }
672 
673  // Paths inside the container
674  std::string filePart = baseName(pathInContainer);
675  std::string tempDirInContainer = buildPath(gatewaysDirInContainer(), filePart);
676 
677  pid_t pid = INVALID_PID;
678 
679  // Utility lambda function to create parent paths of a given path
680  auto createParentDirectories = [this] (std::string path) {
681  std::stack<std::string> paths;
682  std::string currentPath = path;
683  while(!isDirectory(currentPath)) {
684  paths.push(currentPath);
685  currentPath = parentPath(currentPath);
686  }
687 
688  while(!paths.empty()) {
689  std::string path = paths.top();
690  if (mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
691  return 1;
692  }
693 
694  paths.pop();
695  }
696  return 0;
697  };
698 
699  // Move the mount in the container if the tempdir is not the desired dir
700  if (tempDirInContainer.compare(pathInContainer) != 0) {
701  //
702  // First, optionally create any parent directories to the targets
703  // We do this on the host to avoid working too much inside the container
704  //
705  std::string parentPathInContainer = parentPath(pathInContainer);
706  ExecFunction createParent = std::bind(createParentDirectories, parentPathInContainer);
707 
708  if (!executeSync(createParent, &pid)) {
709  log_error() << "Could not create parent directory " << parentPathInContainer
710  << " in container";
711  return false;
712  }
713 
714  //
715  // Then, create the actual directory / file to mount to.
716  //
717  if (isDirectory(tempDirInContainerOnHost)) {
718  ExecFunction createDir = std::bind(createParentDirectories, pathInContainer);
719 
720  if (!executeSync(createDir, &pid)) {
721  log_error() << "Could not create target directory " << pathInContainer
722  << " in the container";
723  return false;
724  }
725  } else {
726  log_debug() << "Touching file in container: " << pathInContainer;
727  bool touchResult = executeSync([pathInContainer] () {
728  return touch(pathInContainer) ? 0 : 1;
729  }, &pid);
730 
731  if (!touchResult) {
732  log_error() << "Could not touch target file " << pathInContainer
733  << " in the container";
734  return false;
735  }
736  }
737 
738  //
739  // And move the mount from /gateways to the desired location
740  //
741 
742  bool mountMoveResult = executeSync([tempDirInContainer, pathInContainer] () {
743  unsigned long flags = MS_MOVE;
744  int ret = mount(tempDirInContainer.c_str(), pathInContainer.c_str(), nullptr, flags, nullptr);
745  if (ret != 0) {
746  printf("Error while moving the mount %s to %s: %s\n",
747  tempDirInContainer.c_str(),
748  pathInContainer.c_str(),
749  strerror(errno));
750  }
751  return ret;
752  }, &pid);
753 
754  if (!mountMoveResult) {
755  log_error() << "Could not move the mount inside the container: "
756  << tempDirInContainer << " to " << pathInContainer;
757  return false;
758  }
759  }
760 
761  // Remount read only in the container if applicable
762  if (readonly && !remountReadOnlyInContainer(pathInContainer)) {
763  log_error() << "Failed to remount read only: " << pathInContainer;
764  return false;
765  }
766 
767  return true;
768 }
769 
770 bool Container::remountReadOnlyInContainer(const std::string &path)
771 {
772  pid_t pid = INVALID_PID;
773 
774  bool ret = executeSync([path] () {
775  unsigned long flags = MS_REMOUNT | MS_RDONLY | MS_BIND;
776  return mount(path.c_str(), path.c_str(), "", flags, nullptr);
777  }, &pid);
778 
779  if (!ret) {
780  log_error() << "Could not remount " << path << " read-only in container";
781  return false;
782  }
783 
784  return true;
785 }
786 
787 bool Container::mountDevice(const std::string &pathInHost)
788 {
789  if(!ensureContainerRunning()) {
790  log_error() << "Container is not running or in bad state, can't mount device: " << pathInHost;
791  return false;
792  }
793  log_debug() << "Mounting device in container : " << pathInHost;
794  return m_container->add_device_node(m_container, pathInHost.c_str(), nullptr);
795 }
796 
797 bool Container::setEnvironmentVariable(const std::string &var, const std::string &val)
798 {
799  if (m_state < ContainerState::CREATED) {
800  log_error() << "Can't set environment variable for non-created container";
801  return false;
802  }
803 
804  log_debug() << "Setting env variable in container " << var << "=" << val;
805  m_gatewayEnvironmentVariables[var] = val;
806 
807  // We generate a file containing all variables for convenience when connecting to the container in command-line
808  logging::StringBuilder s;
809  for (auto &var : m_gatewayEnvironmentVariables) {
810  s << "export " << var.first << "='" << var.second << "'\n";
811  }
812  std::string path = buildPath(gatewaysDir(), "env");
813  FileToolkitWithUndo::writeToFile(path, s);
814 
815  return true;
816 }
817 
818 bool Container::suspend()
819 {
820  if (m_state < ContainerState::STARTED) {
821  log_error() << "Container is not started yet";
822  return false;
823  }
824 
825  if (m_state == ContainerState::FROZEN) {
826  log_error() << "Container is already suspended";
827  return false;
828  }
829 
830  log_debug() << "Suspending container";
831  bool retval = m_container->freeze(m_container);
832 
833  if (!retval) {
834  std::string errorMessage("Could not suspend the container.");
835  log_error() << errorMessage;
836  throw SoftwareContainerError(errorMessage);
837  }
838 
839  m_state = ContainerState::FROZEN;
840  return true;
841 }
842 
843 bool Container::resume()
844 {
845  if (m_state < ContainerState::FROZEN) {
846  log_error() << "Container is not suspended";
847  return false;
848  }
849 
850  log_debug() << "Resuming container";
851  bool retval = m_container->unfreeze(m_container);
852 
853  if (!retval) {
854  std::string errorMessage("Could not resume the container.");
855  log_error() << errorMessage;
856  throw SoftwareContainerError(errorMessage);
857  }
858 
859  m_state = ContainerState::STARTED;
860  return true;
861 }
862 
863 const char *Container::id() const
864 {
865  return m_id.c_str();
866 }
867 
868 std::string Container::gatewaysDirInContainer() const
869 {
870  return GATEWAYS_PATH;
871 }
872 
873 std::string Container::gatewaysDir() const
874 {
875  return buildPath(m_containerRoot, GATEWAYS_PATH);
876 }
877 
878 } // namespace softwarecontainer
bool create()
create Creates a new lxc_container and creates it with all the initialization.
Definition: container.cpp:145
Container(const std::string id, const std::string &configFile, const std::string &containerRoot, bool writeBufferEnabled=false, int shutdownTimeout=1)
Constructor.
Definition: container.cpp:75
std::vector< std::unique_ptr< CleanUpHandler > > m_cleanupHandlers
m_cleanupHandlers A vector of cleanupHandlers added during the lifetime of the FileToolKitWithUndo th...
The FileCleanUpHandler class is a subclass of CleanUpHandler that deletes a file. ...
bool execute(const std::string &commandLine, pid_t *pid, const EnvironmentVariables &variables, const std::string &workingDirectory="/", int stdin=-1, int stdout=1, int stderr=2)
Start a process from the given command line, with an environment consisting of the variables previous...
Definition: container.cpp:429
The CreateDir class is responsible for creating new directories and removing them when it is necessar...
Definition: createdir.h:33
std::vector< std::unique_ptr< CreateDir > > m_createDirList
m_createDirList A vector of CreateDir classes.
bool overlayMount(const std::string &lower, const std::string &upper, const std::string &work, const std::string &dst)
overlayMount Mount a directory with an overlay on top of it.
bool stop()
Calls stop on the lxc container(force stop)
Definition: container.cpp:477
bool pathInList(const std::string path)
checks whether given path is already added to clean up handlers or not
bool executeSync(ExecFunction function, pid_t *pid, const EnvironmentVariables &variables=EnvironmentVariables(), int stdin=-1, int stdout=1, int stderr=2)
synchronous version of execute
Definition: container.cpp:335
bool shutdown()
Calls shutdown on the lxc container.
Definition: container.cpp:497
bool bindMountInContainer(const std::string &pathInHost, const std::string &pathInContainer, bool readOnly=true)
Tries to bind mount a path from host to container.
Definition: container.cpp:571
bool start(pid_t *pid)
Start the container.
Definition: container.cpp:265
bool isDirectory(const std::string &path)
isDirectory Check if path is a directory
bool createSharedMountPoint(const std::string &path)
createSharedMountPoint Make the mount point shared, ie new mount points created in one bind mount wil...
bool bindMount(const std::string &src, const std::string &dst, const std::string &tmpContainerRoot, bool readOnly, bool writeBufferEnabled=false)
bindMount Bind mount a src directory to another position dst.
bool initialize()
Setup the container for startup.
Definition: container.cpp:109
bool existsInFileSystem(const std::string &path)
existsInFileSystem Check if path exists
int waitForProcessTermination(pid_t pid)
waitForProcessTermination Waits for a process to terminate and then returns the status of the process...
bool destroy()
Calls shutdown, and then destroys the container.
Definition: container.cpp:530
Developers guide to adding a config item: