Hello,

  This patch changes the way utmp is handled to be far more robust. It
allows shutdown for fedora core 12, by instigating a timer once the
shutdown is noticed in utmp to check the number of tasks remaining, as
well as allowing the /var/run/utmp file to be deleted and remade (for
example during boot). Depending on whats in the run levels it will
enable container shutdown and reboot on a wider range of container 
operating systems. 

  Theres a one line fix to lxc.spec.in as well, as a recent patch to 
move lxc-init out of libexec broke "make rpm".
 
Signed-off-by: Andy Phillips <a...@lmax.com>

Andrew Phillips
Head of Systems

www.lmax.com 

Office: +44 203 1922509
Mobile: +44 (0)7595 242 900

LMAX | Level 2, Yellow Building | 1 Nicholas Road | London | W11 4AN




The information in this e-mail and any attachment is confidential and is 
intended only for the named recipient(s). The e-mail may not be disclosed or 
used by any person other than the addressee, nor may it be copied in any way. 
If you are not a named recipient please notify the sender immediately and 
delete any copies of this message. Any unauthorized copying, disclosure or 
distribution of the material in this e-mail is strictly forbidden. Any view or 
opinions presented are solely those of the author and do not necessarily 
represent those of the company.
diff --git a/src/lxc/utmp.c b/src/lxc/utmp.c
index 319a5ce..166b24d 100644
--- a/src/lxc/utmp.c
+++ b/src/lxc/utmp.c
@@ -28,6 +28,8 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <sys/inotify.h>
+#include <sys/ioctl.h>
+#include <sys/timerfd.h>
 
 #include "conf.h"
 #include "cgroup.h"
@@ -41,21 +43,128 @@
 
 lxc_log_define(lxc_utmp, lxc);
 
+typedef void (*lxc_mainloop_timer_t) (void *data);
+
+static int utmp_get_runlevel(struct lxc_conf *conf);
+static int utmp_get_ntasks(struct lxc_handler *handler);
+static int utmp_shutdown_handler(int fd, void *data,
+				 struct lxc_epoll_descr *descr);
+static int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
+			      lxc_mainloop_callback_t callback, void *data);
+static int lxc_utmp_del_timer(struct lxc_epoll_descr *descr);
+
+static char container_prev_runlevel = 'N', container_curr_runlevel = 'N';
+
+#define CONTAINER_STARTING  0
+#define CONTAINER_REBOOTING 1
+#define CONTAINER_HALTING   2
+#define CONTAINER_RUNNING   4
+
+static char container_state = CONTAINER_STARTING;
+
+/* Not wild about timer_fd. However we want to test and call _del_timer from
+ * utmp_handler, and I can't see a neat way of accessing the timer_fd, as
+ * opposed to utmp_handler's inotify fd, short of searching the list 
+ * of lxc_handlers, but then without the fd, we can't even search in the way
+ * lxc_mainloop_del_handler does. 
+ * 
+ * This limits us to only one timer so its not as generic a solution as 
+ * I'd have liked. 
+ * 
+ * timercreate_fd file descriptor for utmp_shutdown_handler
+ */
+static int timer_fd = -1;
+
 static int utmp_handler(int fd, void *data, struct lxc_epoll_descr *descr)
 {
-	struct inotify_event ie;
-	struct utmpx *utmpx;
+	struct inotify_event *ie;
+	int size, ret, length;
+
 	struct lxc_handler *handler = (struct lxc_handler *)data;
 	struct lxc_conf *conf = handler->conf;
-	char prevrun_level = 'N', currun_level = 'N';
-	int ntasks, ret;
-	char path[MAXPATHLEN];
 
-	if (read(fd, &ie, sizeof(ie)) < 0) {
-		SYSERROR("failed to read utmp notification");
+	/* we're monitoring a directory. ie->name is not included in sizeof(struct inotify_event)
+	 * if we don't read it all at once, read gives us EINVAL, so we read and cast to struct ie
+	 */
+	char buffer[MAXPATHLEN];
+
+	if (ioctl(fd, FIONREAD, &size) < 0) {
+		SYSERROR("cannot determine the size of this notification");
+		return -1;
+	}
+
+	if (read(fd, buffer, size) < 0) {
+		SYSERROR("failed to read notification");
 		return -1;
 	}
 
+	ie = (struct inotify_event *)buffer;
+
+	if (ie->len <= 0) {
+		SYSERROR("inotify event with no name");
+		return -1;
+	}
+
+	ret = 0;
+
+	DEBUG("got inotify event %d for %s", ie->mask, ie->name);
+
+	length = (4 < ie->len) ? 4 : ie->len;
+
+	/* only care about utmp */
+
+	if (!strncmp(ie->name, "utmp", length)) {
+
+		if (ie->mask & IN_CREATE)
+			ret = utmp_get_runlevel(conf);
+
+		if (ie->mask & IN_MODIFY)
+			ret = utmp_get_runlevel(conf);
+
+		if (ret < 0)
+			goto out;
+
+		/* container halting, from running or starting state */
+		if (container_curr_runlevel == '0'
+		    && ((container_state == CONTAINER_RUNNING)
+			|| (container_state == CONTAINER_STARTING))) {
+			container_state = CONTAINER_HALTING;
+			if (timer_fd == -1)
+				lxc_utmp_add_timer(descr, utmp_shutdown_handler,
+						   data);
+			DEBUG("Container halting");
+		}
+
+		/* container rebooting, from running or starting state */
+		if (container_curr_runlevel == '6'
+		    && ((container_state == CONTAINER_RUNNING)
+			|| (container_state == CONTAINER_STARTING))) {
+			container_state = CONTAINER_REBOOTING;
+			if (timer_fd == -1)
+				lxc_utmp_add_timer(descr, utmp_shutdown_handler,
+						   data);
+			DEBUG("Container rebooting");
+		}
+
+		/* normal operation, running, from starting state. */
+		if (container_curr_runlevel > '0'
+		    && container_curr_runlevel < '6') {
+			container_state = CONTAINER_RUNNING;
+			if (timer_fd > 0)
+				lxc_utmp_del_timer(descr);
+			DEBUG("Container running");
+		}
+	}
+
+out:
+	return 0;
+}
+
+static int utmp_get_runlevel(struct lxc_conf *conf)
+{
+	struct utmpx *utmpx;
+	char path[MAXPATHLEN];
+
 	if (snprintf(path, MAXPATHLEN, "%s/var/run/utmp", conf->rootfs.path) >
 	    MAXPATHLEN) {
 		ERROR("path is too long");
@@ -72,39 +181,32 @@ static int utmp_handler(int fd, void *data, struct lxc_epoll_descr *descr)
 	while ((utmpx = getutxent())) {
 
 		if (utmpx->ut_type == RUN_LVL) {
-			prevrun_level = utmpx->ut_pid / 256;
-			currun_level = utmpx->ut_pid % 256;
+			container_prev_runlevel = utmpx->ut_pid / 256;
+			container_curr_runlevel = utmpx->ut_pid % 256;
+			DEBUG("utmp handler - run level is %c/%c",
+			      container_prev_runlevel, container_curr_runlevel);
 		}
 	}
 
-	ntasks = lxc_cgroup_nrtasks(handler->name);
-	if (ntasks < 0) {
-		ERROR("failed to get the number of tasks");
-		goto out;
-	}
+	endutxent();
 
-	if (ntasks == 1 && prevrun_level > '1' && prevrun_level < '6') {
+	return 0;
+}
 
-		DEBUG("run level is %c/%c", prevrun_level, currun_level);
-		DEBUG("there is %d tasks remaining", ntasks);
+static int utmp_get_ntasks(struct lxc_handler *handler)
+{
+	int ntasks;
 
-		if (currun_level == '0') {
-			INFO("container has shutdown");
-			kill(handler->pid, SIGKILL);
-		}
+	ntasks = lxc_cgroup_nrtasks(handler->name);
 
-		if (currun_level == '6') {
-			INFO("container has rebooted");
-			conf->reboot = 1;
-			kill(handler->pid, SIGKILL);
-		}
+	if (ntasks < 0) {
+		ERROR("failed to get the number of tasks");
+		return -1;
 	}
 
-	ret = 0;
-out:
-	endutxent();
+	DEBUG("there are %d tasks running", ntasks);
 
-	return ret;
+	return ntasks;
 }
 
 int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
@@ -117,7 +219,10 @@ int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
 	if (!conf->rootfs.path)
 		return 0;
 
-	if (snprintf(path, MAXPATHLEN, "%s/var/run/utmp", conf->rootfs.path) >
+	/* We set up a watch for the /var/run directory. We're only interested in 
+	 * utmp at the moment, but want to watch for delete and create events as well.
+	 */
+	if (snprintf(path, MAXPATHLEN, "%s/var/run", conf->rootfs.path) >
 	    MAXPATHLEN) {
 		ERROR("path is too long");
 		return -1;
@@ -140,7 +245,7 @@ int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
 		return -1;
 	}
 
-	wd = inotify_add_watch(fd, path, IN_MODIFY);
+	wd = inotify_add_watch(fd, path, IN_MODIFY | IN_CREATE);
 	if (wd < 0) {
 		SYSERROR("failed to add watch for '%s'", path);
 		close(fd);
@@ -153,5 +258,101 @@ int lxc_utmp_mainloop_add(struct lxc_epoll_descr *descr,
 		return -1;
 	}
 
+	DEBUG("Added '%s' to inotifywatch", path);
+
 	return 0;
 }
+
+static int utmp_shutdown_handler(int fd, void *data,
+				 struct lxc_epoll_descr *descr)
+{
+	int ntasks;
+	struct lxc_handler *handler = (struct lxc_handler *)data;
+	struct lxc_conf *conf = handler->conf;
+	uint64_t expirations;
+
+	/* read and clear notifications */
+	read(fd, &expirations, sizeof(expirations));
+
+	ntasks = utmp_get_ntasks(handler);
+
+	if (ntasks == 1 && (container_state == CONTAINER_HALTING)) {
+		INFO("container has shutdown");
+		/* shutdown timer */
+		lxc_utmp_del_timer(descr);
+
+		kill(handler->pid, SIGKILL);
+	}
+
+	if (ntasks == 1 && (container_state == CONTAINER_REBOOTING)) {
+		INFO("container has rebooted");
+		conf->reboot = 1;
+		/* shutdown timer */
+		lxc_utmp_del_timer(descr);
+		/* this seems a bit rough. */
+		kill(handler->pid, SIGKILL);
+	}
+	return 0;
+
+}
+
+int lxc_utmp_add_timer(struct lxc_epoll_descr *descr,
+		       lxc_mainloop_callback_t callback, void *data)
+{
+	int fd, result;
+	struct itimerspec timeout;
+	struct lxc_handler *handler = (struct lxc_handler *)data;
+
+	fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+	if (fd < 0) {
+		SYSERROR("failed to create timer");
+		return -1;
+	}
+
+	DEBUG("Setting up utmp shutdown timer");
+
+	/* set a one second timeout. Repeated. */
+	timeout.it_value.tv_sec = 1;
+	timeout.it_value.tv_nsec = 0;
+
+	timeout.it_interval.tv_sec = 1;
+	timeout.it_interval.tv_nsec = 0;
+
+	result = timerfd_settime(fd, 0, &timeout, NULL);
+
+	if (result < 0) {
+		SYSERROR("timerfd_settime:");
+		return -1;
+	}
+
+	if (lxc_mainloop_add_handler(descr, fd, callback, handler)) {
+		SYSERROR("failed to add utmp timer to mainloop");
+		close(fd);
+		return -1;
+	}
+
+	timer_fd = fd;
+
+	return 0;
+}
+
+int lxc_utmp_del_timer(struct lxc_epoll_descr *descr)
+{
+	int result;
+	struct itimerspec timeout;
+
+	DEBUG("Clearing utmp shutdown timer");
+
+	result = lxc_mainloop_del_handler(descr, timer_fd);
+	if (result < 0)
+		SYSERROR("failed to del utmp timer from mainloop");
+
+	/* shutdown timer_fd */
+	close(timer_fd);
+	timer_fd = -1;
+
+	if (result < 0)
+		return -1;
+	else
+		return 0;
+}
------------------------------------------------------------------------------
ThinkGeek and WIRED's GeekDad team up for the Ultimate 
GeekDad Father's Day Giveaway. ONE MASSIVE PRIZE to the 
lucky parental unit.  See the prize list and enter to win: 
http://p.sf.net/sfu/thinkgeek-promo
_______________________________________________
Lxc-devel mailing list
Lxc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/lxc-devel

Reply via email to