summaryrefslogtreecommitdiff
path: root/libpod/container_attach_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/container_attach_linux.go')
-rw-r--r--libpod/container_attach_linux.go162
1 files changed, 162 insertions, 0 deletions
diff --git a/libpod/container_attach_linux.go b/libpod/container_attach_linux.go
new file mode 100644
index 000000000..1d6f0bd96
--- /dev/null
+++ b/libpod/container_attach_linux.go
@@ -0,0 +1,162 @@
+//+build linux
+
+package libpod
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "path/filepath"
+
+ "github.com/containers/libpod/pkg/kubeutils"
+ "github.com/containers/libpod/utils"
+ "github.com/docker/docker/pkg/term"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+//#include <sys/un.h>
+// extern int unix_path_length(){struct sockaddr_un addr; return sizeof(addr.sun_path) - 1;}
+import "C"
+
+/* Sync with stdpipe_t in conmon.c */
+const (
+ AttachPipeStdin = 1
+ AttachPipeStdout = 2
+ AttachPipeStderr = 3
+)
+
+// Attach to the given container
+// Does not check if state is appropriate
+func (c *Container) attach(streams *AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, startContainer bool) error {
+ if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput {
+ return errors.Wrapf(ErrInvalidArg, "must provide at least one stream to attach to")
+ }
+
+ // Check the validity of the provided keys first
+ var err error
+ detachKeys := []byte{}
+ if len(keys) > 0 {
+ detachKeys, err = term.ToBytes(keys)
+ if err != nil {
+ return errors.Wrapf(err, "invalid detach keys")
+ }
+ }
+
+ logrus.Debugf("Attaching to container %s", c.ID())
+
+ return c.attachContainerSocket(resize, detachKeys, streams, startContainer)
+}
+
+// attachContainerSocket connects to the container's attach socket and deals with the IO
+// TODO add a channel to allow interrupting
+func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSize, detachKeys []byte, streams *AttachStreams, startContainer bool) error {
+ kubeutils.HandleResizing(resize, func(size remotecommand.TerminalSize) {
+ controlPath := filepath.Join(c.bundlePath(), "ctl")
+ controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY, 0)
+ if err != nil {
+ logrus.Debugf("Could not open ctl file: %v", err)
+ return
+ }
+ defer controlFile.Close()
+
+ logrus.Debugf("Received a resize event: %+v", size)
+ if _, err = fmt.Fprintf(controlFile, "%d %d %d\n", 1, size.Height, size.Width); err != nil {
+ logrus.Warnf("Failed to write to control file to resize terminal: %v", err)
+ }
+ })
+
+ socketPath := c.AttachSocketPath()
+
+ maxUnixLength := int(C.unix_path_length())
+ if maxUnixLength < len(socketPath) {
+ socketPath = socketPath[0:maxUnixLength]
+ }
+
+ logrus.Debug("connecting to socket ", socketPath)
+
+ conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"})
+ if err != nil {
+ return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath)
+ }
+ defer conn.Close()
+
+ if startContainer {
+ if err := c.start(); err != nil {
+ return err
+ }
+ }
+
+ receiveStdoutError := make(chan error)
+ go func() {
+ receiveStdoutError <- redirectResponseToOutputStreams(streams.OutputStream, streams.ErrorStream, streams.AttachOutput, streams.AttachError, conn)
+ }()
+
+ stdinDone := make(chan error)
+ go func() {
+ var err error
+ if streams.AttachInput {
+ _, err = utils.CopyDetachable(conn, streams.InputStream, detachKeys)
+ conn.CloseWrite()
+ }
+ stdinDone <- err
+ }()
+
+ select {
+ case err := <-receiveStdoutError:
+ return err
+ case err := <-stdinDone:
+ if _, ok := err.(utils.DetachError); ok {
+ return nil
+ }
+ if streams.AttachOutput || streams.AttachError {
+ return <-receiveStdoutError
+ }
+ }
+ return nil
+}
+
+func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, writeOutput, writeError bool, conn io.Reader) error {
+ var err error
+ buf := make([]byte, 8192+1) /* Sync with conmon STDIO_BUF_SIZE */
+ for {
+ nr, er := conn.Read(buf)
+ if nr > 0 {
+ var dst io.Writer
+ var doWrite bool
+ switch buf[0] {
+ case AttachPipeStdout:
+ dst = outputStream
+ doWrite = writeOutput
+ case AttachPipeStderr:
+ dst = errorStream
+ doWrite = writeError
+ default:
+ logrus.Infof("Received unexpected attach type %+d", buf[0])
+ }
+
+ if doWrite {
+ nw, ew := dst.Write(buf[1:nr])
+ if ew != nil {
+ err = ew
+ break
+ }
+ if nr != nw+1 {
+ err = io.ErrShortWrite
+ break
+ }
+ }
+ }
+ if er == io.EOF {
+ break
+ }
+ if er != nil {
+ err = er
+ break
+ }
+ }
+ return err
+}