From d96556572ce4e64837c03eec1f64121a2bc8a3f8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 4 Mar 2016 19:32:00 +0100 Subject: [PATCH 1/2] lxc-attach: fix redirection - stderr So that we can do things like: lxc-attach -n a -- sh -c 'echo ERR >&2' > /dev/null There seems to be no easy way to discern when we need to write to stderr instead of stdout when we receive an event on the master fd of an allocated pty. So we're using a "trick"/"hack". We write to STDOUT_FILENO if it refers to a pty. If STDOUT_FILENO does not refer to a pty we check whether STDERR_FILENO refers to a pty and if so write to it. Signed-off-by: Christian Brauner --- src/lxc/lxc_attach.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index 348014d96..ffcd3d189 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -273,7 +273,39 @@ static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int * if (lxc_console_create(conf) < 0) return -1; ts = conf->console.tty_state; - ts->stdoutfd = STDOUT_FILENO; + /* + * We need to make sure that the ouput that is produced inside the + * container is received on the host. Suppose we want to run + * + * lxc-attach -n a -- /bin/sh -c 'hostname >&2' > /dev/null + * + * This command produces output on stderr inside the container. On the + * host we close stdout by redirecting it to /dev/null. But stderr is, + * as expected, still connected to a tty. We receive the output produced + * on stderr in the container on stderr on the host. + * + * For the command + * + * lxc-attach -n a -- /bin/sh -c 'hostname >&2' 2> /dev/null + * + * the logic is analogous but because we now have closed stderr on the + * host no output will be received. + * + * Finally, imagine a more complicated case + * + * lxc-attach -n a -- /bin/sh -c 'echo OUT; echo ERR >&2' > /tmp/out 2> /tmp/err + * + * Here, we produce output in the container on stdout and stderr. On the + * host we redirect stdout and stderr to files. Because of that stdout + * and stderr are not dup2()ed. Thus, they are not connected to a pty + * and output on stdout and stderr is redirected to the corresponding + * files as expected. + */ + if (!isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) + ts->stdoutfd = STDERR_FILENO; + else + ts->stdoutfd = STDOUT_FILENO; + conf->console.descr = &descr; /* Shift ttys to container. */ From 8d1ea537851718553358a4a9767274f893b40420 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 4 Mar 2016 21:12:31 +0100 Subject: [PATCH 2/2] tests: add lxc-test-attach-test Test if the various types of I/O redirection work with lxc-attach. Signed-off-by: Christian Brauner --- src/tests/Makefile.am | 2 + src/tests/lxc-test-lxc-attach | 135 ++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100755 src/tests/lxc-test-lxc-attach diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 462d4f278..5151dca80 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -52,6 +52,7 @@ bin_SCRIPTS = lxc-test-autostart lxc-test-cloneconfig lxc-test-createconfig if DISTRO_UBUNTU bin_SCRIPTS += \ + lxc-test-lxc-attach \ lxc-test-apparmor-mount \ lxc-test-checkpoint-restore \ lxc-test-snapdeps \ @@ -77,6 +78,7 @@ EXTRA_DIST = \ list.c \ locktests.c \ lxcpath.c \ + lxc-test-lxc-attach \ lxc-test-autostart \ lxc-test-apparmor-mount \ lxc-test-checkpoint-restore \ diff --git a/src/tests/lxc-test-lxc-attach b/src/tests/lxc-test-lxc-attach new file mode 100755 index 000000000..6a5317670 --- /dev/null +++ b/src/tests/lxc-test-lxc-attach @@ -0,0 +1,135 @@ +#!/bin/sh + +# lxc: linux Container library + +# Authors: +# Christian Brauner +# +# This is a test script for the lxc-attach program. It tests whether I/O +# redirection works correctly. + +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +set -e + +FAIL() { + echo -n "Failed " >&2 + echo "$*" >&2 + lxc-destroy -n busy -f + exit 1 +} + +# Create a container, start it and wait for it to be in running state +lxc-create -t busybox -n busy || FAIL "creating busybox container" +lxc-start -n busy -d || FAIL "starting busybox container" +lxc-wait -n busy -s RUNNING || FAIL "waiting for busybox container to run" + +# Test if simple attach is working +# - stdin --> attached to pty +# - stdout --> attached to pty +# - stderr --> attached to pty +attach=$(lxc-attach -n busy -- hostname) # expect output +if [ "$attach" != "busy" ]; then + FAIL " simple attach" +fi + +# Test if we still receive output when stdin does not refer to a tty +# - stdin --> /dev/null +# - stdout --> attached to pty +# - stderr --> attached to pty +attach=$(lxc-attach -n busy -- hostname < /dev/null) # expect output +if [ "$attach" != "busy" ]; then + FAIL " with stdin redirection < /dev/null" +fi + +# Test if we are silent when stdout does not refer to a tty +# - stdin --> attached to pty +# - stdout --> /dev/null +# - stderr --> attached to pty +attach=$(lxc-attach -n busy -- hostname > /dev/null) # expect no output +if [ -n "$attach" ]; then + FAIL " with stdout redirection > /dev/null" +fi + +# Test if we are silent when stdout and stdin do not refer to a tty +# - stdin --> /dev/null +# - stdout --> /dev/null +# - stderr --> attached to pty +attach=$(lxc-attach -n busy -- hostname > /dev/null < /dev/null) # expect no output +if [ -n "$attach" ]; then + FAIL " with stdin and stdout redirection > /dev/null < /dev/null" +fi + +# Test if we still receive output when stdin and stderr do not refer to a tty +# - stdin --> /dev/null +# - stdout --> attached to pty +# - stderr --> /dev/null +attach=$(lxc-attach -n busy -- hostname 2> /dev/null < /dev/null) # expect output +if [ "$attach" != "busy" ]; then + FAIL " with stdin and stderr redirection 2> /dev/null < /dev/null" +fi + +# - produce output on stderr in container +# - redirect stdout on host to /dev/null +# - output on host should be received on stderr +# - to capture the ouput on stderr on the host we redirect stderr on the host to +# stdout +# - stdin --> attached to pty +# - stdout --> /dev/null +# - stderr --> attached to pty +attach=$( ( lxc-attach -n busy -- sh -c 'hostname >&2' > /dev/null ) 2>&1 ) +if [ "$attach" != "busy" ]; then + FAIL " bla sh -c 'hostname >&2' >/dev/null" +fi + +# - produce output on stderr in container +# - redirect stderr on host to /dev/null +# - no output from container should be received on stderr on host +# - stdin --> attached to pty +# - stdout --> attached to pty +# - stderr --> /dev/null +attach=$( ( lxc-attach -n busy -- sh -c 'hostname >&2' 2> /dev/null ) 2>&1 ) +if [ -n "$attach" ]; then + FAIL " bla sh -c 'hostname >&2' 2> /dev/null" +fi + +# Test file redirection +# - stdin --> attached to pty +# - stdout --> /dev/null +# - stderr --> /dev/null +out=$(mktemp /tmp/out_XXXX) +err=$(mktemp /tmp/err_XXXX) +lxc-attach -n busy -- sh -c 'echo OUT; echo ERR >&2' > $out 2> $err +outcontent=$(cat $out) +errcontent=$(cat $err) +if [ "$outcontent" != "OUT" ] || [ "$errcontent" != "ERR" ]; then + FAIL " bla sh -c 'echo OUT; echo ERR >&2' > $out 2> $err" +fi + +# Test stdin input +# - stdin --> $in +# - stdout --> attached to pty +# - stderr --> attached to pty +in=$(mktemp /tmp/in_XXXX) +echo '#!/bin/sh' > $in +echo hostname >> $in +attach=$(lxc-attach -n busy -- < $in) +if [ "$attach" != "busy" ]; then + FAIL " < $in" +fi + +lxc-destroy -n busy -f + +exit 0