[lxc-devel] [PATCH v2.1] Add mechanism for container to notify host about end of boot

Christian Seiler christian at iwakd.de
Wed Sep 19 15:31:32 UTC 2012


This patch adds a simple notification system that allows the container to
notify the host (in particular, the lxc-start process) that the boot process
has been completed successfully. It also adds an additional status BOOTING
that lxc-info may return. This allows the administrator and scripts to
distinguish between a fully-running container and a container that is still
in the process of booting.

If nothing is added to the configuration file, the current behavior is not
changed, i.e. after lxc-start finishes the initialization, the container is
immediately put into the RUNNING state. This ensures backwards
compatibility.

If lxc.notification.enabled is set to yes, after lxc-start initialization
the container is initially put into the state BOOTING. Also, a UNIX domain
socket /var/lib/lxc/%s/notification-socket is created and bind-mounted into
the container, by default to /dev/lxc-notify. (This can be changed by the
lxc.notification.path setting.) The default access mode of this socket is
0600, so only root may access it.

A program in the container may connect to the socket and use a very simple
protocol to notify lxc-start that the boot sequence of the container has
been completed. Sending 'status RUNNING' or 'status STOPPING' will cause the
container to change the status to either RUNNING or STOPPING, others are not
allowed from within the container. If everything succeeds, lxc-start will
respond with 'OK', otherwise with 'ERR $error_message'.

The following script may be used from the shell to notify lxc-start from
within the container that boot has completed successfully:

echo status RUNNING | perl -e \
   'use IO::Socket;
    $client = IO::Socket::UNIX->new(
       Peer => "/dev/lxc-notify",
       Type => SOCK_STREAM,
       Timeout => 10
    );
    while (<>) {
      print $client $_;
    }'

If OpenBSD's version of netcat is available, executing

echo status RUNNING | nc -U /dev/lxc-notify

is also possible. In addition to RUNNING, STOPPING is also allowed from
within the container, so if somebody types 'shutdown -h now' in the
container, the status gets updated immediately.

The mechanism is designed to be extensible, i.e. commands other than
'status' may be supported at a later point, if other needs arise for the
communication between inside and outside the container.

Signed-off-by: Christian Seiler <christian at iwakd.de>
Cc: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 doc/lxc.conf.sgml.in   |   67 ++++++++
 src/lxc/Makefile.am    |    1 +
 src/lxc/conf.c         |    8 +
 src/lxc/conf.h         |    2 +
 src/lxc/confile.c      |   35 ++++
 src/lxc/notification.c |  435 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/notification.h |   40 +++++
 src/lxc/start.c        |   22 ++-
 src/lxc/start.h        |    1 +
 src/lxc/state.c        |    1 +
 src/lxc/state.h        |    3 +-
 11 files changed, 611 insertions(+), 4 deletions(-)
 create mode 100644 src/lxc/notification.c
 create mode 100644 src/lxc/notification.h

diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in
index 1428f25..a4950e8 100644
--- a/doc/lxc.conf.sgml.in
+++ b/doc/lxc.conf.sgml.in
@@ -738,6 +738,73 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
       </variablelist>
     </refsect2>
 
+    <refsect2>
+      <title>Notifications</title>
+      <para>
+	LXC supports a notification system that allows the container
+	to notify LXC when it has finished booting. If enabled, the
+	container has the status of BOOTING when control is given to
+	the init process and the container must notify lxc once the
+	boot sequence has completed. Since this requires intervention
+	from the inside of the container, it is disabled by default.
+      </para>
+      <para>
+	This system allows the administrator to distinguish from the
+	outside a container is that fully started (RUNNING) from a
+	container that is still in the process of initialization
+	(BOOTING).
+      </para>
+      <para>
+	The system is kept simple: LXC bind-mounts a UNIX socket into
+	the container (by default to
+	<filename>/dev/lxc-notify</filename>) and the container can
+	inform LXC via a very simple protocol. The following piece of
+	code may be used for the notification:
+      </para>
+      <programlisting>
+	echo status RUNNING | perl -e 'use IO::Socket;
+	    $client = IO::Socket::UNIX->new(
+	       Peer => "/dev/lxc-notify",
+	       Type => SOCK_STREAM,
+	       Timeout => 10
+	    );
+	    while (<>) {
+	      print $client $_;
+	    }'
+      </programlisting>
+      <para>
+	The following options control notifications:
+      </para>
+      <variablelist>
+	<varlistentry>
+	  <term>
+	    <option>lxc.notification.enabled</option>
+	  </term>
+	  <listitem>
+	    <para>
+	      Whether the notification system is enabled. The values
+	      <option>true</option>, <option>yes</option>,
+	      <option>on</option> and <option>1</option> indicate that
+	      it should be enabled, any other value that it should be
+	      disabled.
+	    </para>
+	  </listitem>
+	</varlistentry>
+	<varlistentry>
+	  <term>
+	    <option>lxc.notification.path</option>
+	  </term>
+	  <listitem>
+	    <para>
+	      The path inside the container where the lxc notification
+	      socket should be bind-mounted to. It defaults to
+	      <filename>/dev/lxc-notify</filename>.
+	    </para>
+	  </listitem>
+	</varlistentry>
+      </variablelist>
+    </refsect2>
+
   </refsect1>
 
   <refsect1>
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 7d86ad6..d976bf7 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -32,6 +32,7 @@ liblxc_so_SOURCES = \
 	freezer.c \
 	checkpoint.c \
 	restart.c \
+	notification.h notification.c \
 	error.h error.c \
 	parse.c parse.h \
 	cgroup.c cgroup.h \
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index f3c2334..7c9e936 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -61,6 +61,7 @@
 #include "log.h"
 #include "lxc.h"	/* for lxc_cgroup_set() */
 #include "caps.h"       /* for lxc_caps_last_cap() */
+#include "notification.h"
 
 #if HAVE_APPARMOR
 #include <apparmor.h>
@@ -2278,6 +2279,11 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
 		return -1;
 	}
 
+	if (lxc_notification_mount_hook(name, lxc_conf)) {
+		ERROR("failed to init notification mechanism for container '%s'.", name);
+		return -1;
+	}
+
 	if (setup_cgroup(name, &lxc_conf->cgroup)) {
 		ERROR("failed to setup the cgroups for '%s'", name);
 		return -1;
@@ -2583,6 +2589,8 @@ void lxc_conf_free(struct lxc_conf *conf)
 	if (conf->aa_profile)
 		free(conf->aa_profile);
 #endif
+	if (conf->notification_path)
+		free(conf->notification_path);
 	lxc_clear_config_caps(conf);
 	lxc_clear_cgroups(conf, "lxc.cgroup");
 	lxc_clear_hooks(conf, "lxc.hook");
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index dccc176..af9a178 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -237,6 +237,8 @@ struct lxc_conf {
 #endif
 	char *seccomp;  // filename with the seccomp rules
 	int maincmd_fd;
+	int notification_enabled;
+	char *notification_path;
 };
 
 int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 94bb698..fa15fd5 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -53,6 +53,8 @@ static int config_ttydir(const char *, char *, struct lxc_conf *);
 #if HAVE_APPARMOR
 static int config_aa_profile(const char *, char *, struct lxc_conf *);
 #endif
+static int config_notification_enabled(const char *, char *, struct lxc_conf *);
+static int config_notification_path(const char *, char *, struct lxc_conf *);
 static int config_cgroup(const char *, char *, struct lxc_conf *);
 static int config_mount(const char *, char *, struct lxc_conf *);
 static int config_rootfs(const char *, char *, struct lxc_conf *);
@@ -89,6 +91,8 @@ static struct lxc_config_t config[] = {
 #if HAVE_APPARMOR
 	{ "lxc.aa_profile",            config_aa_profile          },
 #endif
+	{ "lxc.notification.enabled", config_notification_enabled },
+	{ "lxc.notification.path",    config_notification_path    },
 	{ "lxc.cgroup",               config_cgroup               },
 	{ "lxc.mount",                config_mount                },
 	{ "lxc.rootfs.mount",         config_rootfs_mount         },
@@ -880,6 +884,34 @@ static int config_aa_profile(const char *key, char *value, struct lxc_conf *lxc_
 }
 #endif
 
+static int config_notification_enabled(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+	lxc_conf->notification_enabled = (
+		strcmp(value, "on")   == 0 ||
+		strcmp(value, "true") == 0 ||
+		strcmp(value, "yes")  == 0 ||
+		strcmp(value, "1")    == 0
+	);
+	return 0;
+}
+
+static int config_notification_path(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+	char *path;
+
+	if (!value || strlen(value) == 0)
+		return 0;
+	path = strdup(value);
+	if (!path) {
+		SYSERROR("failed to strdup '%s': %m", value);
+		return -1;
+	}
+
+	lxc_conf->notification_path = path;
+
+	return 0;
+}
+
 static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf)
 {
 	char *token = "lxc.cgroup.";
@@ -1597,6 +1629,9 @@ void write_config(FILE *fout, struct lxc_conf *c)
 	case PER_LINUX: fprintf(fout, "lxc.arch = x86_64\n"); break;
 	default: break;
 	}
+	fprintf(fout, "lxc.notification.enabled = %s\n", c->notification_enabled ? "yes" : "no");
+	if (c->notification_path)
+		fprintf(fout, "lxc.notification.path = %s\n", c->notification_path);
 #if HAVE_APPARMOR
 	if (c->aa_profile)
 		fprintf(fout, "lxc.aa_profile = %s\n", c->aa_profile);
diff --git a/src/lxc/notification.c b/src/lxc/notification.c
new file mode 100644
index 0000000..f236798
--- /dev/null
+++ b/src/lxc/notification.c
@@ -0,0 +1,435 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright LXC Project 2012
+ *
+ * Authors:
+ * Christian Seiler <christian at iwakd.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <libgen.h>
+
+#include "notification.h"
+#include "start.h"
+#include "state.h"
+#include "conf.h"
+#include "mainloop.h"
+#include "log.h"
+#include "utils.h"
+
+#define SOCKET_EXTERNAL_PATH          "/var/lib/lxc/%s/notification-socket"
+#define SOCKET_INTERNAL_PATH_DFLT     "/dev/lxc-notify"
+#define SOCKET_MAX_CLIENTS            10
+
+lxc_log_define(lxc_notification, lxc);
+
+static int notification_callback(int fd, void *data, struct lxc_epoll_descr *descr);
+static int notification_callback_client(int fd, void *data, struct lxc_epoll_descr *descr);
+
+struct notification_data {
+	int listen_fd;
+	int clients;
+};
+
+static int notification_statuschange(struct lxc_handler *handler, const char *command, const char *arg, int fd);
+
+static struct {
+	const char *name;
+	int (*handler)(struct lxc_handler *handler, const char *command, const char *arg, int fd);
+} notification_command_vtable[] = {
+	{ "status",       notification_statuschange        },
+};
+
+static int dispatch_notification_command(struct lxc_handler *handler, const char *command, const char *arg, int fd);
+
+int lxc_notification_init(struct lxc_handler *handler)
+{
+	int ret;
+	struct sockaddr_un addr;
+	char external_name[sizeof(addr.sun_path)];
+
+	if (!handler->conf->notification_enabled)
+		return 0;
+
+	struct notification_data *d = malloc(sizeof(struct notification_data));
+
+	d->listen_fd = -1;
+	d->clients = 0;
+
+	ret = snprintf(external_name, sizeof(addr.sun_path), SOCKET_EXTERNAL_PATH, handler->name);
+	if (ret < 0 || ret >= sizeof(addr.sun_path)) {
+		ERROR("error determining name of external notification socket: name too long");
+		goto cleanup_free;
+	}
+
+	/* remove old object that was there before */
+	unlink(external_name);
+
+	d->listen_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+	if (d->listen_fd < 0) {
+		SYSERROR("error creating notification socket %s", external_name);
+		goto cleanup_free;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	/* external_name will always fit in the structure because we chose it to
+	 * be small enough
+	 */
+	memcpy(addr.sun_path, external_name, strlen(external_name));
+
+	ret = bind(d->listen_fd, (struct sockaddr *)&addr, sizeof(addr));
+	if (ret < 0) {
+		SYSERROR("error creating notification socket %s (bind failed)", external_name);
+		goto cleanup_close;
+	}
+
+	/* change file permissions, BEFORE listening! */
+	ret = chmod(external_name, 0600);
+	if (ret < 0) {
+		SYSERROR("error creating notification socket %s (chmod failed)", external_name);
+		goto cleanup_unlink;
+	}
+
+	ret = listen(d->listen_fd, 10);
+	if (ret < 0) {
+		SYSERROR("error listening on notification socket %s", external_name);
+		goto cleanup_unlink;
+	}
+
+	handler->notification_data = d;
+	return 0;
+
+cleanup_unlink:
+	unlink(external_name);
+cleanup_close:
+	close(d->listen_fd);
+cleanup_free:
+	free(d);
+	return -1;
+}
+
+int lxc_notification_mount_hook(const char *name, struct lxc_conf *conf)
+{
+	int ret;
+	char inside[MAXPATHLEN];
+	char outside[MAXPATHLEN];
+	const char *inside_path = SOCKET_INTERNAL_PATH_DFLT;
+	char *d;
+
+	if (!conf->notification_enabled)
+		return 0;
+
+	if (conf->notification_path)
+		inside_path = conf->notification_path;
+
+	if (inside_path[0] == '/')
+		ret = snprintf(inside, MAXPATHLEN, "%s%s", conf->rootfs.mount, inside_path);
+	else
+		ret = snprintf(inside, MAXPATHLEN, "%s/%s", conf->rootfs.mount, inside_path);
+
+	if (ret < 0 || ret >= MAXPATHLEN) {
+		ERROR("error constructing in-container notification socket path: name too long");
+		return -1;
+	}
+
+	ret = snprintf(outside, MAXPATHLEN, SOCKET_EXTERNAL_PATH, name);
+	if (ret < 0 || ret >= MAXPATHLEN) {
+		ERROR("error constructing out-of-container notification socket path: name too long");
+		return -1;
+	}
+
+	d = strdup(inside);
+	if (!d) {
+		SYSERROR("out of memory");
+		return -1;
+	}
+
+	ret = mkdir_p(dirname(d), 0755);
+	free(d);
+	if (ret < 0) {
+		SYSERROR("could not create directories leading up to notification socket mountpoint inside container");
+		return -1;
+	}
+
+	unlink(inside);
+	ret = open(inside, O_CREAT | O_RDWR, 0600);
+	if (ret < 0) {
+		SYSERROR("could not create notification socket mountpoint inside container");
+		return -1;
+	}
+	/* we just wanted to create the file so we can
+	 * bind-mount the socket over it, we don't care at
+	 * all about anything else
+	 */
+	close(ret);
+
+	ret = mount(outside, inside, NULL, MS_BIND, NULL);
+	if (ret < 0) {
+		SYSERROR("could not bind-mount notification socket into the container");
+		return -1;
+	}
+
+	return 0;
+}
+
+int lxc_notification_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
+                                  struct lxc_handler *handler)
+{
+	struct notification_data *d = (struct notification_data *)handler->notification_data;
+
+	int ret;
+
+	if (!d) {
+		ERROR("no notification socket passed on");
+		return -1;
+	}
+
+	ret = lxc_mainloop_add_handler(descr, d->listen_fd, notification_callback,
+	                               handler);
+	if (ret) {
+		ERROR("failed to add handler for notification socket");
+		close(d->listen_fd);
+		d->listen_fd = -1;
+	}
+
+	return ret;
+}
+
+lxc_state_t lxc_notification_initial_running_state(struct lxc_handler *handler)
+{
+	return handler->conf->notification_enabled ? BOOTING : RUNNING;
+}
+
+int notification_callback(int fd, void *data, struct lxc_epoll_descr *descr)
+{
+	struct lxc_handler *handler = data;
+	struct notification_data *d = (struct notification_data *)handler->notification_data;
+
+	int ret;
+	int flags;
+	int conn_fd;
+
+	conn_fd = accept(fd, NULL, 0);
+	if (conn_fd < 0) {
+		SYSERROR("failed to accept connection from notification socket");
+		return -1;
+	}
+
+	if (d->clients > SOCKET_MAX_CLIENTS) {
+		close(conn_fd);
+		SYSERROR("too many clients already connected to notification socket, closing connection (max %d allowed)", SOCKET_MAX_CLIENTS);
+		return -1;
+	}
+
+	ret = fcntl(conn_fd, F_SETFD, FD_CLOEXEC);
+	if (ret < 0) {
+		SYSERROR("failed to set notification connection socket flags");
+		close(conn_fd);
+		return -1;
+	}
+
+	flags = fcntl(conn_fd, F_GETFL);
+	if (flags < 0) {
+		SYSERROR("failed to set notification connection socket flags");
+		close(conn_fd);
+		return -1;
+	}
+
+	ret = fcntl(conn_fd, F_SETFL, flags | O_NONBLOCK);
+	if (ret < 0) {
+		SYSERROR("failed to set notification connection socket flags");
+		close(conn_fd);
+		return -1;
+	}
+
+	ret = lxc_mainloop_add_handler(descr, conn_fd, notification_callback_client, data);
+	if (ret) {
+		ERROR("failed to add mainloop handler");
+		close(conn_fd);
+		return -1;
+	}
+
+	d->clients++;
+
+	return 0;
+}
+
+int notification_callback_client(int fd, void *data, struct lxc_epoll_descr *descr)
+{
+	struct lxc_handler *handler = data;
+	struct notification_data *d = (struct notification_data *)handler->notification_data;
+
+	int ret;
+	char buf[4096];
+	char *ptr;
+	char *saved_ptr;
+
+	ret = read(fd, buf, sizeof(buf) - 1);
+	if (ret < 0) {
+		/* ignore if this was a bogus poll event, may happen on busy systems */
+		if (errno == EAGAIN || errno == EINTR)
+			return 0;
+
+		/* something's wrong with this socket, let's close the connection */
+		SYSERROR("failed to read data from notification socket");
+		lxc_mainloop_del_handler(descr, fd);
+		close(fd);
+		--d->clients;
+		return -1;
+	} else if (ret == 0) {
+		/* this client ended its connection, we're done */
+		lxc_mainloop_del_handler(descr, fd);
+		close(fd);
+		--d->clients;
+		return 0;
+	}
+
+	/* make sure string is zero-terminated */
+	buf[ret] = '\0';
+
+	saved_ptr = NULL;
+	for (ptr = strtok_r(buf, "\n", &saved_ptr); ptr; ptr = strtok_r(NULL, "\n", &saved_ptr)) {
+		int length;
+		char *space;
+		char *command;
+		char *arg;
+
+		/* trim string, see if line is empty, if so, ignore it */
+		while (*ptr == ' ' || *ptr == '\t')
+			ptr++;
+		length = strlen(ptr);
+		while (length > 0 && (ptr[length - 1] == ' ' || ptr[length - 1] == '\t'))
+			length--;
+		if (length == 0)
+			continue;
+		ptr[length] = '\0';
+
+		space = strstr(ptr, " ");
+		if (space) {
+			*space = '\0';
+			command = ptr;
+			arg = space + 1;
+			/* left-trim argument */
+			while (*arg == ' ' || *arg == '\t')
+				arg++;
+		} else {
+			command = ptr;
+			arg = NULL;
+		}
+
+		ret = dispatch_notification_command(handler, command, arg, fd);
+		/* we should close the connection and remove the client */
+		if (ret > 0) {
+			lxc_mainloop_del_handler(descr, fd);
+			close(fd);
+			--d->clients;
+		}
+	}
+
+	return 0;
+}
+
+int lxc_notification_fini(struct lxc_handler *handler)
+{
+	struct notification_data *d = (struct notification_data *)handler->notification_data;
+
+	char external_name[MAXPATHLEN];
+	int ret;
+
+	if (d) {
+		if (d->listen_fd >= 0)
+			close(d->listen_fd);
+		free(d);
+		handler->notification_data = NULL;
+	}
+
+	ret = snprintf(external_name, MAXPATHLEN, SOCKET_EXTERNAL_PATH, handler->name);
+	if (ret < 0 || ret >= MAXPATHLEN) {
+		WARN("error determining name of external notification socket on cleanup: name too long (not removing)");
+		/* don't fail, since this part is purely optional, the socket
+		 * will also be removed on next start
+		 *
+		 * also, this really shouldn't happen, since we already checked
+		 * lengths when creating the socket
+		 */
+		return 0;
+	}
+
+	unlink(external_name);
+
+	return 0;
+}
+
+static int dispatch_notification_command(struct lxc_handler *handler, const char *command, const char *arg, int fd)
+{
+	int count = sizeof(notification_command_vtable) / sizeof(notification_command_vtable[0]);
+	int i;
+	const char *errmsg = "ERR Invalid / unsupported command\n";
+
+	for (i = 0; i < count; i++) {
+		if (strcmp(command, notification_command_vtable[i].name) == 0)
+			return notification_command_vtable[i].handler(handler, command, arg, fd);
+	}
+
+	write(fd, errmsg, strlen(errmsg));
+	ERROR("Unsupported notification command '%s' sent", command);
+	return 1; /* positive return value closes connection */
+}
+
+static int notification_statuschange(struct lxc_handler *handler, const char *command, const char *arg, int fd)
+{
+	lxc_state_t new_state;
+	const char *errmsg = "ERR Invalid / unsupported / disallowed state\n";
+	const char *errmsg2 = "ERR Setting state failed\n";
+	const char *okmsg = "OK\n";
+	int ret;
+
+	if (!arg) {
+		write(fd, errmsg, strlen(errmsg));
+		ERROR("Container requested state change but did not specify state");
+		return 1; /* positive return value closes connection */
+	}
+
+	new_state = lxc_str2state(arg);
+	if (new_state == RUNNING || new_state == STOPPING) {
+		ret = lxc_set_state(handler->name, handler, new_state);
+		if (ret < 0) {
+			write(fd, errmsg2, strlen(errmsg2));
+			ERROR("Setting container state to state %s failed", arg);
+		} else {
+			write(fd, okmsg, strlen(okmsg));
+		}
+		return ret;
+	}
+
+	write(fd, errmsg, strlen(errmsg));
+	ERROR("Container requested state change to unsupported or disallowed state '%s'", arg);
+	return 1; /* positive return value closes connection */
+}
diff --git a/src/lxc/notification.h b/src/lxc/notification.h
new file mode 100644
index 0000000..f258bf6
--- /dev/null
+++ b/src/lxc/notification.h
@@ -0,0 +1,40 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright LXC Project 2012
+ *
+ * Authors:
+ * Christian Seiler <christian at iwakd.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _notification_h
+#define _notification_h
+
+#include "state.h"
+
+struct lxc_handler;
+struct lxc_conf;
+struct lxc_epoll_descr;
+
+int lxc_notification_init(struct lxc_handler *handler);
+int lxc_notification_mount_hook(const char *name, struct lxc_conf *conf);
+int lxc_notification_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
+                                  struct lxc_handler *handler);
+int lxc_notification_fini(struct lxc_handler *handler);
+lxc_state_t lxc_notification_initial_running_state(struct lxc_handler *handler);
+
+#endif
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 3e26b27..f1aa524 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -128,6 +128,7 @@ int signalfd(int fd, const sigset_t *mask, int flags)
 #include "namespace.h"
 #include "apparmor.h"
 #include "lxcseccomp.h"
+#include "notification.h"
 
 lxc_log_define(lxc_start, lxc);
 
@@ -335,6 +336,11 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
 		goto out_mainloop_open;
 	}
 
+	if (lxc_notification_mainloop_add(name, &descr, handler)) {
+		ERROR("failed to add notification handler to mainloop");
+		goto out_mainloop_open;
+	}
+
 	if (handler->conf->need_utmp_watch) {
 		if (lxc_utmp_mainloop_add(&descr, handler)) {
 			ERROR("failed to add utmp handler to mainloop");
@@ -403,18 +409,25 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
 		goto out_delete_tty;
 	}
 
+	if (lxc_notification_init(handler)) {
+		ERROR("failed to initialize notification support");
+		goto out_delete_console;
+	}
+
 	/* the signal fd has to be created before forking otherwise
 	 * if the child process exits before we setup the signal fd,
 	 * the event will be lost and the command will be stuck */
 	handler->sigfd = setup_signal_fd(&handler->oldmask);
 	if (handler->sigfd < 0) {
 		ERROR("failed to set sigchild fd handler");
-		goto out_delete_console;
+		goto out_delete_notification;
 	}
 
 	INFO("'%s' is initialized", name);
 	return handler;
 
+out_delete_notification:
+	lxc_notification_fini(handler);
 out_delete_console:
 	lxc_delete_console(&conf->console);
 out_delete_tty:
@@ -446,6 +459,7 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
 
 	lxc_delete_console(&handler->conf->console);
 	lxc_delete_tty(&handler->conf->tty_info);
+	lxc_notification_fini(handler);
 	free(handler->name);
 	free(handler);
 }
@@ -584,6 +598,7 @@ int lxc_spawn(struct lxc_handler *handler)
 	int failed_before_rename = 0;
 	const char *name = handler->name;
 	int pinfd;
+	lxc_state_t initial_running_state;
 
 	if (lxc_sync_init(handler))
 		return -1;
@@ -659,9 +674,10 @@ int lxc_spawn(struct lxc_handler *handler)
 	if (handler->ops->post_start(handler, handler->data))
 		goto out_abort;
 
-	if (lxc_set_state(name, handler, RUNNING)) {
+	initial_running_state = lxc_notification_initial_running_state(handler);
+	if (lxc_set_state(name, handler, initial_running_state)) {
 		ERROR("failed to set state to %s",
-			      lxc_state2str(RUNNING));
+			      lxc_state2str(initial_running_state));
 		goto out_abort;
 	}
 
diff --git a/src/lxc/start.h b/src/lxc/start.h
index 4b2e2b5..518189c 100644
--- a/src/lxc/start.h
+++ b/src/lxc/start.h
@@ -49,6 +49,7 @@ struct lxc_handler {
 #if HAVE_APPARMOR
 	int aa_enabled;
 #endif
+	void *notification_data;
 };
 
 extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *);
diff --git a/src/lxc/state.c b/src/lxc/state.c
index 466dc0a..e37d80f 100644
--- a/src/lxc/state.c
+++ b/src/lxc/state.c
@@ -44,6 +44,7 @@ lxc_log_define(lxc_state, lxc);
 static char *strstate[] = {
 	"STOPPED", "STARTING", "RUNNING", "STOPPING",
 	"ABORTING", "FREEZING", "FROZEN", "THAWED",
+	"BOOTING",
 };
 
 const char *lxc_state2str(lxc_state_t state)
diff --git a/src/lxc/state.h b/src/lxc/state.h
index 77b3424..ce09ba6 100644
--- a/src/lxc/state.h
+++ b/src/lxc/state.h
@@ -25,7 +25,8 @@
 
 typedef enum {
 	STOPPED, STARTING, RUNNING, STOPPING,
-	ABORTING, FREEZING, FROZEN, THAWED, MAX_STATE,
+	ABORTING, FREEZING, FROZEN, THAWED,
+	BOOTING, MAX_STATE,
 } lxc_state_t;
 
 extern int lxc_rmstate(const char *name);
-- 
1.7.2.5





More information about the lxc-devel mailing list