Merge pull request #2562 from crosbymichael/console
Fix windows interactive consoles
This commit is contained in:
commit
248ff06850
@ -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)
|
l, err := winio.ListenPipe(fifos.Stderr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr)
|
return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr)
|
||||||
|
@ -135,9 +135,17 @@ var Command = cli.Command{
|
|||||||
if context.Bool("rm") && !detach {
|
if context.Bool("rm") && !detach {
|
||||||
defer container.Delete(ctx, containerd.WithSnapshotCleanup)
|
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)
|
opts := getNewTaskOpts(context)
|
||||||
ioOpts := []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))}
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -153,14 +161,6 @@ var Command = cli.Command{
|
|||||||
return err
|
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 {
|
if err := task.Start(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -65,13 +65,21 @@ var startCommand = cli.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tty = spec.Process.Terminal
|
tty = spec.Process.Terminal
|
||||||
opts = getNewTaskOpts(context)
|
opts = getNewTaskOpts(context)
|
||||||
ioOpts = []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))}
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -86,14 +94,6 @@ var startCommand = cli.Command{
|
|||||||
return err
|
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 {
|
if err := task.Start(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewTask creates a new task
|
// 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...)...)
|
stdio := cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...)
|
||||||
if checkpoint != "" {
|
if checkpoint != "" {
|
||||||
im, err := client.GetImage(ctx, 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))
|
opts = append(opts, containerd.WithTaskCheckpoint(im))
|
||||||
}
|
}
|
||||||
ioCreator := stdio
|
ioCreator := stdio
|
||||||
if tty {
|
if con != nil {
|
||||||
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...)
|
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, nil), cio.WithTerminal}, ioOpts...)...)
|
||||||
}
|
}
|
||||||
if nullIO {
|
if nullIO {
|
||||||
if tty {
|
if con != nil {
|
||||||
return nil, errors.New("tty and null-io cannot be used together")
|
return nil, errors.New("tty and null-io cannot be used together")
|
||||||
}
|
}
|
||||||
ioCreator = cio.NullIO
|
ioCreator = cio.NullIO
|
||||||
|
@ -58,13 +58,13 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewTask creates a new task
|
// 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
|
var ioCreator cio.Creator
|
||||||
if tty {
|
if con != nil {
|
||||||
if nullIO {
|
if nullIO {
|
||||||
return nil, errors.New("tty and null-io cannot be used together")
|
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 {
|
} else if nullIO {
|
||||||
ioCreator = cio.NullIO
|
ioCreator = cio.NullIO
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
github.com/containerd/go-runc 808e8444ac4633a8e5eb7314fc6b5d27051727dd
|
github.com/containerd/go-runc 808e8444ac4633a8e5eb7314fc6b5d27051727dd
|
||||||
github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081
|
github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
|
||||||
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
|
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
|
||||||
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
|
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
|
||||||
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
|
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
|
||||||
|
88
vendor/github.com/containerd/console/console_windows.go
generated
vendored
88
vendor/github.com/containerd/console/console_windows.go
generated
vendored
@ -17,6 +17,7 @@
|
|||||||
package console
|
package console
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -28,55 +29,90 @@ var (
|
|||||||
ErrNotImplemented = errors.New("not implemented")
|
ErrNotImplemented = errors.New("not implemented")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *master) init() {
|
func (m *master) initStdios() {
|
||||||
m.h = windows.Handle(m.f.Fd())
|
m.in = windows.Handle(os.Stdin.Fd())
|
||||||
if err := windows.GetConsoleMode(m.h, &m.mode); err == nil {
|
if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil {
|
||||||
if m.f == os.Stdin {
|
|
||||||
// Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
|
// 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 {
|
if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil {
|
||||||
vtInputSupported = true
|
vtInputSupported = true
|
||||||
}
|
}
|
||||||
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
||||||
// remembers invalid bits on input handles.
|
// remembers invalid bits on input handles.
|
||||||
windows.SetConsoleMode(m.h, m.mode)
|
windows.SetConsoleMode(m.in, m.inMode)
|
||||||
} else if err := windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
|
|
||||||
m.mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
||||||
} else {
|
} else {
|
||||||
windows.SetConsoleMode(m.h, m.mode)
|
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 {
|
type master struct {
|
||||||
h windows.Handle
|
in windows.Handle
|
||||||
mode uint32
|
inMode uint32
|
||||||
f *os.File
|
|
||||||
|
out windows.Handle
|
||||||
|
outMode uint32
|
||||||
|
|
||||||
|
err windows.Handle
|
||||||
|
errMode uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) SetRaw() error {
|
func (m *master) SetRaw() error {
|
||||||
if m.f == os.Stdin {
|
if err := makeInputRaw(m.in, m.inMode); err != nil {
|
||||||
if err := makeInputRaw(m.h, m.mode); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Set StdOut and StdErr to raw mode, we ignore failures since
|
// 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.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of
|
||||||
// Windows.
|
// Windows.
|
||||||
windows.SetConsoleMode(m.h, m.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
|
||||||
}
|
windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||||
|
|
||||||
|
windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Reset() error {
|
func (m *master) Reset() error {
|
||||||
if err := windows.SetConsoleMode(m.h, m.mode); err != nil {
|
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 errors.Wrap(err, "unable to restore console mode")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Size() (WinSize, error) {
|
func (m *master) Size() (WinSize, error) {
|
||||||
var info windows.ConsoleScreenBufferInfo
|
var info windows.ConsoleScreenBufferInfo
|
||||||
err := windows.GetConsoleScreenBufferInfo(m.h, &info)
|
err := windows.GetConsoleScreenBufferInfo(m.out, &info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return WinSize{}, errors.Wrap(err, "unable to get console info")
|
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 {
|
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_PROCESSED_INPUT
|
||||||
mode |= windows.ENABLE_LINE_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")
|
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) {
|
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) {
|
func (m *master) Write(b []byte) (int, error) {
|
||||||
return m.f.Write(b)
|
return os.Stdout.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *master) Fd() uintptr {
|
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
|
// 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 {
|
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")
|
return nil, errors.New("creating a console from a file is not supported on windows")
|
||||||
}
|
}
|
||||||
m := &master{f: f}
|
m := &master{}
|
||||||
m.init()
|
m.initStdios()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user