[lxc-devel] [PATCH 3/3] lxc: introduce lxc-rexec

Greg Kurz gkurz at fr.ibm.com
Wed Oct 26 15:13:28 UTC 2011


The lxc-rexec command connects to the lxc-init process on an
AF_UNIX socket to spawn commands inside the container. Signals
are forwarded from lxc-rexec to the command and the exit code
is sent back to lxc-rexec. The command also runs in its own
session with its own ctty.

Multiple commands can be attached to the container.

Caveats :
    - environment and current dir is not propagated
    - the container exits only when the last attached command dies
    - attachement between containers is possible

Signed-off-by: Greg Kurz <gkurz at fr.ibm.com>
Signed-off-by: Cedric Le Goater <clg at fr.ibm.com>
---

 configure.ac          |    1 
 doc/Makefile.am       |    1 
 doc/lxc-rexec.sgml.in |  151 ++++++++++++++++++++++
 src/lxc/Makefile.am   |    7 +
 src/lxc/arguments.h   |    3 
 src/lxc/execute.c     |   44 ++++++
 src/lxc/execute.h     |   28 ++++
 src/lxc/lxc_init.c    |  267 ++++++++++++++++++++++++++++++++++++++-
 src/lxc/lxc_rexec.c   |  337 +++++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 833 insertions(+), 6 deletions(-)
 create mode 100644 doc/lxc-rexec.sgml.in
 create mode 100644 src/lxc/execute.h
 create mode 100644 src/lxc/lxc_rexec.c

diff --git a/configure.ac b/configure.ac
index 6fa8c4a..e7c0044 100644
--- a/configure.ac
+++ b/configure.ac
@@ -107,6 +107,7 @@ AC_CONFIG_FILES([
 	doc/lxc-create.sgml
 	doc/lxc-destroy.sgml
 	doc/lxc-execute.sgml
+	doc/lxc-rexec.sgml
 	doc/lxc-start.sgml
 	doc/lxc-checkpoint.sgml
 	doc/lxc-restart.sgml
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 8530ee9..dc2b728 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -10,6 +10,7 @@ man_MANS = \
 	lxc-create.1 \
 	lxc-destroy.1 \
 	lxc-execute.1 \
+	lxc-rexec.1 \
 	lxc-start.1 \
 	lxc-stop.1 \
 	lxc-checkpoint.1 \
diff --git a/doc/lxc-rexec.sgml.in b/doc/lxc-rexec.sgml.in
new file mode 100644
index 0000000..aa3cc9d
--- /dev/null
+++ b/doc/lxc-rexec.sgml.in
@@ -0,0 +1,151 @@
+<!--
+
+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
+
+-->
+
+<!DOCTYPE refentry PUBLIC "-//Davenport//DTD DocBook V3.0//EN" [
+
+<!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml">
+<!ENTITY seealso SYSTEM "@builddir@/see_also.sgml">
+]>
+
+<refentry>
+
+  <docinfo><date>@LXC_GENERATE_DATE@</date></docinfo>
+
+  <refmeta>
+    <refentrytitle>lxc-rexec</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>lxc-rexec</refname>
+
+    <refpurpose>
+      spawn a command into a container
+    </refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>lxc-rexec <replaceable>-n name</replaceable>
+	<optional>--</optional>
+	<replaceable>command</replaceable></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      <command>lxc-rexec</command> runs the specified
+      <replaceable>command</replaceable> within the runnning container
+      <replaceable>name</replaceable>.
+    </para>
+
+    <para>
+      The <command>lxc-rexec</command> command is to be used with
+      application containers, started by
+      the <command>lxc-execute</command>
+      command. <command>lxc-execute</command> spawns a custom init
+      process <command>lxc-init</command> which is also used to exec
+      processes inside the container. The launched process is
+      reparented to <command>lxc-init</command>, as a daemon.
+    </para>
+    <para>
+      The <command>lxc-rexec</command> command forwards received
+      signals to the command running inside the container and returns
+      its exit code with it dies.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+    <variablelist>
+
+      <varlistentry>
+	<term><option>--</option></term>
+	<listitem>
+	  <para>
+	    Signal the end of options and disables further option
+	    processing. Any arguments after the -- are treated as
+	    arguments.
+	  </para>
+	  <para>
+	    This option is useful when you want to execute, with the
+	    command <command>lxc-execute</command>, a command line
+	    with its own options.
+	  </para>
+	</listitem>
+      </varlistentry>
+
+    </variablelist>
+
+  </refsect1>
+
+  &commonoptions;
+
+  <refsect1>
+    <title>Diagnostic</title>
+
+    <variablelist>
+
+      <varlistentry>
+	<term>The container does not exist</term>
+	<listitem>
+	  <para>
+	    Check that the container still running.
+	  </para>
+	</listitem>
+      </varlistentry>
+
+    </variablelist>
+
+  </refsect1>
+
+  &seealso;
+
+  <refsect1>
+    <title>Author</title>
+    <para>Daniel Lezcano <email>daniel.lezcano at free.fr</email></para>
+  </refsect1>
+
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+-->
+
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 924cf1d..4215a94 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -23,7 +23,7 @@ liblxc_so_SOURCES = \
 	commands.c commands.h \
 	start.c start.h \
 	stop.c \
-	execute.c \
+	execute.c execute.h \
 	monitor.c monitor.h \
 	console.c \
 	freezer.c \
@@ -93,7 +93,8 @@ bin_PROGRAMS = \
 	lxc-unfreeze \
 	lxc-checkpoint \
 	lxc-restart \
-	lxc-kill
+	lxc-kill \
+	lxc-rexec
 
 pkglib_PROGRAMS = \
 	lxc-init
@@ -112,6 +113,7 @@ lxc_execute_SOURCES = lxc_execute.c
 lxc_freeze_SOURCES = lxc_freeze.c
 lxc_info_SOURCES = lxc_info.c
 lxc_init_SOURCES = lxc_init.c
+lxc_init_LDADD = -lutil -lcap -llxc
 lxc_monitor_SOURCES = lxc_monitor.c
 lxc_restart_SOURCES = lxc_restart.c
 lxc_start_SOURCES = lxc_start.c
@@ -120,6 +122,7 @@ lxc_unfreeze_SOURCES = lxc_unfreeze.c
 lxc_unshare_SOURCES = lxc_unshare.c
 lxc_wait_SOURCES = lxc_wait.c
 lxc_kill_SOURCES = lxc_kill.c
+lxc_rexec_SOURCES = lxc_rexec.c
 
 install-exec-local: install-soPROGRAMS
 	mv $(DESTDIR)$(libdir)/liblxc.so $(DESTDIR)$(libdir)/liblxc.so.$(VERSION)
diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h
index 6a2ffc6..5574b52 100644
--- a/src/lxc/arguments.h
+++ b/src/lxc/arguments.h
@@ -58,6 +58,9 @@ struct lxc_arguments {
 	/* for lxc-wait */
 	char *states;
 
+	/* for lxc-init */
+	int rexec_sk;
+
 	/* remaining arguments */
 	char *const *argv;
 	int argc;
diff --git a/src/lxc/execute.c b/src/lxc/execute.c
index 5b52771..475779a 100644
--- a/src/lxc/execute.c
+++ b/src/lxc/execute.c
@@ -21,13 +21,20 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#define _GNU_SOURCE
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
 #include "lxc.h"
 #include "log.h"
 #include "start.h"
+#include "af_unix.h"
+#include "execute.h"
 
 lxc_log_define(lxc_execute, lxc_start);
 
@@ -36,8 +43,31 @@ struct execute_args {
 	int quiet;
 	char *log_file;
 	char *log_priority;
+	int rexec_sk;
 };
 
+static int create_rexec_socket(const char *name)
+{
+	char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
+	char *offset = &path[1];
+	int sk;
+
+	if (snprintf(offset, sizeof(path) - 1, LXC_REXEC_NAME, name) >=
+	    (sizeof(path) - 1)) {
+		ERROR("container name %s is too long", name);
+		return -1;
+	}
+
+	sk = lxc_af_unix_open(path, SOCK_STREAM, O_TRUNC);
+	if (sk < 0) {
+		SYSERROR("failed to create rexec server socket: %s", path);
+		return -1;
+	}
+
+	INFO("rexec server socket @%s opened on %d", offset, sk);
+	return sk;
+}
+
 static int execute_start(struct lxc_handler *handler, void* data)
 {
 	int j, i = 0;
@@ -66,6 +96,13 @@ static int execute_start(struct lxc_handler *handler, void* data)
 	}
 	if (my_args->quiet)
 		argv[i++] = "--quiet";
+	argv[i++] = "--rexec-sock";
+
+	if (asprintf(&argv[i++], "%d", my_args->rexec_sk) == -1) {
+		ERROR("not enough memory to build the lxc-init arguments");
+		return 1;
+	}
+
 	argv[i++] = "--";
 	for (j = 0; j < argc; j++)
 		argv[i++] = my_args->argv[j];
@@ -81,6 +118,9 @@ static int execute_start(struct lxc_handler *handler, void* data)
 static int execute_post_start(struct lxc_handler *handler, void* data)
 {
 	struct execute_args *my_args = data;
+
+	close(my_args->rexec_sk);
+
 	NOTICE("'%s' started with pid '%d'", my_args->argv[0], handler->pid);
 	return 0;
 }
@@ -104,5 +144,9 @@ int lxc_execute(const char *name, char *const argv[], int quiet,
 	if (lxc_check_inherited(-1))
 		return -1;
 
+	args.rexec_sk = create_rexec_socket(name);
+	if (args.rexec_sk == -1)
+		return -1;
+
 	return __lxc_start(name, conf, &execute_start_ops, &args);
 }
diff --git a/src/lxc/execute.h b/src/lxc/execute.h
new file mode 100644
index 0000000..50b4b17
--- /dev/null
+++ b/src/lxc/execute.h
@@ -0,0 +1,28 @@
+/*
+ * 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 __lxc_execute_h
+#define __lxc_execute_h
+
+#define LXC_REXEC_NAME	"/lxc/%s/rexec"
+
+#endif
diff --git a/src/lxc/lxc_init.c b/src/lxc/lxc_init.c
index 2f209d1..1811862 100644
--- a/src/lxc/lxc_init.c
+++ b/src/lxc/lxc_init.c
@@ -21,7 +21,10 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#include <utmp.h>
+#include <pty.h>
 #include <stdio.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
@@ -31,16 +34,24 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/signalfd.h>
+#include <sys/socket.h>
+#include <linux/limits.h>
 
 #include "arguments.h"
 #include "log.h"
 #include "caps.h"
 #include "error.h"
 #include "utils.h"
+#include "af_unix.h"
 #include "mainloop.h"
+#include "list.h"
 
 lxc_log_define(lxc_init, lxc);
 
+#ifndef ARG_MAX
+# define ARG_MAX (4096)
+#endif
+
 static int my_checker(const struct lxc_arguments* args)
 {
 	if (!args->argc) {
@@ -48,10 +59,32 @@ static int my_checker(const struct lxc_arguments* args)
 		return -1;
 	}
 
+	if (!args->rexec_sk == -1) {
+		lxc_error(args, "--rexec-sock is mandatory !");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+	switch (c) {
+	case 'r': {
+			int fd;
+			fd = lxc_arguments_str_to_int(args, arg);
+			if (fd < 0)
+				return -1;
+
+			args->rexec_sk = fd;
+			break;
+		}
+	}
 	return 0;
 }
 
 static const struct option my_longopts[] = {
+	{"rexec-sock", required_argument, 0, 'r'},
 	LXC_COMMON_OPTIONS
 };
 
@@ -63,9 +96,13 @@ static struct lxc_arguments my_args = {
 lxc-init execs COMMAND into this container acts as a minimal init process\n\
 \n\
 Options :\n\
-  -n, --name=NAME      NAME for name of the container\n",
+  -n, --name=NAME      NAME for name of the container\n\
+  -r, --rexec-sock=FD  server socket used by lxc-rexec(1)\n",
 	.options  = my_longopts,
+	.parser   = my_parser,
 	.checker  = my_checker,
+
+	.rexec_sk = -1
 };
 
 static pid_t child_pid;
@@ -73,11 +110,39 @@ static int shutting_down;
 static int exit_status;
 static int orphan;
 
-static int handle_child(void)
+struct lxc_rexec_context {
+	pid_t pid;
+	int ctrl_sk;	/* receives the stdios, the command line to
+			 * exec, and the signals to forward. send
+			 * the exit status. */
+	char *command;
+	int argc;
+};
+
+static struct lxc_list rexec_list;
+
+static int rexec_clear_context(struct lxc_rexec_context *context, int status)
+{
+	int ret = 0;
+
+	NOTICE("attached child <%d> is dead", context->pid);
+
+	ret = write(context->ctrl_sk, &status, sizeof(status));
+	if (ret == -1)
+		SYSERROR("failed to write child status");
+	close(context->ctrl_sk);
+	free(context);
+	return ret;
+}
+
+static int handle_child(struct lxc_epoll_descr *descr)
 {
 	for(;;) {
 		siginfo_t siginfo;
+		int status;
+		struct lxc_list *iter;
 
+	loop:
 		siginfo.si_pid = 0;
 
 		if (waitid(P_ALL, -1, &siginfo, WEXITED|WNOHANG) < 0) {
@@ -96,6 +161,24 @@ static int handle_child(void)
 		if (shutting_down)
 			alarm(1);
 
+		status = lxc_error_set_and_log_siginfo(&siginfo);
+
+		lxc_list_for_each(iter, &rexec_list) {
+			struct lxc_rexec_context *context = iter->elem;
+
+			if (siginfo.si_pid == context->pid) {
+				if (context->ctrl_sk != -1) {
+					lxc_mainloop_del_handler(descr,
+								 context->ctrl_sk);
+					rexec_clear_context(context,
+							    status);
+				}
+				lxc_list_del(iter);
+				free(iter);
+				goto loop;
+			}
+		}
+
 		/*
 		 * keep the exit code of started application
 		 * (not wrapped pid) and continue to wait for
@@ -134,8 +217,7 @@ static int handle_signal(int fd, void* data, struct lxc_epoll_descr *descr)
 		break;
 
 	case SIGCHLD:
-		return handle_child();
-		break;
+		return handle_child(descr);
 
 	default:
 		NOTICE("forwarding signal %d to child <%d>", siginfo.ssi_signo,
@@ -147,6 +229,168 @@ static int handle_signal(int fd, void* data, struct lxc_epoll_descr *descr)
 	return 0;
 }
 
+static int handle_rexec_socket(int ctrl_sk, void* data,
+			       struct lxc_epoll_descr *descr)
+{
+	struct lxc_rexec_context *context = data;
+	int sig = SIGKILL;
+
+	if (read(context->ctrl_sk, &sig, sizeof(sig)) <= 0) {
+		NOTICE("rexec disconnected. killing child <%d>", context->pid);
+		lxc_mainloop_del_handler(descr, context->ctrl_sk);
+		close(context->ctrl_sk);
+		context->ctrl_sk = -1;
+	} else
+		NOTICE("sending sig %d to child <%d>", sig, context->pid);
+
+	killpg(context->pid, sig);
+	return 0;
+}
+
+static int rexec_recv_context(struct lxc_rexec_context *context)
+{
+	int n, len;
+	char *buffer = context->command;
+
+	n = read(context->ctrl_sk, &context->argc, sizeof(context->argc));
+	if (n != sizeof(context->argc)) {
+		ERROR("failed to receive command line");
+		return -1;
+	}
+
+	n = read(context->ctrl_sk, &len, sizeof(len));
+	if (n != sizeof(len)) {
+		ERROR("failed to receive command line");
+		return -1;
+	}
+
+	if (len > ARG_MAX) {
+		ERROR("command line truncated");
+		return -1;
+	}
+
+	while (len > 0) {
+		n = read(context->ctrl_sk, buffer, len);
+		if (n < 0) {
+			ERROR("failed to receive command line");
+			return -1;
+		}
+		len    -= n;
+		buffer += n;
+	}
+	return 0;
+}
+
+static void rexec_exec_context(struct lxc_rexec_context *context)
+{
+	char *arg, **argv;
+	int i;
+	sigset_t mask;
+
+	sigemptyset(&mask);
+	sigprocmask(SIG_SETMASK, &mask, NULL);
+
+	close(context->ctrl_sk);
+
+	argv = malloc((context->argc + 1) * sizeof(char*));
+	arg = context->command;
+
+	for (i = 0; i < context->argc; i++) {
+		argv[i] = arg;
+
+		arg = index(arg, '\0');
+		if (arg)
+			arg++;
+		else
+			break;
+	}
+	argv[context->argc] = NULL;
+
+	NOTICE("about to exec '%s'", context->command);
+
+	execvp(argv[0], argv);
+	ERROR("failed to exec: '%s'", context->command);
+	exit(1);
+}
+
+static int handle_rexec_connect(int rexec_sk, void* data,
+				struct lxc_epoll_descr *descr)
+{
+	struct lxc_rexec_context *context;
+	struct lxc_list *iter;
+	int slave, master;
+
+	context = malloc(sizeof(*context) + ARG_MAX);
+	if (!context) {
+		ERROR("failed to allocate rexec context");
+		return 0;
+	}
+	context->command = (char*)&context[1];
+
+	context->ctrl_sk = accept(rexec_sk, NULL, 0);
+	if (context->ctrl_sk < 0) {
+		SYSERROR("accept");
+		goto out_free;
+	}
+
+	fcntl(context->ctrl_sk, F_SETFD, FD_CLOEXEC); /* can't fail */
+
+	if (rexec_recv_context(context))
+		goto out_close_sk;
+
+	if (openpty(&master, &slave, NULL, NULL, NULL)) {
+		ERROR("can't open new pty");
+		goto out_close_sk;
+	}
+
+	if (lxc_af_unix_send_fd(context->ctrl_sk, master, NULL, 0) < 0) {
+		ERROR("fail to send command pty");
+		goto out_close_pty;
+	}
+
+	if (lxc_mainloop_add_handler(descr, context->ctrl_sk,
+				     handle_rexec_socket, context)) {
+		ERROR("failed to install rexec client handler");
+		goto out_close_pty;
+	}
+
+	iter = malloc(sizeof(*iter));
+	if (!iter) {
+		ERROR("failed to link rexec context");
+		goto out_del_handler;
+	}
+
+	context->pid = fork();
+	if (context->pid < 0) {
+		SYSERROR("fork");
+		goto out_free_iter;
+	}
+
+	if (!context->pid) {
+		setsid();
+		close(master);
+		login_tty(slave);
+		rexec_exec_context(context);
+	}
+
+	iter->elem = context;
+	lxc_list_add(&rexec_list, iter);
+	return 0;
+
+out_free_iter:
+	free(iter);
+out_del_handler:
+	lxc_mainloop_del_handler(descr, context->ctrl_sk);
+out_close_pty:
+	close(master);
+	close(slave);
+out_close_sk:
+	close(context->ctrl_sk);
+out_free:
+	free(context);
+	return 0;
+}
+
 int main(int argc, char *argv[])
 {
 	int err = -1;
@@ -154,6 +398,8 @@ int main(int argc, char *argv[])
 	struct lxc_epoll_descr mainloop_descr;
 	int signal_fd;
 
+	lxc_list_init(&rexec_list);
+
 	if (lxc_caps_init())
 		exit(err);
 
@@ -170,6 +416,8 @@ int main(int argc, char *argv[])
 	if (lxc_caps_reset())
 		exit(err);
 
+	fcntl(my_args.rexec_sk, F_SETFD, FD_CLOEXEC); /* can't fail */
+
 	child_pid = fork();
 
 	if (child_pid < 0)
@@ -199,6 +447,11 @@ int main(int argc, char *argv[])
 	sigdelset(&mask, SIGKILL);
 	sigprocmask(SIG_SETMASK, &mask, NULL);
 
+	/* Just in case write() on a lxc-rexec socket fails, we don't want
+	 * to be killed.
+	 */
+	signal(SIGPIPE, SIG_IGN);
+
 	signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
 	if (signal_fd < 0) {
 		SYSERROR("failed to create signal fd");
@@ -216,6 +469,12 @@ int main(int argc, char *argv[])
 		exit(err);
 	}
 
+	if (lxc_mainloop_add_handler(&mainloop_descr, my_args.rexec_sk,
+				     handle_rexec_connect, NULL)) {
+		ERROR("failed to install rexec server handler");
+		exit(err);
+	}
+
 	if (lxc_mainloop(&mainloop_descr))
 		exit(err);
 
diff --git a/src/lxc/lxc_rexec.c b/src/lxc/lxc_rexec.c
new file mode 100644
index 0000000..1d261d7
--- /dev/null
+++ b/src/lxc/lxc_rexec.c
@@ -0,0 +1,337 @@
+/*
+ * 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
+ */
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>	/* AF-UNIX socket types */
+
+#include <lxc/caps.h>
+#include <lxc/log.h>
+
+#include "arguments.h"
+#include "mainloop.h"
+#include "af_unix.h"
+#include "commands.h"
+#include "execute.h"
+
+lxc_log_define(lxc_rexec_ui, lxc);
+
+static const struct option my_longopts[] = {
+	LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+	.progname = "lxc-rexec",
+	.help     = "\
+--name=NAME\n\
+\n\
+Execute the specified command - enter the container NAME\n\
+\n\
+Options :\n\
+  -n, --name=NAME   NAME for name of the container\n",
+	.options  = my_longopts,
+	.parser   = NULL,
+	.checker  = NULL,
+};
+
+static void catch_signals(void(*handler)(int))
+{
+	struct sigaction sa;
+	int i;
+
+	sa.sa_handler = handler;
+	sa.sa_flags = SA_RESTART;	/* handle EINTR */
+	sigemptyset(&sa.sa_mask);
+	for (i = 0; i < NSIG; i++) {
+		if (i == SIGILL || i == SIGSEGV || i == SIGBUS ||
+		    i == SIGSTOP || i == SIGKILL || i == SIGPIPE ||
+		    i == SIGWINCH)
+			continue;
+		sigaction(i, &sa, NULL);
+	}
+
+	signal(SIGPIPE, SIG_IGN);
+}
+
+static int send_argv(int fd, int argc, char *const argv[])
+{
+	int i, len = 0;
+
+	if (write(fd, &argc, sizeof(argc)) == -1) {
+		SYSERROR("write");
+		return -1;
+	}
+
+	for (i = 0; i < argc; i++)
+		len += strlen(argv[i]) + 1;
+
+	if (write(fd, &len, sizeof(len)) == -1) {
+		SYSERROR("write");
+		return -1;
+	}
+
+	for (i = 0; i < argc; i++) {
+		if (write(fd, argv[i], strlen(argv[i]) + 1) == -1) {
+			SYSERROR("write");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int send_shell(int fd)
+{
+	struct passwd *passwd;
+	uid_t uid = getuid();
+
+	passwd = getpwuid(uid);
+	if (!passwd) {
+		SYSERROR("getpwuid failed '%d'", uid);
+		return -1;
+	}
+	{
+		char *const args[] = {
+			passwd->pw_shell,
+			NULL
+		};
+
+		if (send_argv(fd, 1, args))
+			return -1;
+	}
+	return 0;
+}
+
+static int sock;
+static int pty_master;
+static int exit_status;
+
+static int proxy_handler(int fd, void *data, struct lxc_epoll_descr *descr)
+{
+	char buf[1024];
+	int *peer = (int *)data;
+	int r;
+
+	r = read(fd, buf, sizeof(buf));
+	if (r < 0) {
+		SYSERROR("failed to read");
+		return 1;
+	}
+	r = write(*peer, buf, r);
+
+	return 0;
+}
+
+
+static int writen(int fd, const void *buf, size_t count)
+{
+    ssize_t left = count;
+
+    while (left > 0) {
+	    ssize_t n = write(fd, buf, left);
+	    if (n == -1) {
+		    if (errno == EINTR)  continue;
+		    SYSERROR("write");
+		    return -1;
+	    }
+
+	    left -= n;
+	    buf  += n;
+    }
+
+    return count;
+}
+
+static void winsz(void)
+{
+	struct winsize wsz;
+	if (ioctl(0, TIOCGWINSZ, &wsz) == 0)
+		ioctl(pty_master, TIOCSWINSZ, &wsz);
+}
+
+static void sigwinch(int sig)
+{
+	winsz();
+}
+
+static void signal_handler(int sig)
+{
+	if (writen(sock, &sig, sizeof(sig)) != sizeof(sig))
+		ERROR("sending signal failed");
+}
+
+static int sock_handler(int fd, void *data, struct lxc_epoll_descr *descr)
+{
+	if (read(sock, &exit_status, sizeof(exit_status)) == -1) {
+		SYSERROR("read");
+		return -1;
+	}
+
+	return 1;
+}
+
+static int setup_tios(int fd, struct termios *newtios, struct termios *oldtios)
+{
+	if (!isatty(fd)) {
+		ERROR("'%d' is not a tty", fd);
+		return -1;
+	}
+
+	/* Get current termios */
+	if (tcgetattr(0, oldtios)) {
+		SYSERROR("failed to get current terminal settings");
+		return -1;
+	}
+
+	*newtios = *oldtios;
+
+	/* Remove the echo characters and signal reception, the echo
+	 * will be done below with master proxying */
+	newtios->c_iflag &= ~IGNBRK;
+	newtios->c_iflag &= BRKINT;
+	newtios->c_lflag &= ~(ECHO|ICANON|ISIG);
+	newtios->c_cc[VMIN] = 1;
+	newtios->c_cc[VTIME] = 0;
+
+	/* Set new attributes */
+	if (tcsetattr(0, TCSAFLUSH, newtios)) {
+		ERROR("failed to set new terminal settings");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int do_rcmd(int argc, char *const argv[])
+{
+	char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
+	char *offset = &path[1];
+	struct lxc_epoll_descr descr;
+	struct termios newtios, oldtios;
+
+	if (snprintf(offset, sizeof(path) - 1, LXC_REXEC_NAME, my_args.name) >=
+	    (sizeof(path) - 1)) {
+		ERROR("container name %s is too long", my_args.name);
+		return -1;
+	}
+
+	sock = lxc_af_unix_connect(path);
+	if (sock < 0)
+		return -1;
+
+	if (argc) {
+		if (send_argv(sock, argc, argv)) {
+			ERROR("fail to send argv");
+			return -1;
+		}
+	} else {
+		if (send_shell(sock)) {
+			ERROR("fail to send shell command");
+			return -1;
+		}
+	}
+
+	if (lxc_af_unix_recv_fd(sock, &pty_master, NULL, 0) < 0) {
+		ERROR("fail to receive peer tty");
+		return -1;
+	}
+
+	if (lxc_mainloop_open(&descr)) {
+		ERROR("failed to create mainloop");
+		return -1;
+	}
+
+	if (lxc_mainloop_add_handler(&descr, sock, sock_handler, NULL)) {
+		ERROR("failed to install rexec server handler");
+		return -1;
+	}
+
+	if (fcntl(0, F_GETFD) != -1) {
+		static int stdin_fd;
+
+		if (lxc_mainloop_add_handler(&descr, stdin_fd, proxy_handler,
+					     &pty_master)) {
+			ERROR("failed to install peer pty handler");
+			return -1;
+		}
+
+		if (lxc_mainloop_add_handler(&descr, pty_master, proxy_handler,
+					     &stdin_fd)) {
+			ERROR("failed to install stdin handler");
+			return -1;
+		}
+
+		setup_tios(0, &newtios, &oldtios);
+
+		signal(SIGWINCH, sigwinch);
+		winsz();
+	}
+
+	catch_signals(signal_handler);
+
+	if (lxc_mainloop(&descr)) {
+		ERROR("mainloop returned an error");
+		return -1;
+	}
+
+	if (fcntl(0, F_GETFD) != -1)
+		tcsetattr(0, TCSAFLUSH, &oldtios);
+
+	return exit_status;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret;
+
+	ret = lxc_caps_init();
+	if (ret)
+		return ret;
+
+	ret = lxc_arguments_parse(&my_args, argc, argv);
+	if (ret)
+		return ret;
+
+	ret = lxc_log_init(my_args.log_file, my_args.log_priority,
+			   my_args.progname, my_args.quiet);
+	if (ret)
+		return ret;
+
+	if (get_init_pid(my_args.name) < 0) {
+		ERROR("failed to get the init pid");
+		return -1;
+	}
+
+	return do_rcmd(my_args.argc, my_args.argv);
+}





More information about the lxc-devel mailing list