Replace the current tty service socket by a general command service
socket and plug for the moment only the existing tty service.

Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
Signed-off-by: Michel Normand <normand@fr.ibm.com>
---
 src/lxc/Makefile.am |    1 
 src/lxc/commands.h  |   46 ++++++++++++++++
 src/lxc/console.c   |   65 +++++++++++++++++------
 src/lxc/start.c     |  146 ++++++++++++++++++++++++++++++++++------------------
 4 files changed, 193 insertions(+), 65 deletions(-)
 create mode 100644 src/lxc/commands.h

Index: lxc/src/lxc/Makefile.am
===================================================================
--- lxc.orig/src/lxc/Makefile.am
+++ lxc/src/lxc/Makefile.am
@@ -19,6 +19,7 @@ pkginclude_HEADERS = \
 
 liblxc_la_SOURCES = \
 	arguments.c arguments.h \
+	commands.h \
 	create.c \
 	destroy.c \
 	start.c \
Index: lxc/src/lxc/commands.h
===================================================================
--- /dev/null
+++ lxc/src/lxc/commands.h
@@ -0,0 +1,46 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2009
+ *
+ * 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 __commands_h
+#define __commands_h
+
+enum {
+	LXC_COMMAND_TTY,
+	LXC_COMMAND_MAX,
+};
+
+struct lxc_request {
+	int type;
+	int data;
+};
+
+struct lxc_answer {
+	int fd;
+	int ret; /* 0 on success, -errno on failure */
+};
+
+struct lxc_command {
+	struct lxc_request request;
+	struct lxc_answer answer;
+};
+
+#endif
Index: lxc/src/lxc/console.c
===================================================================
--- lxc.orig/src/lxc/console.c
+++ lxc/src/lxc/console.c
@@ -31,47 +31,82 @@
 #include "error.h"
 
 #include <lxc/log.h>
+#include "commands.h"
 
 lxc_log_define(lxc_console, lxc);
 
-extern int lxc_console(const char *name, int ttynum, int *fd)
+static int receive_answer(int sock, struct lxc_answer *answer)
+{
+	int ret;
+
+	ret = lxc_af_unix_recv_fd(sock, &answer->fd, answer, sizeof(*answer));
+	if (ret < 0)
+		ERROR("failed to receive answer for the command");
+
+	return ret;
+}
+
+static int send_command(const char *name, struct lxc_command *command)
 {
 	struct sockaddr_un addr = { 0 };
 	int sock, ret = -1;
+	char *offset = &addr.sun_path[1];
 
 	snprintf(addr.sun_path, sizeof(addr.sun_path), "@%s", name);
 	addr.sun_path[0] = '\0';
 
 	sock = lxc_af_unix_connect(addr.sun_path);
 	if (sock < 0) {
-		ERROR("failed to connect to the tty service");
-		goto out;
+		WARN("failed to connect to '@%s': %s", offset, strerror(errno));
+		return -1;
 	}
 
-	ret = lxc_af_unix_send_credential(sock, &ttynum, sizeof(ttynum));
+	ret = lxc_af_unix_send_credential(sock, &command->request,
+					sizeof(command->request));
 	if (ret < 0) {
 		SYSERROR("failed to send credentials");
 		goto out_close;
 	}
 
-	ret = lxc_af_unix_recv_fd(sock, fd, &ttynum, sizeof(ttynum));
-	if (ret < 0) {
-		ERROR("failed to connect to the tty");
+	if (ret != sizeof(command->request)) {
+		SYSERROR("message only partially sent to '@%s'", offset);
 		goto out_close;
 	}
 
-	INFO("tty %d allocated", ttynum);
-
-	if (!ret) {
-		ERROR("console denied by '%s'", name);
+	ret = receive_answer(sock, &command->answer);
+	if (ret < 0)
 		goto out_close;
-	}
-
-	ret = 0;
-
 out:
 	return ret;
 out_close:
 	close(sock);
 	goto out;
 }
+
+extern int lxc_console(const char *name, int ttynum, int *fd)
+{
+	int ret;
+	struct lxc_command command = {
+		.request = { .type = LXC_COMMAND_TTY, .data = ttynum },
+	};
+
+	ret = send_command(name, &command);
+	if (ret < 0) {
+		ERROR("failed to send command");
+		return -1;
+	}
+
+	if (!ret) {
+		ERROR("console denied by '%s'", name);
+		return -1;
+	}
+
+	*fd = command.answer.fd;
+	if (*fd <0) {
+		ERROR("unable to allocate fd for tty %d", ttynum);
+		return -1;
+	}
+
+	INFO("tty %d allocated", ttynum);
+	return 0;
+}
Index: lxc/src/lxc/start.c
===================================================================
--- lxc.orig/src/lxc/start.c
+++ lxc/src/lxc/start.c
@@ -91,6 +91,7 @@ int signalfd(int fd, const sigset_t *mas
 #include "error.h"
 #include "af_unix.h"
 #include "mainloop.h"
+#include "commands.h"
 
 #include <lxc/lxc.h>
 #include <lxc/log.h>
@@ -132,10 +133,10 @@ static int setup_sigchld_fd(sigset_t *ol
 	return fd;
 }
 
-static int ttyservice_handler(int fd, void *data,
+static int incoming_command_handler(int fd, void *data,
 			      struct lxc_epoll_descr *descr);
 
-static int tty_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
+static int command_mainloop_add(const char *name, struct lxc_epoll_descr *descr,
 			     struct lxc_handler *handler)
 {
 	int ret, fd;
@@ -147,7 +148,7 @@ static int tty_mainloop_add(const char *
 
 	fd = lxc_af_unix_open(addr.sun_path, SOCK_STREAM, 0);
 	if (fd < 0) {
-		ERROR("failed to create the tty service point");
+		ERROR("failed to create the command service point");
 		return -1;
 	}
 
@@ -157,7 +158,7 @@ static int tty_mainloop_add(const char *
 		return -1;
 	}
 
-	ret = lxc_mainloop_add_handler(descr, fd, ttyservice_handler,
+	ret = lxc_mainloop_add_handler(descr, fd, incoming_command_handler,
 					handler);
 	if (ret) {
 		ERROR("failed to add handler for command socket");
@@ -175,6 +176,41 @@ static int sigchld_handler(int fd, void 
 	return 1;
 }
 
+static int command_handler(int fd, void *data,
+			      struct lxc_epoll_descr *descr);
+static int trigger_command(int fd, struct lxc_request *request,
+			struct lxc_handler *handler,
+			struct lxc_epoll_descr *descr);
+
+static int incoming_command_handler(int fd, void *data, struct lxc_epoll_descr *descr)
+{
+	int ret = 1, connection;
+
+	connection = accept(fd, NULL, 0);
+	if (connection < 0) {
+		SYSERROR("failed to accept connection");
+		return -1;
+	}
+
+	if (setsockopt(connection, SOL_SOCKET, SO_PASSCRED, &ret, sizeof(ret))) {
+		SYSERROR("failed to enable credential on socket");
+		goto out_close;
+	}
+
+	ret = lxc_mainloop_add_handler(descr, connection, command_handler, data);
+	if (ret) {
+		ERROR("failed to add handler");
+		goto out_close;
+	}
+
+out:
+	return ret;
+
+out_close:
+	close(connection);
+	goto out;
+}
+
 static void ttyinfo_remove_fd(int fd, struct lxc_tty_info *tty_info)
 {
 	int i;
@@ -190,43 +226,24 @@ static void ttyinfo_remove_fd(int fd, st
 	return;
 }
 
-static void command_fd_cleanup(int fd, struct lxc_tty_info *tty_info,
+static void command_fd_cleanup(int fd, struct lxc_handler *handler,
 			struct lxc_epoll_descr *descr)
 {
-	ttyinfo_remove_fd(fd, tty_info);
+	ttyinfo_remove_fd(fd, &handler->tty_info);
 	lxc_mainloop_del_handler(descr, fd);
 	close(fd);
 }
 
-static int ttyclient_handler(int fd, void *data,
-			     struct lxc_epoll_descr *descr)
-{
-	struct lxc_tty_info *tty_info = data;
-
-	command_fd_cleanup(fd, tty_info, descr);
-}
-
-static int ttyservice_handler(int fd, void *data,
+static int command_handler(int fd, void *data,
 			      struct lxc_epoll_descr *descr)
 {
-	int conn, ttynum, val = 1, ret = -1;
+	int ret;
+	struct lxc_request request;
 	struct lxc_handler *handler = data;
-	struct lxc_tty_info *tty_info = &handler->tty_info;
-	
-	conn = accept(fd, NULL, 0);
-	if (conn < 0) {
-		SYSERROR("failed to accept tty client");
-		return -1;
-	}
-	
-	if (setsockopt(conn, SOL_SOCKET, SO_PASSCRED, &val, sizeof(val))) {
-		SYSERROR("failed to enable credential on socket");
-		goto out_close;
-	}
 
-	ret = lxc_af_unix_rcv_credential(conn, &ttynum, sizeof(ttynum));
+	ret = lxc_af_unix_rcv_credential(fd, &request, sizeof(request));
 	if (ret < 0) {
-		SYSERROR("failed to receive data on tty socket");
+		SYSERROR("failed to receive data on command socket");
 		goto out_close;
 	}
 
@@ -235,11 +252,32 @@ static int ttyservice_handler(int fd, vo
 		goto out_close;
 	}
 
-	if (ret != sizeof(ttynum)) {
+	if (ret != sizeof(request)) {
 		WARN("partial request, ignored");
 		goto out_close;
 	}
 
+	ret = trigger_command(fd, &request, handler, descr);
+	if (ret) {
+		/* this is not an error, but only a request to close fd */
+		ret = 0;
+		goto out_close;
+	}
+
+out:
+	return ret;
+out_close:
+	command_fd_cleanup(fd, handler, descr);
+	goto out;
+}
+
+static int ttyservice_handler(int fd, struct lxc_request *request,
+			struct lxc_handler *handler,
+			struct lxc_epoll_descr *descr)
+{
+	int ttynum = request->data;
+	struct lxc_tty_info *tty_info = &handler->tty_info;
+
 	if (ttynum > 0) {
 		if (ttynum > tty_info->nbtty)
 			goto out_close;
@@ -260,26 +298,37 @@ static int ttyservice_handler(int fd, vo
 		goto out_close;
 
 out_send:
-	if (lxc_af_unix_send_fd(conn, tty_info->pty_info[ttynum - 1].master,
+	if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master,
 				&ttynum, sizeof(ttynum)) < 0) {
 		ERROR("failed to send tty to client");
 		goto out_close;
 	}
 
-	if (lxc_mainloop_add_handler(descr, conn,
-				     ttyclient_handler, tty_info)) {
-		ERROR("failed to add tty client handler");
-		goto out_close;
-	}
+	tty_info->pty_info[ttynum - 1].busy = fd;
 
-	tty_info->pty_info[ttynum - 1].busy = conn;
+	return 0;
 
-	ret = 0;
-out:
-	return ret;
 out_close:
-	close(conn);
-	goto out;
+	/* the close fd and related cleanup will be done by caller */
+	return 1;
+}
+
+static int trigger_command(int fd, struct lxc_request *request,
+			struct lxc_handler *handler,
+			struct lxc_epoll_descr *descr)
+{
+	typedef int (*callback)(int, struct lxc_request *,
+				struct lxc_handler *,
+				struct lxc_epoll_descr *descr);
+
+	callback cb[LXC_COMMAND_MAX] = {
+		[LXC_COMMAND_TTY] = ttyservice_handler,
+	};
+
+	if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
+		return -1;
+
+	return cb[request->type](fd, request, handler, descr);
 }
 
 int lxc_poll(const char *name, struct lxc_handler *handler)
@@ -291,9 +340,8 @@ int lxc_poll(const char *name, struct lx
 	int nfds, ret = -1;
 	struct lxc_epoll_descr descr;
 
-	/* sigfd + nb tty + tty service 
-	 * if tty is enabled */
-	nfds = tty_info->nbtty + 1 + tty_info->nbtty ? 1 : 0;
+	/* nb tty + sigfd + command service */
+	nfds = tty_info->nbtty + 2;
 
 	if (lxc_mainloop_open(nfds, &descr)) {
 		ERROR("failed to create mainloop");
@@ -305,10 +353,8 @@ int lxc_poll(const char *name, struct lx
 		goto out_mainloop_open;
 	}
 
-	if (tty_info->nbtty) {
-		if (tty_mainloop_add(name, &descr, handler))
-			goto out_mainloop_open;
-	}
+	if (command_mainloop_add(name, &descr, handler))
+		goto out_mainloop_open;
 
 	ret = lxc_mainloop(&descr);
 



