20 #include "softwarecontainer.h" 21 #include "gateway/gateway.h" 22 #include "container.h" 24 #ifdef ENABLE_PULSEGATEWAY 25 #include "gateway/pulsegateway.h" 28 #ifdef ENABLE_NETWORKGATEWAY 29 #include "gateway/network/networkgateway.h" 32 #ifdef ENABLE_DBUSGATEWAY 33 #include "gateway/dbus/dbusgateway.h" 36 #ifdef ENABLE_DEVICENODEGATEWAY 37 #include "gateway/devicenode/devicenodegateway.h" 40 #ifdef ENABLE_CGROUPSGATEWAY 41 #include "gateway/cgroups/cgroupsgateway.h" 44 #ifdef ENABLE_WAYLANDGATEWAY 45 #include "gateway/waylandgateway.h" 48 #ifdef ENABLE_ENVGATEWAY 49 #include "gateway/environment/envgateway.h" 52 #ifdef ENABLE_FILEGATEWAY 53 #include "gateway/files/filegateway.h" 59 std::unique_ptr<const SoftwareContainerConfig> config):
61 m_config(std::move(config)),
62 m_previouslyConfigured(false)
64 m_containerRoot = buildPath(m_config->sharedMountsDir(),
"SC-" + std::to_string(
id));
65 m_tmpfsSize = 100485760;
66 checkContainerRoot(m_containerRoot);
67 if (m_config->writeBufferEnabled()) {
68 if (m_config->temporaryFileSystemWriteBufferEnableds()) {
69 m_tmpfsSize = m_config->temporaryFileSystemSize();
74 #ifdef ENABLE_NETWORKGATEWAY 75 checkNetworkSettings();
76 #endif // ENABLE_NETWORKGATEWAY 78 m_container = std::shared_ptr<ContainerAbstractInterface>(
80 m_config->containerConfigPath(),
82 m_config->writeBufferEnabled(),
83 m_config->containerShutdownTimeout()));
87 + std::to_string(
id));
90 m_containerState.setValueNotify(ContainerState::READY);
93 SoftwareContainer::~SoftwareContainer()
97 bool SoftwareContainer::start()
99 log_debug() <<
"Initializing container";
100 if (!m_container->initialize()) {
101 log_error() <<
"Could not initialize container";
105 log_debug() <<
"Creating container";
106 if (!m_container->create()) {
110 log_debug() <<
"Starting container";
111 if (!m_container->start(&m_pcPid)) {
112 log_error() <<
"Could not start container";
116 log_debug() <<
"Started container with PID " << m_pcPid;
120 bool SoftwareContainer::init()
123 log_error() <<
"Failed to start container";
127 #ifdef ENABLE_NETWORKGATEWAY 129 m_gateways.push_back( std::unique_ptr<Gateway>(
new NetworkGateway(
131 m_config->bridgeDevice(),
132 m_config->bridgeIPAddress(),
133 m_config->bridgeNetmaskBitLength(),
137 log_error() <<
"Given netmask is not appropriate for creating ip address." 138 <<
"It should be an unsigned value between 1 and 31";
143 #ifdef ENABLE_PULSEGATEWAY 144 m_gateways.push_back( std::unique_ptr<Gateway>(
new PulseGateway(m_container)) );
147 #ifdef ENABLE_DEVICENODEGATEWAY 148 m_gateways.push_back( std::unique_ptr<Gateway>(
new DeviceNodeGateway(m_container)) );
151 #ifdef ENABLE_DBUSGATEWAY 152 m_gateways.push_back( std::unique_ptr<Gateway>(
new DBusGateway( getGatewayDir(), m_container)) );
155 #ifdef ENABLE_CGROUPSGATEWAY 156 m_gateways.push_back( std::unique_ptr<Gateway>(
new CgroupsGateway(m_container)) );
159 #ifdef ENABLE_WAYLANDGATEWAY 160 m_gateways.push_back( std::unique_ptr<Gateway>(
new WaylandGateway(m_container)) );
163 #ifdef ENABLE_ENVGATEWAY 164 m_gateways.push_back( std::unique_ptr<Gateway>(
new EnvironmentGateway(m_container)) );
167 #ifdef ENABLE_FILEGATEWAY 168 m_gateways.push_back( std::unique_ptr<Gateway>(
new FileGateway(m_container)) );
178 if (m_containerState != ContainerState::READY) {
179 std::string message =
"Invalid to start gateways on non ready container " +
180 std::string(m_container->id());
181 log_error() << message;
185 if (!configureGateways(gwConfig)) {
186 log_error() <<
"Could not configure Gateways";
190 if (!activateGateways()) {
191 log_error() <<
"Could not activate Gateways";
196 m_previouslyConfigured =
true;
207 for (
auto &
id : gwConfig.ids()) {
209 for (
auto &gateway : m_gateways) {
210 if (
id == gateway->id()) {
215 throw GatewayError(
"Could not find any gateway matching id: " +
id);
220 for (
auto &gateway : m_gateways) {
221 std::string gatewayId = gateway->id();
223 json_t *config = gwConfig.config(gatewayId);
224 if (config !=
nullptr) {
225 log_debug() <<
"Configuring gateway: \"" << gatewayId <<
"\"";
226 log_debug() << json_dumps(config, JSON_INDENT(4));
228 if (!gateway->setConfig(config)) {
229 log_error() <<
"Failed to apply gateway configuration";
237 log_error() <<
"Fatal error when configuring gateway \"" 238 << gatewayId <<
"\": " << error.what();
248 bool SoftwareContainer::activateGateways()
250 for (
auto &gateway : m_gateways) {
251 std::string gatewayId = gateway->id();
254 if (gateway->isConfigured()) {
255 log_debug() <<
"Activating gateway: \"" << gatewayId <<
"\"";
257 if (!gateway->activate()) {
258 log_error() <<
"Failed to activate gateway \"" << gatewayId <<
"\"";
268 log_error() <<
"Fatal error when activating gateway \"" 269 << gatewayId <<
"\" : " << error.what();
279 return shutdown(m_config->containerShutdownTimeout());
286 log_debug() <<
"shutdown called";
288 if (m_containerState != ContainerState::READY
289 && m_containerState != ContainerState::SUSPENDED)
291 std::string message =
"Invalid to shut down container which is not ready or suspended " +
292 std::string(m_container->id());
293 log_error() << message;
297 if(!shutdownGateways()) {
298 log_error() <<
"Could not shut down all gateways cleanly, check the log";
301 if(!m_container->destroy(timeout)) {
302 std::string message =
"Could not destroy the container during shutdown " +
303 std::string(m_container->id());
304 log_error() << message;
305 m_containerState.setValueNotify(ContainerState::INVALID);
309 m_containerState.setValueNotify(ContainerState::TERMINATED);
316 std::string
id = std::string(m_container->id());
318 if (m_containerState != ContainerState::READY) {
319 std::string message =
"Invalid to suspend non ready container " + id;
320 log_error() << message;
324 if (!m_container->suspend()) {
325 std::string message =
"Failed to suspend container " + id;
326 log_error() << message;
327 m_containerState.setValueNotify(ContainerState::INVALID);
331 log_debug() <<
"Suspended container " << id;
332 m_containerState.setValueNotify(ContainerState::SUSPENDED);
338 std::string
id = std::string(m_container->id());
340 if (m_containerState != ContainerState::SUSPENDED) {
341 std::string message =
"Invalid to resume non suspended container " + id;
342 log_error() << message;
346 if (!m_container->resume()) {
347 std::string message =
"Failed to resume container " + id;
348 log_error() << message;
349 m_containerState.setValueNotify(ContainerState::INVALID);
353 log_debug() <<
"Resumed container " << id;
354 m_containerState.setValueNotify(ContainerState::READY);
358 bool SoftwareContainer::shutdownGateways()
361 for (
auto &gateway : m_gateways) {
362 if (gateway->isActivated()) {
363 log_debug() <<
"Tearing down gateway: \"" << gateway->id() <<
"\"";
364 if (!gateway->teardown()) {
365 log_warning() <<
"Could not tear down gateway cleanly: " << gateway->id();
375 std::string SoftwareContainer::getContainerDir()
377 const std::string containerID = std::string(m_container->id());
378 return buildPath(m_config->sharedMountsDir(), containerID);
381 std::string SoftwareContainer::getGatewayDir()
383 return buildPath(getContainerDir(),
"gateways");
388 return m_containerState;
395 if (m_containerState != ContainerState::READY) {
396 std::string message =
"Invalid to execute code in non ready container " +
397 std::string(m_container->id());
398 log_error() << message;
402 return std::make_shared<FunctionJob>(m_container, fun);
409 if (m_containerState != ContainerState::READY) {
410 std::string message =
"Invalid to execute code in non ready container " +
411 std::string(m_container->id());
412 log_error() << message;
416 return std::make_shared<CommandJob>(m_container, command);
420 const std::string &pathInContainer,
425 std::string
id = std::string(m_container->id());
427 if (m_containerState != ContainerState::READY) {
428 std::string message =
"Invalid to bind mount in non ready container " + id;
429 log_error() << message;
433 return m_container->bindMountInContainer(pathOnHost, pathInContainer, readonly);
436 void SoftwareContainer::assertValidState()
438 if (m_containerState == ContainerState::INVALID) {
439 std::string message =
"Container is invalid " + std::string(m_container->id());
440 log_error() << message;
447 return m_previouslyConfigured;
450 void SoftwareContainer::checkContainerRoot(std::string rootDir)
453 log_debug() <<
"Container root " << rootDir <<
" does not exist, trying to create";
454 std::unique_ptr<CreateDir> createDirInstance = std::unique_ptr<CreateDir>(
new CreateDir());
455 if(!createDirInstance->createDirectory(rootDir)) {
456 std::string message =
"Failed to create container root directory";
457 log_error() << message;
464 log_warning() <<
"Container Root " << rootDir <<
" is not empty.";
468 #ifdef ENABLE_NETWORKGATEWAY 469 void SoftwareContainer::checkNetworkSettings()
471 std::vector<std::string> argv;
472 argv.push_back(buildPath(std::string(INSTALL_BINDIR),
"setup_softwarecontainer.sh"));
473 argv.push_back(m_config->bridgeDevice());
475 if (m_config->shouldCreateBridge()) {
476 argv.push_back(m_config->bridgeIPAddress());
477 argv.push_back(m_config->bridgeNetmask());
478 argv.push_back(std::to_string(m_config->bridgeNetmaskBitLength()));
479 argv.push_back(m_config->bridgeNetAddr());
482 log_debug() <<
"Checking the network setup...";
485 Glib::spawn_sync(
"", argv, Glib::SPAWN_DEFAULT,
486 sigc::slot<void>(),
nullptr,
487 nullptr, &returnCode);
488 }
catch (Glib::SpawnError e) {
489 std::string message =
"Failed to spawn setup_softwarecontainer.sh: " + e.what();
490 log_error() << message;
494 if (returnCode != 0) {
495 std::string message =
"Return code of setup_softwarecontainer.sh is non-zero";
496 log_error() << message;
501 #endif // ENABLE_NETWORKGATEWAY
std::shared_ptr< FunctionJob > createFunctionJob(const std::function< int()> fun)
Create a job that can run a function in a container.
bool isDirectoryEmpty(const std::string &path)
isDirectoryEmpty Check if path is empty
A method was called which is inappropriate in the current state.
SoftwareContainer(const ContainerID id, std::unique_ptr< const SoftwareContainerConfig > config)
Creates a new SoftwareContainer instance.
Sets up and manages network access and routing to the container.
This gateway lets you map files (including socket files) or folders from the host into the container'...
Environment Gateway is used to define environment variables to the container.
The CreateDir class is responsible for creating new directories and removing them when it is necessar...
The Container class is an abstraction of the specific containment technology used.
bool previouslyConfigured()
Indicates if gateways have been configured previously.
bool startGateways(const GatewayConfiguration &configs)
Starts the Gateways by setting the gateway configurations and activating the configured gateway...
std::shared_ptr< CommandJob > createCommandJob(const std::string &command)
Create a job that can run a command in a container.
The cgroups gateway sets cgroups related settings for the container.
The container instance is in an invalid state and should not be used.
bool isDirectory(const std::string &path)
isDirectory Check if path is a directory
void shutdown()
Shut down the container.
An error has occured in the underlying container implementation.
void resume()
Resume a suspended container.
ObservableProperty< ContainerState > & getContainerState()
Get the state of this container instance.
void suspend()
Suspend the container.
Developers guide to adding a config item:
The PulseAudio Gateway is used to provide access to the host system PulseAudio server.
bool bindMount(const std::string &pathOnHost, const std::string &pathInContainer, bool readonly=true)
Should only be called on containers in state 'READY'.
This gateway is responsible for exposing device nodes in an LXC container.