diff --git a/go.mod b/go.mod index ede568e7..e36ee673 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad github.com/stretchr/testify v1.2.2 github.com/tcnksm/go-gitconfig v0.1.2 + golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 gopkg.in/yaml.v2 v2.2.2 // indirect ) diff --git a/pkg/commands/container.go b/pkg/commands/container.go index 5b42a5df..77d414ed 100644 --- a/pkg/commands/container.go +++ b/pkg/commands/container.go @@ -2,9 +2,14 @@ package commands import ( "context" + "golang.org/x/crypto/ssh/terminal" + "io" + "os" "os/exec" + "os/signal" "strconv" "strings" + "syscall" "time" "github.com/docker/docker/api/types" @@ -341,18 +346,80 @@ func (c *Container) Restart() error { } // Attach attaches the container -func (c *Container) Attach() (*exec.Cmd, error) { +func (c *Container) Attach() error { // verify that we can in fact attach to this container if !c.Details.Config.OpenStdin { - return nil, errors.New(c.Tr.UnattachableContainerError) + return errors.New(c.Tr.UnattachableContainerError) } if c.Container.State == "exited" { - return nil, errors.New(c.Tr.CannotAttachStoppedContainerError) + return errors.New(c.Tr.CannotAttachStoppedContainerError) } - cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID) - return cmd, nil + options := types.ContainerAttachOptions{ + Stream: true, + Stdin: c.Details.Config.OpenStdin, + Stdout: true, + Stderr: true, + } + + hijack, err := c.Client.ContainerAttach(context.Background(), c.ID, options) + if err != nil { + return err + } + + if c.Details.Config.OpenStdin { + fd := int(os.Stdin.Fd()) + + if terminal.IsTerminal(fd) { + oldState, err := terminal.MakeRaw(fd) + if err != nil { + return err + } + defer terminal.Restore(fd, oldState) + + go c.resizeIfChanged(fd) + go io.Copy(hijack.Conn, os.Stdin) + } + } + + _, err = io.Copy(os.Stdout, hijack.Conn) + if err != nil { + return err + } + + hijack.Close() + + return nil +} + +func (c *Container) resizeIfChanged(fd int) { + channel := make(chan os.Signal) + signal.Notify(channel, syscall.SIGWINCH) + + for { + <-channel + c.Resize(fd) + } +} + +func (c *Container) Resize(fd int) error { + width, height, err := terminal.GetSize(fd) + if err != nil { + return err + } + + options := types.ResizeOptions{ + Height: uint(height), + Width: uint(width), + } + + err = c.Client.ContainerResize(context.Background(), c.ID, options) + if err != nil { + return err + } + + return nil } // Top returns process information diff --git a/pkg/commands/service.go b/pkg/commands/service.go index 245e8a7c..1a628e05 100644 --- a/pkg/commands/service.go +++ b/pkg/commands/service.go @@ -56,7 +56,7 @@ func (s *Service) Restart() error { } // Attach attaches to the service -func (s *Service) Attach() (*exec.Cmd, error) { +func (s *Service) Attach() error { return s.Container.Attach() } diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index d1c61f5e..6f397c72 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -467,12 +467,13 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error { return nil } - c, err := container.Attach() + err = container.Attach() if err != nil { return gui.createErrorPanel(gui.g, err.Error()) } - gui.SubProcess = c + gui.RunWithSubprocesses() + return gui.Errors.ErrSubProcess } diff --git a/pkg/gui/services_panel.go b/pkg/gui/services_panel.go index 0c1e7a43..e471cc6d 100644 --- a/pkg/gui/services_panel.go +++ b/pkg/gui/services_panel.go @@ -256,13 +256,11 @@ func (gui *Gui) handleServiceAttach(g *gocui.Gui, v *gocui.View) error { return gui.createErrorPanel(gui.g, gui.Tr.NoContainers) } - c, err := service.Attach() - + err = service.Attach() if err != nil { return gui.createErrorPanel(gui.g, err.Error()) } - gui.SubProcess = c return gui.Errors.ErrSubProcess }