#define SECURITY_CONTAINERS_SERVER_CONTAINER_HPP
#include "scs-container-config.hpp"
+
#include <string>
#include <libvirt/libvirt.h>
class Container {
public:
- Container();
+ Container(const std::string& configXML);
virtual ~Container();
- void define(const char* configXML = NULL);
- void undefine();
+
+ /**
+ * Boot the container
+ */
void start();
+
+ /**
+ * Forcefully stop the container.
+ */
void stop();
+ /**
+ * Gracefully shutdown the domain.
+ * This method will NOT block untill domain is shut down,
+ * because some configurations may ignore this.
+ */
+ void shutdown();
+
+ /**
+ * @return Is the process started?
+ */
+ bool isRunning();
+
+ /**
+ * Check if the container is stopped. It's NOT equivalent to !isRunning,
+ * because it checks different internal libvirt's states. There are other states,
+ * (e.g. paused) when the container isn't runnig nor stopped.
+ *
+ * @return Is the process stopped?
+ */
+ bool isStopped();
+
+ /**
+ * Suspends an active domain, the process is frozen
+ * without further access to CPU resources and I/O,
+ * but the memory used by the domain
+ * at the hypervisor level will stay allocated
+ */
+ void suspend();
+
+ /**
+ * Resume the container after suspension.
+ */
+ void resume();
+
+ /**
+ * @return Is the container in a paused state?
+ */
+ bool isPaused();
+
private:
ContainerConfig mConfig; // container configuration
virConnectPtr mVir = NULL; // pointer to the connection with libvirt
virDomainPtr mDom = NULL; // pointer to the domain
- bool mIsRunning = false; // is the domain now running
-
// TODO: This is a temporary sollution.
const std::string mDefaultConfigXML = "<domain type=\"lxc\">\
<name>cnsl</name>\
<console type=\"pty\"/>\
</devices>\
</domain>";
- void connect();
- void disconnect();
+ void connect(); // fill mVir
+ void disconnect(); // release mVir
+ void define(const std::string& configXML); // containers can't share the same libvirt configuration
+ void undefine();
+
+ int getState(); // get the libvirt's domain state
+ bool isPMSuspended(); // the cotainer suspended by the power manager
};
}
#endif // SECURITY_CONTAINERS_SERVER_CONTAINER_HPP
* @brief Main file for the Security Containers Daemon
*/
-#include "scs-container.hpp" // TODO: Delete
-
#include <iostream>
#include <getopt.h> // For getopt
-#include <unistd.h> // For sleep
-
-using namespace security_containers;
-
int main(int argc, char* argv[])
{
#include "scs-container.hpp"
#include "scs-exception.hpp"
#include "scs-log.hpp"
+
#include <assert.h>
+#include <string>
-using namespace security_containers;
+namespace security_containers {
-Container::Container()
+Container::Container(const std::string& configXML)
{
connect();
+ define(configXML);
}
Container::~Container()
{
+ // Try to shutdown
+ try {
+ resume();
+ shutdown();
+ } catch (ServerException& e) {}
+
+ // Destroy the container
+ try {
+ stop();
+ undefine();
+ } catch (ServerException& e) {
+ LOGE("Failed to destroy the container!");
+ }
disconnect();
}
-void
-Container::connect()
+void Container::connect()
{
assert(mVir == NULL);
};
-void
-Container::disconnect()
+void Container::disconnect()
{
if (mVir == NULL) {
return;
};
-void
-Container::start()
+void Container::start()
{
assert(mVir != NULL);
assert(mDom != NULL);
- if (mIsRunning) {
+ if (isRunning()) {
return;
}
LOGE("Failed to start the container");
throw DomainOperationException();
}
-
- mIsRunning = true;
};
-void
-Container::stop()
+void Container::stop()
{
assert(mVir != NULL);
assert(mDom != NULL);
- if (!mIsRunning) {
+ if (!isRunning()) {
return;
}
+
// Forceful termination of the guest
u_int flags = VIR_DOMAIN_DESTROY_DEFAULT;
LOGE("Error during domain stopping");
throw DomainOperationException();
}
-
- mIsRunning = false;
};
-void
-Container::define(const char* configXML)
+void Container::shutdown()
+{
+ assert(mVir != NULL);
+ assert(mDom != NULL);
+
+ if (!isRunning()) {
+ return;
+ }
+
+ if (virDomainShutdown(mDom) < 0) {
+ LOGE("Error during domain shutdown");
+ throw DomainOperationException();
+ }
+}
+
+
+bool Container::isRunning()
+{
+ return getState() == VIR_DOMAIN_RUNNING;
+}
+
+
+bool Container::isStopped()
+{
+ int state = getState();
+ return state == VIR_DOMAIN_SHUTDOWN ||
+ state == VIR_DOMAIN_SHUTOFF ||
+ state == VIR_DOMAIN_CRASHED;
+}
+
+void Container::define(const std::string& configXML)
{
assert(mVir != NULL);
- if (configXML) {
- mDom = virDomainDefineXML(mVir, configXML);
+ if (!configXML.empty()) {
+ mDom = virDomainDefineXML(mVir, configXML.c_str());
} else {
mDom = virDomainDefineXML(mVir, mDefaultConfigXML.c_str());
}
};
-void
-Container::undefine()
+void Container::undefine()
{
assert(mVir != NULL);
assert(mDom != NULL);
mDom = NULL;
};
+
+
+void Container::suspend()
+{
+ assert(mVir != NULL);
+ assert(mDom != NULL);
+
+ if (isPaused()) {
+ return;
+ }
+
+ if (isPMSuspended() || virDomainSuspend(mDom) < 0) {
+ LOGE("Error during domain suspension");
+ throw DomainOperationException();
+ }
+}
+
+
+void Container::resume()
+{
+ assert(mVir != NULL);
+ assert(mDom != NULL);
+
+ if (!isPaused()) {
+ return;
+ }
+
+ if (isPMSuspended() || virDomainResume(mDom) < 0) {
+ LOGE("Error during domain resumming");
+ throw DomainOperationException();
+ }
+}
+
+
+bool Container::isPaused()
+{
+ return getState() == VIR_DOMAIN_PAUSED;
+}
+
+
+bool Container::isPMSuspended()
+{
+ return getState() == VIR_DOMAIN_PMSUSPENDED;
+}
+
+
+int Container::getState()
+{
+ assert(mVir != NULL);
+ assert(mDom != NULL);
+
+ int state;
+
+ if (virDomainGetState(mDom, &state, NULL, 0)) {
+ LOGE("Error during getting domain's state");
+ throw DomainOperationException();
+ }
+
+ return state;
+}
+
+} // namespace security_containers
\ No newline at end of file
*/
#include "scs-container.hpp"
+#include "scs-exception.hpp"
+
+#include <memory>
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
-using namespace security_containers;
BOOST_AUTO_TEST_SUITE(ContainerSuite)
-BOOST_AUTO_TEST_CASE(SimpleTest)
+using namespace security_containers;
+
+const std::string testConfigXML =
+ "<domain type=\"lxc\"> \
+ <name>cnsl</name> \
+ <memory>102400</memory> \
+ <on_poweroff>destroy</on_poweroff> \
+ <os> \
+ <type>exe</type> \
+ <init>/bin/sh</init> \
+ </os> \
+ <devices> \
+ <console type=\"pty\"/> \
+ </devices> \
+ </domain>";
+
+
+BOOST_AUTO_TEST_CASE(ConstructorTest)
+{
+ BOOST_CHECK_THROW(Container c1("<><TRASHXML>"), ServerException);
+ BOOST_CHECK_NO_THROW(Container c2(testConfigXML));
+}
+
+BOOST_AUTO_TEST_CASE(DestructorTest)
+{
+ std::unique_ptr<Container> c(new Container(testConfigXML));
+ BOOST_REQUIRE_NO_THROW(c.reset());
+}
+
+BOOST_AUTO_TEST_CASE(StartTest)
+{
+ Container c(testConfigXML);
+ BOOST_REQUIRE_NO_THROW(c.start());
+ BOOST_CHECK(c.isRunning());
+}
+
+BOOST_AUTO_TEST_CASE(StopTest)
+{
+ Container c(testConfigXML);
+ BOOST_REQUIRE_NO_THROW(c.start());
+ BOOST_CHECK(c.isRunning());
+ BOOST_REQUIRE_NO_THROW(c.stop())
+ BOOST_CHECK(!c.isRunning());
+ BOOST_CHECK(c.isStopped());
+}
+
+BOOST_AUTO_TEST_CASE(ShutdownTest)
+{
+ Container c(testConfigXML);
+ BOOST_REQUIRE_NO_THROW(c.start())
+ BOOST_CHECK(c.isRunning());
+ BOOST_REQUIRE_NO_THROW(c.shutdown())
+ // TODO: For this simple configuration, the shutdown signal is ignored
+ // BOOST_CHECK(!c.isRunning());
+ // BOOST_CHECK(c.isStopped());
+}
+
+BOOST_AUTO_TEST_CASE(SuspendTest)
+{
+ Container c(testConfigXML);
+ BOOST_REQUIRE_NO_THROW(c.start())
+ BOOST_CHECK(c.isRunning());
+ BOOST_REQUIRE_NO_THROW(c.suspend())
+ BOOST_CHECK(!c.isRunning());
+ BOOST_CHECK(c.isPaused());
+}
+
+BOOST_AUTO_TEST_CASE(ResumeTest)
{
- Container c;
- c.define();
- c.start();
- c.stop();
- c.undefine();
+ Container c(testConfigXML);
+ BOOST_REQUIRE_NO_THROW(c.start());
+ BOOST_REQUIRE_NO_THROW(c.suspend())
+ BOOST_CHECK(c.isPaused());
+ BOOST_REQUIRE_NO_THROW(c.resume());
+ BOOST_CHECK(!c.isPaused());
+ BOOST_CHECK(c.isRunning());
}
BOOST_AUTO_TEST_SUITE_END()