diff --git a/cio/io_windows.go b/cio/io_windows.go index 8eb77778f..5208f3eaa 100644 --- a/cio/io_windows.go +++ b/cio/io_windows.go @@ -99,7 +99,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { }() } - if !fifos.Terminal && fifos.Stderr != "" { + if fifos.Stderr != "" { l, err := winio.ListenPipe(fifos.Stderr, nil) if err != nil { return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr) diff --git a/cmd/ctr/commands/run/run.go b/cmd/ctr/commands/run/run.go index da91429cc..935730259 100644 --- a/cmd/ctr/commands/run/run.go +++ b/cmd/ctr/commands/run/run.go @@ -135,9 +135,17 @@ var Command = cli.Command{ if context.Bool("rm") && !detach { defer container.Delete(ctx, containerd.WithSnapshotCleanup) } + var con console.Console + if tty { + con = console.Current() + defer con.Reset() + if err := con.SetRaw(); err != nil { + return err + } + } opts := getNewTaskOpts(context) ioOpts := []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))} - task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), tty, context.Bool("null-io"), ioOpts, opts...) + task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), con, context.Bool("null-io"), ioOpts, opts...) if err != nil { return err } @@ -153,14 +161,6 @@ var Command = cli.Command{ return err } } - var con console.Console - if tty { - con = console.Current() - defer con.Reset() - if err := con.SetRaw(); err != nil { - return err - } - } if err := task.Start(ctx); err != nil { return err } diff --git a/cmd/ctr/commands/tasks/start.go b/cmd/ctr/commands/tasks/start.go index 63694a2e1..089caa6f2 100644 --- a/cmd/ctr/commands/tasks/start.go +++ b/cmd/ctr/commands/tasks/start.go @@ -65,13 +65,21 @@ var startCommand = cli.Command{ if err != nil { return err } - var ( tty = spec.Process.Terminal opts = getNewTaskOpts(context) ioOpts = []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))} ) - task, err := NewTask(ctx, client, container, "", tty, context.Bool("null-io"), ioOpts, opts...) + var con console.Console + if tty { + con = console.Current() + defer con.Reset() + if err := con.SetRaw(); err != nil { + return err + } + } + + task, err := NewTask(ctx, client, container, "", con, context.Bool("null-io"), ioOpts, opts...) if err != nil { return err } @@ -86,14 +94,6 @@ var startCommand = cli.Command{ return err } - var con console.Console - if tty { - con = console.Current() - defer con.Reset() - if err := con.SetRaw(); err != nil { - return err - } - } if err := task.Start(ctx); err != nil { return err } diff --git a/cmd/ctr/commands/tasks/tasks_unix.go b/cmd/ctr/commands/tasks/tasks_unix.go index f7b111410..e10fe798d 100644 --- a/cmd/ctr/commands/tasks/tasks_unix.go +++ b/cmd/ctr/commands/tasks/tasks_unix.go @@ -67,7 +67,7 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol } // NewTask creates a new task -func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { +func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, con console.Console, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { stdio := cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...) if checkpoint != "" { im, err := client.GetImage(ctx, checkpoint) @@ -77,11 +77,11 @@ func NewTask(ctx gocontext.Context, client *containerd.Client, container contain opts = append(opts, containerd.WithTaskCheckpoint(im)) } ioCreator := stdio - if tty { - ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...) + if con != nil { + ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, nil), cio.WithTerminal}, ioOpts...)...) } if nullIO { - if tty { + if con != nil { return nil, errors.New("tty and null-io cannot be used together") } ioCreator = cio.NullIO diff --git a/cmd/ctr/commands/tasks/tasks_windows.go b/cmd/ctr/commands/tasks/tasks_windows.go index 5235467fc..f6ec5563a 100644 --- a/cmd/ctr/commands/tasks/tasks_windows.go +++ b/cmd/ctr/commands/tasks/tasks_windows.go @@ -58,13 +58,13 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol } // NewTask creates a new task -func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { +func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, con console.Console, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { var ioCreator cio.Creator - if tty { + if con != nil { if nullIO { return nil, errors.New("tty and null-io cannot be used together") } - ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...) + ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, con), cio.WithTerminal}, ioOpts...)...) } else if nullIO { ioCreator = cio.NullIO } else { diff --git a/vendor.conf b/vendor.conf index 66384c05f..b565252ee 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,5 +1,5 @@ github.com/containerd/go-runc 808e8444ac4633a8e5eb7314fc6b5d27051727dd -github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081 +github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go index ff0174df4..62dbe1c03 100644 --- a/vendor/github.com/containerd/console/console_windows.go +++ b/vendor/github.com/containerd/console/console_windows.go @@ -17,6 +17,7 @@ package console import ( + "fmt" "os" "github.com/pkg/errors" @@ -28,55 +29,90 @@ var ( ErrNotImplemented = errors.New("not implemented") ) -func (m *master) init() { - m.h = windows.Handle(m.f.Fd()) - if err := windows.GetConsoleMode(m.h, &m.mode); err == nil { - if m.f == os.Stdin { - // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. - if err = windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { - vtInputSupported = true - } - // Unconditionally set the console mode back even on failure because SetConsoleMode - // remembers invalid bits on input handles. - windows.SetConsoleMode(m.h, m.mode) - } else if err := windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { - m.mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - } else { - windows.SetConsoleMode(m.h, m.mode) +func (m *master) initStdios() { + m.in = windows.Handle(os.Stdin.Fd()) + if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil { + // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. + if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { + vtInputSupported = true } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + windows.SetConsoleMode(m.in, m.inMode) + } else { + fmt.Printf("failed to get console mode for stdin: %v\n", err) + } + + m.out = windows.Handle(os.Stdout.Fd()) + if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil { + if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.out, m.outMode) + } + } else { + fmt.Printf("failed to get console mode for stdout: %v\n", err) + } + + m.err = windows.Handle(os.Stderr.Fd()) + if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil { + if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.err, m.errMode) + } + } else { + fmt.Printf("failed to get console mode for stderr: %v\n", err) } } type master struct { - h windows.Handle - mode uint32 - f *os.File + in windows.Handle + inMode uint32 + + out windows.Handle + outMode uint32 + + err windows.Handle + errMode uint32 } func (m *master) SetRaw() error { - if m.f == os.Stdin { - if err := makeInputRaw(m.h, m.mode); err != nil { - return err - } - } else { - // Set StdOut and StdErr to raw mode, we ignore failures since - // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of - // Windows. - windows.SetConsoleMode(m.h, m.mode|windows.DISABLE_NEWLINE_AUTO_RETURN) + if err := makeInputRaw(m.in, m.inMode); err != nil { + return err } + + // Set StdOut and StdErr to raw mode, we ignore failures since + // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of + // Windows. + + windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + + windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + return nil } func (m *master) Reset() error { - if err := windows.SetConsoleMode(m.h, m.mode); err != nil { - return errors.Wrap(err, "unable to restore console mode") + for _, s := range []struct { + fd windows.Handle + mode uint32 + }{ + {m.in, m.inMode}, + {m.out, m.outMode}, + {m.err, m.errMode}, + } { + if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { + return errors.Wrap(err, "unable to restore console mode") + } } + return nil } func (m *master) Size() (WinSize, error) { var info windows.ConsoleScreenBufferInfo - err := windows.GetConsoleScreenBufferInfo(m.h, &info) + err := windows.GetConsoleScreenBufferInfo(m.out, &info) if err != nil { return WinSize{}, errors.Wrap(err, "unable to get console info") } @@ -98,11 +134,11 @@ func (m *master) ResizeFrom(c Console) error { } func (m *master) DisableEcho() error { - mode := m.mode &^ windows.ENABLE_ECHO_INPUT + mode := m.inMode &^ windows.ENABLE_ECHO_INPUT mode |= windows.ENABLE_PROCESSED_INPUT mode |= windows.ENABLE_LINE_INPUT - if err := windows.SetConsoleMode(m.h, mode); err != nil { + if err := windows.SetConsoleMode(m.in, mode); err != nil { return errors.Wrap(err, "unable to set console to disable echo") } @@ -114,15 +150,15 @@ func (m *master) Close() error { } func (m *master) Read(b []byte) (int, error) { - return m.f.Read(b) + return os.Stdin.Read(b) } func (m *master) Write(b []byte) (int, error) { - return m.f.Write(b) + return os.Stdout.Write(b) } func (m *master) Fd() uintptr { - return uintptr(m.h) + return uintptr(m.in) } // on windows, console can only be made from os.Std{in,out,err}, hence there @@ -174,7 +210,7 @@ func newMaster(f *os.File) (Console, error) { if f != os.Stdin && f != os.Stdout && f != os.Stderr { return nil, errors.New("creating a console from a file is not supported on windows") } - m := &master{f: f} - m.init() + m := &master{} + m.initStdios() return m, nil }