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 <gk...@fr.ibm.com> Signed-off-by: Cedric Le Goater <c...@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.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 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); +} ------------------------------------------------------------------------------ The demand for IT networking professionals continues to grow, and the demand for specialized networking skills is growing even more rapidly. Take a complimentary Learning@Cisco Self-Assessment and learn about Cisco certifications, training, and career opportunities. http://p.sf.net/sfu/cisco-dev2dev _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel