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

ChangeLog:
    - v2: fix link error

Signed-off-by: Greg Kurz <gk...@fr.ibm.com>
Signed-off-by: Cedric Le Goater <c...@fr.ibm.com>
---

 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 +++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 680 insertions(+), 6 deletions(-)
 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.lezc...@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 e8cb14c..d9bdc59 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_LDFLAGS = -lutil
 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);
+}


------------------------------------------------------------------------------
RSA(R) Conference 2012
Save $700 by Nov 18
Register now
http://p.sf.net/sfu/rsa-sfdev2dev1
_______________________________________________
Lxc-devel mailing list
Lxc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/lxc-devel

Reply via email to