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

Christian Seiler christian at iwakd.de
Thu Sep 13 15:22:30 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.type is set to 'fifo', after lxc-start initialization
the container is initially put into the state BOOTING. Also, the FIFO
/var/lib/lxc/%s/notification-fifo is created and bind-mounted into the
container, by default to /dev/lxc-notify, but this can be changed via the
lxc.notification.path configuration setting.

Inside the container one may execute 'echo RUNNING > /dev/lxc-notify' or an
equivalent command to notify lxc-start that the container has now booted.
Similarly, 'echo STOPPING > /dev/lxc-notify' will change the status to
STOPPING, which may be done on shutdown. Currently, only RUNNING and
STOPPING are allowed, other states are ignored.

This patch only provides the LXC part for the notification system, the
counterpart inside the container has to be provided separately. The
interface has been kept extremely simple to facilitate this.

The choice of the option lxc.notification.type, as opposed to
lxc.notification.enabled, is deliberate in order to make this extensible. If
at some point there is some kind of standardized system for these types of
notifications, it will be simple to just add a new value for the
lxc.notification.type option.

Signed-off-by: Christian Seiler <christian at iwakd.de>
Cc: Serge Hallyn <serge.hallyn at ubuntu.com>
Cc: Guido Jäkel <G.Jaekel at dnb.de>
---
 src/lxc/Makefile.am    |    1 +
 src/lxc/conf.c         |    8 +
 src/lxc/conf.h         |    3 +
 src/lxc/confile.c      |   34 +++++
 src/lxc/notification.c |  349 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/notification.h |   50 +++++++
 src/lxc/start.c        |   22 +++-
 src/lxc/start.h        |    1 +
 src/lxc/state.c        |    1 +
 src/lxc/state.h        |    3 +-
 10 files changed, 468 insertions(+), 4 deletions(-)
 create mode 100644 src/lxc/notification.c
 create mode 100644 src/lxc/notification.h

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 1450ca6..422b742 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>
@@ -2253,6 +2254,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;
@@ -2540,6 +2546,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);
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index dcf79fe..5ed67ec 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -31,6 +31,7 @@
 #include <lxc/list.h>
 
 #include <lxc/start.h> /* for lxc_handler */
+#include <lxc/notification.h> /* for notification types */
 
 enum {
 	LXC_NET_EMPTY,
@@ -237,6 +238,8 @@ struct lxc_conf {
 #endif
 	char *seccomp;  // filename with the seccomp rules
 	int maincmd_fd;
+	lxc_notification_type_t notification_type;
+	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 2d14e0f..f48b8c0 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_type(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.type",    config_notification_type    },
+	{ "lxc.notification.path",    config_notification_path    },
 	{ "lxc.cgroup",               config_cgroup               },
 	{ "lxc.mount",                config_mount                },
 	{ "lxc.rootfs.mount",         config_rootfs_mount         },
@@ -880,6 +884,33 @@ static int config_aa_profile(const char *key, char *value, struct lxc_conf *lxc_
 }
 #endif
 
+static int config_notification_type(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+	lxc_notification_type_t notification_type = lxc_str2notification_type(value);
+	if (notification_type == -1)
+		return -1;
+
+	lxc_conf->notification_type = notification_type;
+	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 +1628,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.type = %s\n", lxc_notification_type2str(c->notification_type));
+	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..d3d6602
--- /dev/null
+++ b/src/lxc/notification.c
@@ -0,0 +1,349 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * 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
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "notification.h"
+#include "start.h"
+#include "state.h"
+#include "conf.h"
+#include "mainloop.h"
+#include "log.h"
+
+#define FIFO_EXTERNAL_PATH          "/var/lib/lxc/%s/notification-fifo"
+#define FIFO_INTERNAL_PATH_DFLT     "/dev/lxc-notify"
+
+lxc_log_define(lxc_notification, lxc);
+
+static char *strnotification_type[] = {
+	"none",
+	"fifo",
+};
+
+struct notification_data_fifo {
+	int fd;
+};
+
+const char *lxc_notification_type2str(lxc_notification_type_t type)
+{
+	if (type < LXC_NOTIFICATION_NONE || type > _LXC_NOTIFICATION_MAX - 1)
+		return NULL;
+	return strnotification_type[type];
+}
+
+lxc_notification_type_t lxc_str2notification_type(const char *type)
+{
+	int i, len;
+	len = sizeof(strnotification_type)/sizeof(strnotification_type[0]);
+	for (i = 0; i < len; i++)
+		if (!strcmp(strnotification_type[i], type))
+			return i;
+
+	ERROR("invalid notification type '%s'", type);
+	return -1;
+}
+
+static int notification_init_fifo(struct lxc_handler *handler);
+static int notification_mount_hook_fifo(const char *name, struct lxc_conf *conf);
+static int notification_mainloop_add_fifo(const char *name, struct lxc_epoll_descr *descr,
+                                          struct lxc_handler *handler);
+static int notification_callback_fifo(int fd, void *data, struct lxc_epoll_descr *descr);
+static int notification_fini_fifo(struct lxc_handler *handler);
+
+int lxc_notification_init(struct lxc_handler *handler)
+{
+	switch (handler->conf->notification_type) {
+		case LXC_NOTIFICATION_NONE: return 0;
+		case LXC_NOTIFICATION_FIFO: return notification_init_fifo(handler);
+		default:
+			ERROR("invalid notification type");
+			return -1;
+	}
+}
+
+int lxc_notification_fini(struct lxc_handler *handler)
+{
+	switch (handler->conf->notification_type) {
+		case LXC_NOTIFICATION_NONE: return 0;
+		case LXC_NOTIFICATION_FIFO: return notification_fini_fifo(handler);
+		default:
+			ERROR("invalid notification type");
+			return -1;
+	}
+}
+
+int lxc_notification_mount_hook(const char *name, struct lxc_conf *conf)
+{
+	switch (conf->notification_type) {
+		case LXC_NOTIFICATION_NONE: return 0;
+		case LXC_NOTIFICATION_FIFO: return notification_mount_hook_fifo(name, conf);
+		default:
+			ERROR("invalid notification type");
+			return -1;
+	}
+}
+
+int lxc_notification_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
+                                  struct lxc_handler *handler)
+{
+	switch (handler->conf->notification_type) {
+		case LXC_NOTIFICATION_NONE: return 0;
+		case LXC_NOTIFICATION_FIFO: return notification_mainloop_add_fifo(name, descr, handler);
+		default:
+			ERROR("invalid notification type");
+			return -1;
+	}
+}
+
+lxc_state_t lxc_notification_initial_running_state(struct lxc_handler *handler)
+{
+	switch (handler->conf->notification_type) {
+		case LXC_NOTIFICATION_NONE: return RUNNING;
+		case LXC_NOTIFICATION_FIFO: return BOOTING;
+		default: return RUNNING;
+	}
+}
+
+int notification_init_fifo(struct lxc_handler *handler)
+{
+	int ret;
+	char external_name[MAXPATHLEN];
+
+	struct notification_data_fifo *d = malloc(sizeof(struct notification_data_fifo));
+
+	d->fd = -1;
+
+	snprintf(external_name, MAXPATHLEN, FIFO_EXTERNAL_PATH, handler->name);
+
+	/* remove old object that was there before */
+	unlink(external_name);
+
+	ret = mknod(external_name, S_IFIFO | 0600, 0);
+	if (ret < 0) {
+		SYSERROR("error creating notification FIFO %s", external_name);
+		free(d);
+		return -1;
+	}
+
+	/* open it as O_RDWR so we don't get EOF and the pipe
+	 * remains open over multiple programs writing to it
+	 */
+	d->fd = open(external_name, O_RDWR | O_NONBLOCK);
+	if (d->fd < 0) {
+		SYSERROR("error opening notification FIFO for reading");
+		unlink(external_name);
+		free(d);
+		return -1;
+	}
+
+	ret = fcntl(d->fd, F_SETFD, FD_CLOEXEC);
+	if (ret < 0) {
+		SYSERROR("error opening notification FIFO for reading (setting FD_CLOEXEC on fd)");
+		close(d->fd);
+		unlink(external_name);
+		free(d);
+		return -1;
+	}
+
+	handler->notification_data = d;
+	return 0;
+}
+
+int notification_mount_hook_fifo(const char *name, struct lxc_conf *conf)
+{
+	int ret;
+	char inside[MAXPATHLEN];
+	char outside[MAXPATHLEN];
+	const char *inside_path = FIFO_INTERNAL_PATH_DFLT;
+
+	if (conf->notification_path)
+		inside_path = conf->notification_path;
+
+	if (inside_path[0] == '/')
+		snprintf(inside, MAXPATHLEN, "%s%s", conf->rootfs.mount, inside_path);
+	else
+		snprintf(inside, MAXPATHLEN, "%s/%s", conf->rootfs.mount, inside_path);
+
+	snprintf(outside, MAXPATHLEN, FIFO_EXTERNAL_PATH, name);
+
+	unlink(inside);
+	ret = open(inside, O_CREAT | O_RDWR, 0600);
+	if (ret < 0) {
+		SYSERROR("could not create notification FIFO mountpoint inside container");
+		return -1;
+	}
+	/* we just wanted to create the file so we can
+	 * bind-mount the FIFO 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 FIFO into the container");
+		return -1;
+	}
+
+	return 0;
+}
+
+int notification_mainloop_add_fifo(const char *name, struct lxc_epoll_descr *descr,
+                                   struct lxc_handler *handler)
+{
+	struct notification_data_fifo *d = (struct notification_data_fifo *)handler->notification_data;
+
+	int ret;
+
+	if (!d) {
+		ERROR("no notification fifo passed on");
+		return -1;
+	}
+
+	ret = lxc_mainloop_add_handler(descr, d->fd, notification_callback_fifo,
+	                               handler);
+	if (ret) {
+		ERROR("failed to add handler for notification fifo");
+		close(d->fd);
+		d->fd = -1;
+	}
+
+	return ret;
+}
+
+int notification_callback_fifo(int fd, void *data, struct lxc_epoll_descr *descr)
+{
+	struct lxc_handler *handler = data;
+	struct notification_data_fifo *d = (struct notification_data_fifo *)handler->notification_data;
+
+	int ret;
+	char buf[256];
+	char command[256];
+	char *nl;
+	char *ptr;
+	lxc_state_t new_state;
+
+	ret = read(fd, buf, sizeof(buf) - 2);
+	if (ret < 0) {
+		/* ignore if this was a bogus poll event */
+		if (errno == EAGAIN || errno == EINTR)
+			return 0;
+		SYSERROR("failed to read data from notification FIFO");
+		return -1;
+	} else if (ret == 0) {
+		char external_name[MAXPATHLEN];
+
+		/* close and re-open the fifo, else we'll only
+		 * continue to get EOFs
+		 */
+		lxc_mainloop_del_handler(descr, fd);
+		close(fd);
+
+		snprintf(external_name, MAXPATHLEN, FIFO_EXTERNAL_PATH, handler->name);
+		fd = open(external_name, O_RDWR | O_NONBLOCK);
+		if (fd < 0) {
+			SYSERROR("failed to re-open notification FIFO after unexpected EOF");
+			d->fd = -1;
+			return -1;
+		}
+		ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
+		if (ret < 0) {
+			SYSERROR("failed to re-open notification FIFO after unexpected EOF (setting FD_CLOEXEC on fd)");
+			close(fd);
+			d->fd = -1;
+			return -1;
+		}
+
+		d->fd = fd;
+		ret = lxc_mainloop_add_handler(descr, d->fd, notification_callback_fifo,
+		                               handler);
+		if (ret < 0) {
+			ERROR("failed to re-add mainloop handler for re-opened FIFO");
+			return -1;
+		}
+
+		return 0;
+	}
+
+	/* make sure string is newline and zero-terminated,
+	 * makes parsing easier */
+	buf[ret] = '\n';
+	buf[ret + 1] = '\0';
+
+	ptr = buf;
+	nl = strstr(ptr, "\n");
+	do {
+		/* end of string */
+		if (nl == NULL)
+			break;
+
+		/* command is as large as buf, so this works */
+		strncpy(command, ptr, nl - ptr);
+		command[nl - ptr] = '\0';
+
+		ptr = nl + 1;
+		nl = strstr(ptr, "\n");
+
+		if (strlen(command) == 0)
+			continue;
+
+		new_state = lxc_str2state(command);
+		switch (new_state) {
+			case RUNNING:
+			case STOPPING:
+				ret = lxc_set_state(handler->name, handler, new_state);
+				break;
+			default:
+				/* just ignore */
+				break;
+		}
+	} while (ptr < buf + sizeof(buf) - 1);
+
+	return 0;
+}
+
+int notification_fini_fifo(struct lxc_handler *handler)
+{
+	struct notification_data_fifo *d = (struct notification_data_fifo *)handler->notification_data;
+
+	char external_name[MAXPATHLEN];
+
+	if (d) {
+		if (d->fd >= 0)
+			close(d->fd);
+		free(d);
+		handler->notification_data = NULL;
+	}
+
+	snprintf(external_name, MAXPATHLEN, FIFO_EXTERNAL_PATH, handler->name);
+	unlink(external_name);
+
+	return 0;
+}
diff --git a/src/lxc/notification.h b/src/lxc/notification.h
new file mode 100644
index 0000000..3d3a621
--- /dev/null
+++ b/src/lxc/notification.h
@@ -0,0 +1,50 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * 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"
+
+typedef enum {
+	LXC_NOTIFICATION_NONE,
+	LXC_NOTIFICATION_FIFO,
+	_LXC_NOTIFICATION_MAX
+} lxc_notification_type_t;
+
+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);
+
+lxc_notification_type_t lxc_str2notification_type(const char *type);
+const char *lxc_notification_type2str(lxc_notification_type_t type);
+
+
+
+#endif
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 3e26b27..92fbc78 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);
 
@@ -334,6 +335,11 @@ int lxc_poll(const char *name, struct lxc_handler *handler)
 		ERROR("failed to add command handler to mainloop");
 		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)) {
@@ -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