softwarecontainer  0.18.0-739e8d7 2017-05-04
softwarecontainer::Container Class Reference

The Container class is an abstraction of the specific containment technology used. More...

#include <container.h>

Inheritance diagram for softwarecontainer::Container:
[legend]
Collaboration diagram for softwarecontainer::Container:
[legend]

Public Types

typedef std::function< int()> ExecFunction
 

Public Member Functions

 Container (const std::string id, const std::string &configFile, const std::string &containerRoot, bool writeBufferEnabled=false, int shutdownTimeout=1)
 Constructor. More...
 
bool create ()
 create Creates a new lxc_container and creates it with all the initialization. More...
 
bool start (pid_t *pid)
 Start the container. More...
 
bool setCgroupItem (std::string subsys, std::string value)
 
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 previously set by the gateways, plus the ones passed as parameters here. More...
 
bool execute (ExecFunction function, pid_t *pid, const EnvironmentVariables &variables=EnvironmentVariables(), int stdin=-1, int stdout=1, int stderr=2)
 Executes a commandline. More...
 
bool executeSync (ExecFunction function, pid_t *pid, const EnvironmentVariables &variables=EnvironmentVariables(), int stdin=-1, int stdout=1, int stderr=2)
 synchronous version of execute More...
 
bool bindMountInContainer (const std::string &pathInHost, const std::string &pathInContainer, bool readOnly=true)
 Tries to bind mount a path from host to container. More...
 
bool mountDevice (const std::string &pathInHost)
 
bool destroy ()
 Calls shutdown, and then destroys the container. More...
 
bool destroy (unsigned int timeout)
 
bool shutdown ()
 Calls shutdown on the lxc container. More...
 
bool shutdown (unsigned int timeout)
 
bool suspend ()
 
bool resume ()
 
bool stop ()
 Calls stop on the lxc container(force stop) More...
 
bool waitForState (LXCContainerState state, int timeout=20)
 
bool ensureContainerRunning ()
 
bool initialize ()
 Setup the container for startup. More...
 
std::string toString ()
 
const char * id () const
 
std::string gatewaysDirInContainer () const
 
std::string gatewaysDir () const
 
bool setEnvironmentVariable (const std::string &var, const std::string &val)
 

Private Member Functions

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. More...
 
bool tmpfsMount (const std::string dst, const int maxSize)
 tmpfsMount Mount a tmpfs in the dst path and limit size of the tmpfs to maxSize More...
 
bool writeToFile (const std::string &path, const std::string &content)
 
void markFileForDeletion (const std::string &path)
 
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. More...
 
bool syncOverlayMount (const std::string &lower, const std::string &upper)
 syncOverlayMount Copy the directory structure from upper layer to the lower layer More...
 
bool createSharedMountPoint (const std::string &path)
 createSharedMountPoint Make the mount point shared, ie new mount points created in one bind mount will also be created in the other mount point. More...
 
bool pathInList (const std::string path)
 checks whether given path is already added to clean up handlers or not More...
 

Private Attributes

std::vector< std::unique_ptr< CleanUpHandler > > m_cleanupHandlers
 m_cleanupHandlers A vector of cleanupHandlers added during the lifetime of the FileToolKitWithUndo that will be run from the destructor. More...
 
std::vector< std::unique_ptr< CreateDir > > m_createDirList
 m_createDirList A vector of CreateDir classes. More...
 

Detailed Description

The Container class is an abstraction of the specific containment technology used.

The Container class is meant to be an abstraction of the containment technology, so that SoftwareContainer can view it as a generic concept that implements the specifics behind the conceptual phases of 'Pereload', 'Launch', and 'Shutdown'.

Definition at line 42 of file container.h.

Constructor & Destructor Documentation

softwarecontainer::Container::Container ( const std::string  id,
const std::string &  configFile,
const std::string &  containerRoot,
bool  writeBufferEnabled = false,
int  shutdownTimeout = 1 
)

Constructor.

Parameters
nameName of the container
configFilePath to the configuration file (including the file name)
containerRootA path to the root of the container, i.e. the base path to e.g. the configurations and application root
writeBufferEnabledEnable RAM write buffers on top of rootfs
shutdownTimeoutTimeout for shutdown of container.

Definition at line 75 of file container.cpp.

References destroy(), and shutdown().

79  :
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 }

Here is the call graph for this function:

Member Function Documentation

bool softwarecontainer::Container::create ( )
virtual

create Creates a new lxc_container and creates it with all the initialization.

Returns
false on failed creation and true on a successful creation.

Implements softwarecontainer::ContainerAbstractInterface.

Definition at line 145 of file container.cpp.

References softwarecontainer::FileToolkitWithUndo::overlayMount().

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 }
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.

Here is the call graph for this function:

bool softwarecontainer::Container::start ( pid_t *  pid)
virtual

Start the container.

Returns
The pid of the init process of the container

Implements softwarecontainer::ContainerAbstractInterface.

Definition at line 265 of file container.cpp.

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 }
bool softwarecontainer::Container::execute ( const std::string &  commandLine,
pid_t *  pid,
const EnvironmentVariables &  variables,
const std::string &  workingDirectory = "/",
int  stdin = -1,
int  stdout = 1,
int  stderr = 2 
)
virtual

Start a process from the given command line, with an environment consisting of the variables previously set by the gateways, plus the ones passed as parameters here.

Implements softwarecontainer::Executable.

Definition at line 429 of file container.cpp.

Referenced by executeSync().

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 }
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

Here is the caller graph for this function:

bool softwarecontainer::Container::execute ( ExecFunction  function,
pid_t *  pid,
const EnvironmentVariables &  variables = EnvironmentVariables(),
int  stdin = -1,
int  stdout = 1,
int  stderr = 2 
)
virtual

Executes a commandline.

Implements softwarecontainer::Executable.

Definition at line 349 of file container.cpp.

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 }
bool softwarecontainer::Container::executeSync ( ExecFunction  function,
pid_t *  pid,
const EnvironmentVariables &  variables = EnvironmentVariables(),
int  stdin = -1,
int  stdout = 1,
int  stderr = 2 
)

synchronous version of execute

Definition at line 335 of file container.cpp.

References execute(), and softwarecontainer::waitForProcessTermination().

Referenced by bindMountInContainer().

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 }
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
int waitForProcessTermination(pid_t pid)
waitForProcessTermination Waits for a process to terminate and then returns the status of the process...

Here is the call graph for this function:

Here is the caller graph for this function:

bool softwarecontainer::Container::bindMountInContainer ( const std::string &  pathInHost,
const std::string &  pathInContainer,
bool  readOnly = true 
)
virtual

Tries to bind mount a path from host to container.

Any missing parent paths will be created.

Parameters
pathInHostThe path on the host that shall be bind mounted into the container
pathInContainerWhere to mount the path in the container.
readonlySets if the mount should be read only or read write
Returns
SUCCESS if everything worked as expected, FAILURE otherwise

Implements softwarecontainer::ContainerAbstractInterface.

Definition at line 571 of file container.cpp.

References softwarecontainer::FileToolkitWithUndo::bindMount(), executeSync(), softwarecontainer::existsInFileSystem(), softwarecontainer::isDirectory(), softwarecontainer::FileToolkitWithUndo::m_cleanupHandlers, softwarecontainer::FileToolkitWithUndo::m_createDirList, and softwarecontainer::FileToolkitWithUndo::pathInList().

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 }
std::vector< std::unique_ptr< CleanUpHandler > > m_cleanupHandlers
m_cleanupHandlers A vector of cleanupHandlers added during the lifetime of the FileToolKitWithUndo th...
std::vector< std::unique_ptr< CreateDir > > m_createDirList
m_createDirList A vector of CreateDir classes.
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 isDirectory(const std::string &path)
isDirectory Check if path is a directory
bool existsInFileSystem(const std::string &path)
existsInFileSystem Check if path exists

Here is the call graph for this function:

bool softwarecontainer::Container::destroy ( )
virtual

Calls shutdown, and then destroys the container.

Implements softwarecontainer::ContainerAbstractInterface.

Definition at line 530 of file container.cpp.

References shutdown().

Referenced by Container().

531 {
532  return destroy(m_shutdownTimeout);
533 }
bool destroy()
Calls shutdown, and then destroys the container.
Definition: container.cpp:530

Here is the call graph for this function:

Here is the caller graph for this function:

bool softwarecontainer::Container::shutdown ( )
virtual

Calls shutdown on the lxc container.

Implements softwarecontainer::ContainerAbstractInterface.

Definition at line 497 of file container.cpp.

References stop().

Referenced by Container(), and destroy().

498 {
499  return shutdown(m_shutdownTimeout);
500 }
bool shutdown()
Calls shutdown on the lxc container.
Definition: container.cpp:497

Here is the call graph for this function:

Here is the caller graph for this function:

bool softwarecontainer::Container::stop ( )
virtual

Calls stop on the lxc container(force stop)

Implements softwarecontainer::ContainerAbstractInterface.

Definition at line 477 of file container.cpp.

Referenced by shutdown().

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 }

Here is the caller graph for this function:

bool softwarecontainer::Container::initialize ( )
virtual

Setup the container for startup.

Setup the container so directories are available for later use when setApplication is called.

Returns
true or false

Implements softwarecontainer::ContainerAbstractInterface.

Definition at line 109 of file container.cpp.

References softwarecontainer::FileToolkitWithUndo::createSharedMountPoint(), and softwarecontainer::FileToolkitWithUndo::m_createDirList.

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 }
std::vector< std::unique_ptr< CreateDir > > m_createDirList
m_createDirList A vector of CreateDir classes.
bool createSharedMountPoint(const std::string &path)
createSharedMountPoint Make the mount point shared, ie new mount points created in one bind mount wil...

Here is the call graph for this function:


The documentation for this class was generated from the following files: