[lxc-devel] [RFC PATCH] allow multiple monitor clients

Dwight Engen dwight.engen at oracle.com
Thu Apr 18 18:09:49 UTC 2013


After the recent discussions on the lxc monitor, I took a closer look at
it. It seems like Serge was resigned to having some sort of daemon as
having multicast unix domain supported by the kernel is not going to
happen any time soon, and doing it over loopback has some
complications too.

Initially I thought it would be nice to just have lxc-start "be the
daemon" as Stéphane had suggested. I tried this out, putting the fds
for the consumer clients (lxc-monitors) in lxc_mainloop , but this is
problematic for the scenario where lxc-monitor runs first and we miss
some startup states.

So here is what I'm proposing: when lxc-monitor starts, it attempts to
start lxc-monitord. lxc-monitord creates a fifo and a socket on lxcpath.

Producers (containers) wishing to send messages open the fifo and
write their lxc_msg's on it. The reason I used a fifo is so that we get
permission checks on the opening of it. Right now it is created 0600.
This fixes the problem we have today where anyone can open the unix
socket and send state changes in.

Consumers (lxc-monitor) connect to the unix socket and each gets a copy
of the messages. lxc-monitord can do a PEERCRED to ensure the client
should be able to get these messages, this is one benefit to using unix
domain. I don't think it would be difficult to write a lxc-monitor
like client that would bridge the messages onto d-bus.

When lxc-monitord hasn't had a client for 30 seconds it will exit.

Attached is a proof of concept patch for your comments. It works for
the common cases that I've tried but I know it needs some cleanup and
testing if we like the approach. The monitor.c part is hard to read
because that file is mostly rewritten.

I need to test S.Çağlar Onur's scenario of parallel startup with his
test script. We need to decide if we want to monitor per lxcpath or per
container, today it is per path and I think that being able to do
lxc-monitor * would be very useful.

--

Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
---
 src/lxc/Makefile.am    |   2 +
 src/lxc/af_unix.c      |   5 +-
 src/lxc/lxc_monitor.c  |   2 +
 src/lxc/lxc_monitord.c | 334 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/mainloop.c     |  11 +-
 src/lxc/mainloop.h     |   7 ++
 src/lxc/monitor.c      | 180 ++++++++++++++++----------
 src/lxc/monitor.h      |  11 +-
 src/lxc/start.c        |   7 +-
 9 files changed, 484 insertions(+), 75 deletions(-)
 create mode 100644 src/lxc/lxc_monitord.c

diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index ebeca466..1fa0fa8 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -150,6 +150,7 @@ bin_PROGRAMS = \
 	lxc-start \
 	lxc-execute \
 	lxc-monitor \
+	lxc-monitord \
 	lxc-wait \
 	lxc-console \
 	lxc-freeze \
@@ -181,6 +182,7 @@ lxc_freeze_SOURCES = lxc_freeze.c
 lxc_info_SOURCES = lxc_info.c
 lxc_init_SOURCES = lxc_init.c
 lxc_monitor_SOURCES = lxc_monitor.c
+lxc_monitord_SOURCES = lxc_monitord.c
 lxc_restart_SOURCES = lxc_restart.c
 lxc_start_SOURCES = lxc_start.c
 lxc_stop_SOURCES = lxc_stop.c
diff --git a/src/lxc/af_unix.c b/src/lxc/af_unix.c
index eff13d4..ceef340 100644
--- a/src/lxc/af_unix.c
+++ b/src/lxc/af_unix.c
@@ -52,6 +52,7 @@ int lxc_af_unix_open(const char *path, int type, int flags)
 
 	addr.sun_family = AF_UNIX;
 	/* copy entire buffer in case of abstract socket */
+	/* FIXME: this is unsafe if path is too long... */
 	memcpy(addr.sun_path, path,
 	       path[0]?strlen(path):sizeof(addr.sun_path));
 
@@ -61,7 +62,7 @@ int lxc_af_unix_open(const char *path, int type, int flags)
 		errno = tmp;
 		return -1;
 	}
-	
+
 	if (type == SOCK_STREAM && listen(fd, 100)) {
 		int tmp = errno;
 		close(fd);
@@ -76,7 +77,7 @@ int lxc_af_unix_close(int fd)
 {
 	struct sockaddr_un addr;
 	socklen_t addrlen = sizeof(addr);
-	
+
 	if (!getsockname(fd, (struct sockaddr *)&addr, &addrlen) &&
 	    addr.sun_path[0])
 		unlink(addr.sun_path);
diff --git a/src/lxc/lxc_monitor.c b/src/lxc/lxc_monitor.c
index 8c15869..0ca829f 100644
--- a/src/lxc/lxc_monitor.c
+++ b/src/lxc/lxc_monitor.c
@@ -87,6 +87,8 @@ int main(int argc, char *argv[])
 		return -1;
 	}
 
+	lxc_monitord_spawn(my_args.lxcpath);
+
 	fd = lxc_monitor_open(my_args.lxcpath);
 	if (fd < 0)
 		return -1;
diff --git a/src/lxc/lxc_monitord.c b/src/lxc/lxc_monitord.c
new file mode 100644
index 0000000..3e9c708
--- /dev/null
+++ b/src/lxc/lxc_monitord.c
@@ -0,0 +1,334 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2012 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen at oracle.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
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <lxc/af_unix.h>
+#include <lxc/log.h>
+#include <lxc/mainloop.h>
+#include <lxc/monitor.h>
+#include <lxc/utils.h>
+
+lxc_log_define(lxc_monitord, lxc);
+
+/*
+ * Defines the structure to store the monitor information
+ * @fifofd       : the file descriptor for publishers (containers) to write state
+ * @listenfd     : the file descriptor for subscribers (lxc-monitors) to connect
+ * @clientfds    : accepted client file descriptors
+ * @clientfds_cnt: the count of valid fds in clientfds
+ */
+struct lxc_monitor {
+	int fifofd;
+	int listenfd;
+	int clientfds[1024];
+	int clientfds_cnt;
+	struct lxc_epoll_descr descr;
+};
+
+static int lxc_monitord_fifo_create(struct lxc_monitor *mon, const char *lxcpath)
+{
+	char fifo_path[PATH_MAX];
+	int ret;
+
+	ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor", lxcpath);
+	if (ret < 0 || ret >= sizeof(fifo_path)) {
+		ERROR("lxcpath too long to monitor fifo");
+		return -1;
+	}
+
+	ret = mknod(fifo_path, S_IFIFO|S_IRUSR|S_IWUSR, 0);
+	if (ret < 0) {
+		INFO("existing monitor fifo at %s, monitor already running?", fifo_path);
+		return -1;
+	}
+	mon->fifofd = open(fifo_path, O_RDWR);
+	if (mon->fifofd < 0) {
+		ERROR("failed to open monitor fifo");
+		return -1;
+	}
+	return 0;
+}
+
+static int lxc_monitord_fifo_delete(const char *lxcpath)
+{
+	char fifo_path[PATH_MAX];
+	int ret;
+
+	ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor", lxcpath);
+	if (ret < 0 || ret >= sizeof(fifo_path)) {
+		ERROR("lxcpath too long to monitor fifo");
+		return -1;
+	}
+	unlink(fifo_path);
+	return 0;
+}
+
+static void lxc_monitord_sockfd_remove(struct lxc_monitor *mon, int fd) {
+	int i;
+
+	lxc_mainloop_del_handler(&mon->descr, fd);
+	close(fd);
+
+	for (i = 0; i < mon->clientfds_cnt; i++) {
+		if (mon->clientfds[i] == fd)
+			break;
+	}
+	if (i >= mon->clientfds_cnt) {
+		CRIT("fd not found");
+		exit(EXIT_FAILURE);
+	}
+	DEBUG("MON: removing client fd:%d memmoving %p %p %lu bytes", fd,
+		&mon->clientfds[mon->clientfds_cnt],
+		&mon->clientfds[i],
+		(&mon->clientfds[mon->clientfds_cnt-1] - &mon->clientfds[i]) * sizeof(int));
+	memmove(&mon->clientfds[i], &mon->clientfds[i+1],
+		(&mon->clientfds[mon->clientfds_cnt-1] - &mon->clientfds[i]) * sizeof(int));
+	mon->clientfds_cnt--;
+}
+
+static int lxc_monitord_sock_handler(int fd, void *data,
+				     struct lxc_epoll_descr *descr)
+{
+	struct lxc_monitor *mon = data;
+	lxc_monitord_sockfd_remove(mon, fd);
+	return 0;
+}
+
+static int lxc_monitord_sock_accept(int fd, void *data,
+				    struct lxc_epoll_descr *descr)
+{
+	int ret,clientfd;
+	struct lxc_monitor *mon = data;
+
+	ret = -1;
+	clientfd = accept(fd, NULL, 0);
+	if (clientfd < 0) {
+		SYSERROR("failed to accept connection");
+		goto out;
+	}
+
+	if (fcntl(clientfd, F_SETFD, FD_CLOEXEC)) {
+		SYSERROR("failed to set close-on-exec on incoming connection");
+		goto err1;
+	}
+
+	/* FIXME: check the connected clients credentials here, drop if we don't
+	 * like them.
+	int on = 1;
+	if (setsockopt(clientfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+		ERROR("failed to enable credential on socket");
+		goto err1;
+	}
+	 */
+
+	ret = lxc_mainloop_add_handler(&mon->descr, clientfd,
+				       lxc_monitord_sock_handler, mon);
+	if (ret) {
+		ERROR("failed to add socket handler");
+		goto err1;
+	}
+	mon->clientfds[mon->clientfds_cnt++] = clientfd;
+	INFO("accepted new client fd:%d clientfds_cnt:%d", clientfd, mon->clientfds_cnt);
+	goto out;
+
+err1:
+	close(clientfd);
+out:
+	return ret;
+}
+
+static int lxc_monitord_sock_create(struct lxc_monitor *mon, const char *lxcpath)
+{
+	struct sockaddr_un addr;
+	int fd;
+
+	if (lxc_monitor_sock_name(lxcpath, &addr) < 0)
+		return -1;
+
+	fd = lxc_af_unix_open(addr.sun_path, SOCK_STREAM, O_TRUNC);
+	if (fd < 0) {
+		ERROR("failed to open unix socket : %s", strerror(errno));
+		return -1;
+	}
+
+	mon->listenfd = fd;
+	return 0;
+}
+
+static int lxc_monitord_sock_delete(const char *lxcpath)
+{
+	struct sockaddr_un addr;
+
+	if (lxc_monitor_sock_name(lxcpath, &addr) < 0)
+		return -1;
+	if (addr.sun_path[0])
+		unlink(addr.sun_path);
+	return 0;
+}
+
+static int lxc_monitord_create(struct lxc_monitor *mon, const char *lxcpath)
+{
+	int ret;
+
+	ret = lxc_monitord_fifo_create(mon, lxcpath);
+	if (ret < 0)
+		return ret;
+
+	ret = lxc_monitord_sock_create(mon, lxcpath);
+	return ret;
+}
+
+static void lxc_monitord_delete(struct lxc_monitor *mon, const char *lxcpath)
+{
+	int i;
+
+	lxc_mainloop_del_handler(&mon->descr, mon->listenfd);
+	close(mon->listenfd);
+	lxc_monitord_sock_delete(lxcpath);
+
+	lxc_mainloop_del_handler(&mon->descr, mon->fifofd);
+	close(mon->fifofd);
+	lxc_monitord_fifo_delete(lxcpath);
+
+	for (i = 0; i < mon->clientfds_cnt; i++) {
+		lxc_mainloop_del_handler(&mon->descr, mon->clientfds[i]);
+		close(mon->clientfds[i]);
+	}
+	mon->clientfds_cnt = 0;
+}
+
+static int lxc_monitord_fifo_handler(int fd, void *data,
+				     struct lxc_epoll_descr *descr)
+{
+	int ret,i;
+	struct lxc_msg msglxc;
+	struct lxc_monitor *mon = data;
+
+	ret = read(fd, &msglxc, sizeof(msglxc));
+	if (ret != sizeof(msglxc)) {
+		SYSERROR("read fifo failed : %s", strerror(errno));
+		return 1;
+	}
+
+	for (i = 0; i < mon->clientfds_cnt; i++) {
+		ret = write(mon->clientfds[i], &msglxc, sizeof(msglxc));
+		if (ret < 0) {
+			ERROR("write failed to client sock, removing");
+			lxc_monitord_sockfd_remove(mon, i);
+		}
+	}
+
+	return 0;
+}
+
+static int lxc_monitord_mainloop_add(struct lxc_monitor *mon)
+{
+	int ret;
+
+	ret = lxc_mainloop_add_handler(&mon->descr, mon->fifofd,
+				       lxc_monitord_fifo_handler, mon);
+	if (ret < 0) {
+		ERROR("failed to add to mainloop monitor handler for fifo");
+		return -1;
+	}
+
+	ret = lxc_mainloop_add_handler(&mon->descr, mon->listenfd,
+				       lxc_monitord_sock_accept, mon);
+	if (ret < 0) {
+		ERROR("failed to add to mainloop monitor handler for listen socket");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void lxc_monitord_sig_handler(int sig)
+{
+	INFO("caught signal %d", sig);
+}
+
+int main(int argc, char *argv[])
+{
+	struct lxc_monitor mon;
+	int ret,pipefd;
+	char *lxcpath = argv[1];
+	char logpath[PATH_MAX];
+
+	signal(SIGTERM, lxc_monitord_sig_handler);
+	snprintf(logpath, sizeof(logpath), "%s/lxc-monitord.log", lxcpath);
+	ret = lxc_log_init(NULL, logpath, "INFO", "lxc-monitord", 0);
+	if (ret)
+		return ret;
+
+	pipefd = atoi(argv[2]);
+
+	ret = EXIT_FAILURE;
+	memset(&mon, 0, sizeof(mon));
+	if (lxc_mainloop_open(&mon.descr)) {
+		ERROR("failed to create mainloop");
+		goto out;
+	}
+
+	if (lxc_monitord_create(&mon, lxcpath)) {
+		goto out;
+	}
+
+	/* sync with parent */
+	write(pipefd, "S", 1);
+	close(pipefd);
+
+	if (lxc_monitord_mainloop_add(&mon)) {
+		ERROR("failed to add mainloop handlers");
+		goto out;
+	}
+
+	NOTICE("monitoring lxcpath %s", lxcpath);
+	for(;;) {
+		ret = lxc_mainloop_timeout(&mon.descr, 1000 * 30);
+		if (mon.clientfds_cnt <= 0)
+		{
+			NOTICE("no clients for 30 seconds, exiting");
+			break;
+		}
+	}
+
+	lxc_monitord_delete(&mon, lxcpath);
+	lxc_mainloop_close(&mon.descr);
+out:
+	return ret;
+}
diff --git a/src/lxc/mainloop.c b/src/lxc/mainloop.c
index 975215d..4805dcd 100644
--- a/src/lxc/mainloop.c
+++ b/src/lxc/mainloop.c
@@ -38,7 +38,7 @@ struct mainloop_handler {
 
 #define MAX_EVENTS 10
 
-int lxc_mainloop(struct lxc_epoll_descr *descr)
+int lxc_mainloop_timeout(struct lxc_epoll_descr *descr, int timeout_ms)
 {
 	int i, nfds;
 	struct mainloop_handler *handler;
@@ -46,7 +46,7 @@ int lxc_mainloop(struct lxc_epoll_descr *descr)
 
 	for (;;) {
 
-		nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, -1);
+		nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, timeout_ms);
 		if (nfds < 0) {
 			if (errno == EINTR)
 				continue;
@@ -64,11 +64,18 @@ int lxc_mainloop(struct lxc_epoll_descr *descr)
 				return 0;
 		}
 
+		if (nfds == 0 && timeout_ms != 0)
+			return 0;
+
 		if (lxc_list_empty(&descr->handlers))
 			return 0;
 	}
 }
 
+int lxc_mainloop(struct lxc_epoll_descr *descr) {
+	return lxc_mainloop_timeout(descr, -1);
+}
+
 int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd,
 			     lxc_mainloop_callback_t callback, void *data)
 {
diff --git a/src/lxc/mainloop.h b/src/lxc/mainloop.h
index 6b16242..c79c4fd 100644
--- a/src/lxc/mainloop.h
+++ b/src/lxc/mainloop.h
@@ -21,6 +21,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#ifndef _mainloop_h
+#define _mainloop_h
+
 #include "list.h"
 
 struct lxc_epoll_descr {
@@ -33,6 +36,8 @@ typedef int (*lxc_mainloop_callback_t)(int fd, void *data,
 
 extern int lxc_mainloop(struct lxc_epoll_descr *descr);
 
+extern int lxc_mainloop_timeout(struct lxc_epoll_descr *descr, int timeout_ms);
+
 extern int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd,
 				    lxc_mainloop_callback_t callback,
 				    void *data);
@@ -42,3 +47,5 @@ extern int lxc_mainloop_del_handler(struct lxc_epoll_descr *descr, int fd);
 extern int lxc_mainloop_open(struct lxc_epoll_descr *descr);
 
 extern int lxc_mainloop_close(struct lxc_epoll_descr *descr);
+
+#endif
diff --git a/src/lxc/monitor.c b/src/lxc/monitor.c
index afdaf67..0438fbc 100644
--- a/src/lxc/monitor.c
+++ b/src/lxc/monitor.c
@@ -20,6 +20,7 @@
  * 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 <errno.h>
 #include <unistd.h>
@@ -30,7 +31,7 @@
 #include <sys/stat.h>
 #include <sys/param.h>
 #include <sys/socket.h>
-#include <sys/un.h>
+#include <sys/wait.h>
 #include <netinet/in.h>
 #include <net/if.h>
 
@@ -43,35 +44,24 @@
 
 lxc_log_define(lxc_monitor, lxc);
 
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX 108
-#endif
-
-static void lxc_monitor_send(struct lxc_msg *msg, const char *lxcpath)
+/* routines used by monitor publishers (containers) */
+static void lxc_monitor_fifo_send(struct lxc_msg *msg, const char *lxcpath)
 {
-	int fd;
-	struct sockaddr_un addr = { .sun_family = AF_UNIX };
-	char *offset = &addr.sun_path[1];
-	size_t ret, len;
+	int fd,ret;
+	char fifo_path[PATH_MAX];
 
-	/*
-	 * addr.sun_path is only 108 bytes.
-	 * should we take a hash of lxcpath?  a subset of it?
-	 */
-	len = sizeof(addr.sun_path) - 1;
-	ret = snprintf(offset, len, "%s/lxc-monitor", lxcpath);
-	if (ret < 0 || ret >= len) {
-		ERROR("lxcpath too long to open monitor");
+	//FIXME: BuildAssert(sizeof(*msg) < PIPE_BUF), "write not guaranteed atomic");
+	ret = snprintf(fifo_path, sizeof(fifo_path), "%s/monitor", lxcpath);
+	if (ret < 0 || ret >= sizeof(fifo_path)) {
+		ERROR("lxcpath too long to open monitor fifo");
 		return;
 	}
-
-	fd = socket(PF_UNIX, SOCK_DGRAM, 0);
-	if (fd < 0)
+	fd = open(fifo_path, O_WRONLY);
+	if (fd < 0) {
+		ERROR("failed to open monitor fifo %s", fifo_path);
 		return;
-
-	sendto(fd, msg, sizeof(*msg), 0,
-	       (const struct sockaddr *)&addr, sizeof(addr));
-
+	}
+	ret = write(fd, msg, sizeof(*msg));
 	close(fd);
 }
 
@@ -82,72 +72,74 @@ void lxc_monitor_send_state(const char *name, lxc_state_t state, const char *lxc
 	strncpy(msg.name, name, sizeof(msg.name));
 	msg.name[sizeof(msg.name) - 1] = 0;
 
-	lxc_monitor_send(&msg, lxcpath);
+	lxc_monitor_fifo_send(&msg, lxcpath);
 }
 
-int lxc_monitor_open(const char *lxcpath)
+
+/* routines used by monitor subscribers (lxc-monitor) */
+int lxc_monitor_close(int fd)
 {
-	struct sockaddr_un addr = { .sun_family = AF_UNIX };
-	char *offset = &addr.sun_path[1];
-	int fd;
-	size_t ret, len;
+	return close(fd);
+}
 
-	/*
-	 * addr.sun_path is only 108 bytes.
-	 * should we take a hash of lxcpath?  a subset of it?
+int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) {
+	size_t len,ret;
+	char *sockname = &addr->sun_path[0]; // 1 for abstract
+
+	/* addr.sun_path is only 108 bytes.
+	 * should we take a hash of lxcpath?  a subset of it? ftok()? none
+	 * are guaranteed.
 	 */
-	len = sizeof(addr.sun_path) - 1;
-	ret = snprintf(offset, len, "%s/lxc-monitor", lxcpath);
+	memset(addr, 0, sizeof(*addr));
+	addr->sun_family = AF_UNIX;
+	len = sizeof(addr->sun_path) - 1;
+	ret = snprintf(sockname, len, "%s/monitor-sock", lxcpath);
 	if (ret < 0 || ret >= len) {
-		ERROR("lxcpath too long to open monitor");
+		ERROR("lxcpath too long for unix socket");
 		return -1;
 	}
+	return 0;
+}
+
+int lxc_monitor_open(const char *lxcpath)
+{
+	struct sockaddr_un addr;
+	size_t ret;
+	int fd;
 
-	fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+	if (lxc_monitor_sock_name(lxcpath, &addr) < 0)
+		return -1;
+
+	fd = socket(PF_UNIX, SOCK_STREAM, 0);
 	if (fd < 0) {
 		ERROR("socket : %s", strerror(errno));
 		return -1;
 	}
 
-	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) {
-		ERROR("bind : %s", strerror(errno));
-		close(fd);
-		return -1;
+	ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+	if (ret < 0) {
+		/* try to spawn a lxc-monitord here instead ? */
+		ERROR("connect : %s", strerror(errno));
+		goto err1;
 	}
-
 	return fd;
+err1:
+	close(fd);
+	return ret;
 }
 
-/* timeout of 0 means return immediately;  -1 means wait forever */
-int lxc_monitor_read_timeout(int fd, struct lxc_msg *msg, int timeout)
+int lxc_monitor_read_timeout(int fd, struct lxc_msg *msglxc, int timeout)
 {
 	struct sockaddr_un from;
 	socklen_t len = sizeof(from);
 	int ret;
-	fd_set rfds;
-	struct timeval tv;
 
-	if (timeout != -1) {
-		FD_ZERO(&rfds);
-		FD_SET(fd, &rfds);
-
-		tv.tv_sec = timeout;
-		tv.tv_usec = 0;
-
-		ret = select(fd+1, &rfds, NULL, NULL, &tv);
-		if (ret == -1)
-			return -1;
-		else if (!ret)
-			return -2;  // timed out
-	}
-
-	ret = recvfrom(fd, msg, sizeof(*msg), 0,
+	ret = recvfrom(fd, msglxc, sizeof(*msglxc), 0,
 		       (struct sockaddr *)&from, &len);
-	if (ret < 0) {
-		SYSERROR("failed to receive state");
+	if (ret <= 0) {
+		SYSERROR("client failed to receive lxc_msg (monitord died?)");
 		return -1;
 	}
-
 	return ret;
 }
 
@@ -156,7 +148,61 @@ int lxc_monitor_read(int fd, struct lxc_msg *msg)
 	return lxc_monitor_read_timeout(fd, msg, -1);
 }
 
-int lxc_monitor_close(int fd)
+
+
+/* used to spawn a monitord either on startup of a container, or when
+ * lxc-monitor starts
+ */
+int lxc_monitord_spawn(const char *lxcpath)
 {
-	return close(fd);
+	pid_t pid1,pid2;
+	int pipefd[2];
+	char pipefd_str[10];
+
+	char * const args[] = {
+		"/usr/bin/lxc-monitord",
+		(char *)lxcpath,
+		pipefd_str,
+		NULL,
+	};
+
+	/* double fork to avoid zombies when monitord exits */
+	pid1 = fork();
+	if (pid1 < 0) {
+		SYSERROR("failed to fork");
+		return -1;
+	}
+
+	if (pid1) {
+		waitpid(pid1, NULL, 0);
+		return 0;
+	}
+
+	if (pipe(pipefd) < 0) {
+		SYSERROR("failed to create pipe");
+		exit(EXIT_FAILURE);
+	}
+
+	pid2 = fork();
+	if (pid2) {
+		char c;
+		/* wait for daemon to create socket */
+		close(pipefd[1]);
+		read(pipefd[0], &c, 1);
+		close(pipefd[0]);
+		exit(EXIT_SUCCESS);
+	}
+
+	umask(0);
+	if (setsid() < 0) {
+		SYSERROR("failed to setsid");
+		exit(EXIT_FAILURE);
+	}
+	freopen("/dev/null", "r", stdin);
+	freopen("/dev/null", "w", stdout);
+	freopen("/dev/null", "w", stderr);
+	close(pipefd[0]);
+	sprintf(pipefd_str, "%d", pipefd[1]);
+	execvp(args[0], args);
+	exit(EXIT_FAILURE);
 }
diff --git a/src/lxc/monitor.h b/src/lxc/monitor.h
index 8bef4c7..faa606c 100644
--- a/src/lxc/monitor.h
+++ b/src/lxc/monitor.h
@@ -24,6 +24,10 @@
 #define __monitor_h
 
 #include <sys/param.h>
+#include <sys/un.h>
+
+#include <lxc/conf.h>
+#include <lxc/mainloop.h>
 
 typedef enum {
 	lxc_msg_state,
@@ -32,11 +36,14 @@ typedef enum {
 
 struct lxc_msg {
 	lxc_msg_type_t type;
-	char name[MAXPATHLEN];
+	char name[NAME_MAX+1];
 	int value;
 };
 
-void lxc_monitor_send_state(const char *name, lxc_state_t state,
+extern int lxc_monitor_open(const char *lxcpath);
+extern int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr);
+extern void lxc_monitor_send_state(const char *name, lxc_state_t state,
 			    const char *lxcpath);
+extern int lxc_monitord_spawn(const char *lxcpath);
 
 #endif
diff --git a/src/lxc/start.c b/src/lxc/start.c
index aefccd6..9451fb7 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -429,6 +429,9 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
 	if (lxc_command_init(name, handler, lxcpath))
 		goto out_free_name;
 
+	if (lxc_monitord_spawn(lxcpath))
+		goto out_close_maincmd_fd;
+
 	if (lxc_read_seccomp_config(conf) != 0) {
 		ERROR("failed loading seccomp policy");
 		goto out_close_maincmd_fd;
@@ -437,7 +440,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
 	/* Begin the set the state to STARTING*/
 	if (lxc_set_state(name, handler, STARTING)) {
 		ERROR("failed to set state '%s'", lxc_state2str(STARTING));
-		goto out_free_name;
+		goto out_close_maincmd_fd;
 	}
 
 	/* Start of environment variable setup for hooks */
@@ -808,7 +811,7 @@ int lxc_spawn(struct lxc_handler *handler)
 	/* TODO - pass lxc.cgroup.dir (or user's pam cgroup) in for first argument */
 	if ((handler->cgroup = lxc_cgroup_path_create(NULL, name)) == NULL)
 		goto out_delete_net;
-	
+
 	if (lxc_cgroup_enter(handler->cgroup, handler->pid) < 0)
 		goto out_delete_net;
 
-- 
1.8.1.4






More information about the lxc-devel mailing list