From b60682c426eedecb6891f0888a3f6e60969928f6 Mon Sep 17 00:00:00 2001 From: frank yang Date: Sun, 30 Jul 2017 20:47:21 +0800 Subject: [PATCH] Fixes #520 #527 only assign pty path to one process named runv-shim whose lifecyle is same as the terminal. After did a lot of tests in my environment, I found that if multiple processes have opened the same pty, docker-containerd-shim wonn't get the close event of pty and will get stucked on waiting streem terminate forever. Because runv and runv-containerd don't have a controlling tty, when they opening the pty, it will become their controlling tty automatically. that's why it complained about "operation not permitted" in case which mentioned in bug #520. AFAIK a pty can only become controlling tty of one process. Signed-off-by: frank yang --- create.go | 20 +++++++++++++------- exec.go | 2 +- shim.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/create.go b/create.go index d7224322..d9d69d7f 100644 --- a/create.go +++ b/create.go @@ -16,6 +16,7 @@ import ( "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" netcontext "golang.org/x/net/context" + "golang.org/x/sys/unix" ) var createCommand = cli.Command{ @@ -245,7 +246,7 @@ func createContainer(context *cli.Context, container, namespace string, config * return err } - return ociCreate(context, container, "init", func(stdin, stdout, stderr string) error { + return ociCreate(context, container, "init", namespace, func(stdin, stdout, stderr string) error { r := &types.CreateContainerRequest{ Id: container, Runtime: "runv-create", @@ -269,7 +270,7 @@ func createContainer(context *cli.Context, container, namespace string, config * } -func ociCreate(context *cli.Context, container, process string, createFunc func(stdin, stdout, stderr string) error) error { +func ociCreate(context *cli.Context, container, process, namespace string, createFunc func(stdin, stdout, stderr string) error) error { path, err := osext.Executable() if err != nil { return fmt.Errorf("cannot find self executable path for %s: %v\n", os.Args[0], err) @@ -298,10 +299,15 @@ func ociCreate(context *cli.Context, container, process string, createFunc func( stdout = fmt.Sprintf("/proc/%d/fd/1", pid) stderr = fmt.Sprintf("/proc/%d/fd/2", pid) } else { - defer tty.Close() - stdin = tty.Name() - stdout = tty.Name() - stderr = tty.Name() + streamDir := filepath.Join(namespace, container) + if _, ex := os.Stat(streamDir); ex != nil && os.IsNotExist(ex) { + os.MkdirAll(streamDir, 0755) + } + + stdin = fmt.Sprintf("%s/%s-0", streamDir, process) + unix.Mkfifo(stdin, 0) + stdout = fmt.Sprintf("%s/%s-1", streamDir, process) + unix.Mkfifo(stdout, 0) } err = createFunc(stdin, stdout, stderr) if err != nil { @@ -324,7 +330,7 @@ func ociCreate(context *cli.Context, container, process string, createFunc func( args = append(args, "--proxy-exit-code", "--proxy-signal") } if tty != nil { - args = append(args, "--proxy-winsize") + args = append(args, "--proxy-winsize", "--input-pipe", stdin, "--output-pipe", stdout) } cmd = &exec.Cmd{ Path: path, diff --git a/exec.go b/exec.go index a4f3ab48..831766f5 100644 --- a/exec.go +++ b/exec.go @@ -208,7 +208,7 @@ func runProcess(context *cli.Context, container string, config *specs.Process) ( monitorTtySize(c, container, process) } - err = ociCreate(context, container, process, func(stdin, stdout, stderr string) error { + err = ociCreate(context, container, process, filepath.Join(context.GlobalString("root"), container, "namespace"), func(stdin, stdout, stderr string) error { p := &types.AddProcessRequest{ Id: container, Pid: process, diff --git a/shim.go b/shim.go index 6f7ce13d..4d930a5e 100644 --- a/shim.go +++ b/shim.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io" "os" "os/signal" "path/filepath" @@ -33,11 +34,46 @@ var shimCommand = cli.Command{ cli.BoolFlag{ Name: "proxy-winsize", }, + cli.StringFlag{ + Name: "input-pipe", + }, + cli.StringFlag{ + Name: "output-pipe", + }, }, Action: func(context *cli.Context) error { root := context.GlobalString("root") container := context.String("container") process := context.String("process") + + var stdinStream, stdoutStream io.ReadWriteCloser + var err error + stdinPath := context.String("input-pipe") + stdoutPath := context.String("output-pipe") + if context.Bool("proxy-winsize") { + stdinStream, err = os.OpenFile(stdinPath, syscall.O_WRONLY, 0) + if err != nil { + return err + } + + stdoutStream, err = os.OpenFile(stdoutPath, syscall.O_RDONLY, 0) + if err != nil { + return err + } + } + + if stdinStream != nil { + go func() { + io.Copy(stdinStream, os.Stdin) + }() + } + + if stdoutStream != nil { + go func() { + io.Copy(os.Stdout, stdoutStream) + }() + } + c, err := getClient(filepath.Join(root, container, "namespace", "namespaced.sock")) if err != nil { return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1)