[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