mirror of
https://github.com/containers/podman.git
synced 2025-05-22 01:27:07 +08:00

At present, when manually detaching from an attached container (using the detach hotkeys, default C-p C-q), Podman will still wait for the container to exit to obtain its exit code (so we can set Podman's exit code to match). This is correct in the case where attach finished because the container exited, but very wrong for the manual detach case. As a result of this, we can no longer guarantee that the cleanup and --rm functions will fire at the end of 'podman run' - we may be exiting before we get that far. Cleanup is easy enough - we swap to unconditionally using the cleanup processes we've used for detached and rootless containers all along. To duplicate --rm we need to also teach 'podman cleanup' to optionally remove containers instead of cleaning them up. (There is an argument for just using 'podman rm' instead of 'podman cleanup --rm', but cleanup does have different semantics given that we only ever expect it to run when the container has just exited. I think it might be useful to keep the two separate for things like 'podman events'...) Signed-off-by: Matthew Heon <mheon@redhat.com>
163 lines
4.0 KiB
Go
163 lines
4.0 KiB
Go
//+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 err == ErrDetach {
|
|
return err
|
|
}
|
|
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
|
|
}
|