|
@@ -6,12 +6,15 @@ import (
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
"os"
|
|
"os"
|
|
"path/filepath"
|
|
"path/filepath"
|
|
|
|
+ "sync"
|
|
"syscall"
|
|
"syscall"
|
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/Sirupsen/logrus"
|
|
containerd "github.com/docker/containerd/api/grpc/types"
|
|
containerd "github.com/docker/containerd/api/grpc/types"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
"github.com/docker/docker/pkg/ioutils"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
|
|
+ "github.com/tonistiigi/fifo"
|
|
"golang.org/x/net/context"
|
|
"golang.org/x/net/context"
|
|
)
|
|
)
|
|
|
|
|
|
@@ -90,22 +93,37 @@ func (ctr *container) start(checkpoint string, checkpointDir string) error {
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
- createChan := make(chan struct{})
|
|
|
|
|
|
+
|
|
|
|
+ ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
+ defer cancel()
|
|
|
|
+ ready := make(chan struct{})
|
|
|
|
+
|
|
iopipe, err := ctr.openFifos(spec.Process.Terminal)
|
|
iopipe, err := ctr.openFifos(spec.Process.Terminal)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ var stdinOnce sync.Once
|
|
|
|
+
|
|
// we need to delay stdin closure after container start or else "stdin close"
|
|
// we need to delay stdin closure after container start or else "stdin close"
|
|
// event will be rejected by containerd.
|
|
// event will be rejected by containerd.
|
|
// stdin closure happens in AttachStreams
|
|
// stdin closure happens in AttachStreams
|
|
stdin := iopipe.Stdin
|
|
stdin := iopipe.Stdin
|
|
iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error {
|
|
iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error {
|
|
- go func() {
|
|
|
|
- <-createChan
|
|
|
|
- stdin.Close()
|
|
|
|
- }()
|
|
|
|
- return nil
|
|
|
|
|
|
+ var err error
|
|
|
|
+ stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed
|
|
|
|
+ err = stdin.Close()
|
|
|
|
+ go func() {
|
|
|
|
+ select {
|
|
|
|
+ case <-ready:
|
|
|
|
+ if err := ctr.sendCloseStdin(); err != nil {
|
|
|
|
+ logrus.Warnf("failed to close stdin: %+v")
|
|
|
|
+ }
|
|
|
|
+ case <-ctx.Done():
|
|
|
|
+ }
|
|
|
|
+ }()
|
|
|
|
+ })
|
|
|
|
+ return err
|
|
})
|
|
})
|
|
|
|
|
|
r := &containerd.CreateContainerRequest{
|
|
r := &containerd.CreateContainerRequest{
|
|
@@ -124,19 +142,17 @@ func (ctr *container) start(checkpoint string, checkpointDir string) error {
|
|
ctr.client.appendContainer(ctr)
|
|
ctr.client.appendContainer(ctr)
|
|
|
|
|
|
if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
|
|
if err := ctr.client.backend.AttachStreams(ctr.containerID, *iopipe); err != nil {
|
|
- close(createChan)
|
|
|
|
ctr.closeFifos(iopipe)
|
|
ctr.closeFifos(iopipe)
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
|
|
resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
|
|
if err != nil {
|
|
if err != nil {
|
|
- close(createChan)
|
|
|
|
ctr.closeFifos(iopipe)
|
|
ctr.closeFifos(iopipe)
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
ctr.systemPid = systemPid(resp.Container)
|
|
ctr.systemPid = systemPid(resp.Container)
|
|
- close(createChan)
|
|
|
|
|
|
+ close(ready)
|
|
|
|
|
|
return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{
|
|
return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{
|
|
CommonStateInfo: CommonStateInfo{
|
|
CommonStateInfo: CommonStateInfo{
|
|
@@ -207,15 +223,15 @@ func (ctr *container) handleEvent(e *containerd.Event) error {
|
|
// discardFifos attempts to fully read the container fifos to unblock processes
|
|
// discardFifos attempts to fully read the container fifos to unblock processes
|
|
// that may be blocked on the writer side.
|
|
// that may be blocked on the writer side.
|
|
func (ctr *container) discardFifos() {
|
|
func (ctr *container) discardFifos() {
|
|
|
|
+ ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
|
|
for _, i := range []int{syscall.Stdout, syscall.Stderr} {
|
|
for _, i := range []int{syscall.Stdout, syscall.Stderr} {
|
|
- f := ctr.fifo(i)
|
|
|
|
- c := make(chan struct{})
|
|
|
|
|
|
+ f, err := fifo.OpenFifo(ctx, ctr.fifo(i), syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logrus.Warnf("error opening fifo %v for discarding: %+v", f, err)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
go func() {
|
|
go func() {
|
|
- r := openReaderFromFifo(f)
|
|
|
|
- close(c) // this channel is used to not close the writer too early, before readonly open has been called.
|
|
|
|
- io.Copy(ioutil.Discard, r)
|
|
|
|
|
|
+ io.Copy(ioutil.Discard, f)
|
|
}()
|
|
}()
|
|
- <-c
|
|
|
|
- closeReaderFifo(f) // avoid blocking permanently on open if there is no writer side
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|