Add tty support for setnsProcess

Signed-off-by: Alexander Morozov <lk4d4@docker.com>
This commit is contained in:
Alexander Morozov 2015-03-04 16:04:20 -08:00
parent 1c9de5b4d2
commit a9644c209f
3 changed files with 113 additions and 3 deletions

View File

@ -175,6 +175,11 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()), fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()),
"_LIBCONTAINER_INITTYPE=setns", "_LIBCONTAINER_INITTYPE=setns",
) )
if p.consolePath != "" {
cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath)
}
// TODO: set on container for process management // TODO: set on container for process management
return &setnsProcess{ return &setnsProcess{
cmd: cmd, cmd: cmd,

View File

@ -1,9 +1,12 @@
package integration package integration
import ( import (
"bytes"
"io"
"os" "os"
"strings" "strings"
"testing" "testing"
"time"
"github.com/docker/libcontainer" "github.com/docker/libcontainer"
) )
@ -173,3 +176,72 @@ func TestExecInError(t *testing.T) {
t.Fatalf("Should be error about not found executable, got %s", err) t.Fatalf("Should be error about not found executable, got %s", err)
} }
} }
func TestExecInTTY(t *testing.T) {
if testing.Short() {
return
}
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
defer remove(rootfs)
config := newTemplateConfig(rootfs)
container, err := newContainer(config)
if err != nil {
t.Fatal(err)
}
defer container.Destroy()
// Execute a first process in the container
stdinR, stdinW, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
process := &libcontainer.Process{
Args: []string{"cat"},
Env: standardEnvironment,
Stdin: stdinR,
}
err = container.Start(process)
stdinR.Close()
defer stdinW.Close()
if err != nil {
t.Fatal(err)
}
var stdout bytes.Buffer
ps := &libcontainer.Process{
Args: []string{"ps"},
Env: standardEnvironment,
}
console, err := ps.NewConsole(0)
copy := make(chan struct{})
go func() {
io.Copy(&stdout, console)
close(copy)
}()
if err != nil {
t.Fatal(err)
}
err = container.Start(ps)
if err != nil {
t.Fatal(err)
}
select {
case <-time.After(5 * time.Second):
t.Fatal("Waiting for copy timed out")
case <-copy:
}
if _, err := ps.Wait(); err != nil {
t.Fatal(err)
}
stdinW.Close()
if _, err := process.Wait(); err != nil {
t.Log(err)
}
out := stdout.String()
if !strings.Contains(out, "cat") || !strings.Contains(string(out), "ps") {
t.Fatalf("unexpected running process, output %q", out)
}
}

View File

@ -10,6 +10,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <setjmp.h> #include <setjmp.h>
@ -27,14 +28,14 @@ struct clone_arg {
jmp_buf *env; jmp_buf *env;
}; };
#define pr_perror(fmt, ...) fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__)
static int child_func(void *_arg) static int child_func(void *_arg)
{ {
struct clone_arg *arg = (struct clone_arg *)_arg; struct clone_arg *arg = (struct clone_arg *)_arg;
longjmp(*arg->env, 1); longjmp(*arg->env, 1);
} }
#define pr_perror(fmt, ...) fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__)
// Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12) // Use raw setns syscall for versions of glibc that don't include it (namely glibc-2.12)
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14 #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 14
#define _GNU_SOURCE #define _GNU_SOURCE
@ -65,8 +66,9 @@ void nsexec()
const int num = sizeof(namespaces) / sizeof(char *); const int num = sizeof(namespaces) / sizeof(char *);
jmp_buf env; jmp_buf env;
char buf[PATH_MAX], *val; char buf[PATH_MAX], *val;
int i, tfd, child, len; int i, tfd, child, len, consolefd = -1;
pid_t pid; pid_t pid;
char *console;
val = getenv("_LIBCONTAINER_INITPID"); val = getenv("_LIBCONTAINER_INITPID");
if (val == NULL) if (val == NULL)
@ -79,6 +81,15 @@ void nsexec()
exit(1); exit(1);
} }
console = getenv("_LIBCONTAINER_CONSOLE_PATH");
if (console != NULL) {
consolefd = open(console, O_RDWR);
if (consolefd < 0) {
pr_perror("Failed to open console %s", console);
exit(1);
}
}
/* Check that the specified process exists */ /* Check that the specified process exists */
snprintf(buf, PATH_MAX - 1, "/proc/%d/ns", pid); snprintf(buf, PATH_MAX - 1, "/proc/%d/ns", pid);
tfd = open(buf, O_DIRECTORY | O_RDONLY); tfd = open(buf, O_DIRECTORY | O_RDONLY);
@ -113,6 +124,28 @@ void nsexec()
} }
if (setjmp(env) == 1) { if (setjmp(env) == 1) {
if (setsid() == -1) {
pr_perror("setsid failed");
exit(1);
}
if (consolefd != -1) {
if (ioctl(consolefd, TIOCSCTTY, 0) == -1) {
pr_perror("ioctl TIOCSCTTY failed");
exit(1);
}
if (dup2(consolefd, STDIN_FILENO) != STDIN_FILENO) {
pr_perror("Failed to dup 0");
exit(1);
}
if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
pr_perror("Failed to dup 1");
exit(1);
}
if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
pr_perror("Failed to dup 2");
exit(1);
}
}
// Finish executing, let the Go runtime take over. // Finish executing, let the Go runtime take over.
return; return;
} }