浏览代码

Fix race condition with exec and resize

When I use `docker exec -ti test ls`, I got error:
```
ERRO[0035] Handler for POST /v1.23/exec/9677ecd7aa9de96f8e9e667519ff266ad26a5be80e80021a997fff6084ed6d75/resize returned error: bad file descriptor
```

It's because `POST /exec/<id>/start` and
`POST /exec/<id>/resize` are asynchronous, it is
possible that exec process finishes and ternimal
is closed before resize. Then `console.Fd()` will
get a large invalid number and we got the above
error.

Fix it by adding synchronization between exec and
resize.

Signed-off-by: Qiang Huang <h.huangqiang@huawei.com>
Qiang Huang 9 年之前
父节点
当前提交
dc56a76bc9
共有 2 个文件被更改,包括 23 次插入0 次删除
  1. 3 0
      daemon/exec.go
  2. 20 0
      daemon/exec/exec.go

+ 3 - 0
daemon/exec.go

@@ -305,6 +305,9 @@ func (d *Daemon) monitorExec(container *container.Container, execConfig *exec.Co
 	}
 	}
 
 
 	if execConfig.ProcessConfig.Terminal != nil {
 	if execConfig.ProcessConfig.Terminal != nil {
+		if err := execConfig.WaitResize(); err != nil {
+			logrus.Errorf("Error waiting for resize: %v", err)
+		}
 		if err := execConfig.ProcessConfig.Terminal.Close(); err != nil {
 		if err := execConfig.ProcessConfig.Terminal.Close(); err != nil {
 			logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
 			logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
 		}
 		}

+ 20 - 0
daemon/exec/exec.go

@@ -29,6 +29,9 @@ type Config struct {
 
 
 	// waitStart will be closed immediately after the exec is really started.
 	// waitStart will be closed immediately after the exec is really started.
 	waitStart chan struct{}
 	waitStart chan struct{}
+
+	// waitResize will be closed after Resize is finished.
+	waitResize chan struct{}
 }
 }
 
 
 // NewConfig initializes the a new exec configuration
 // NewConfig initializes the a new exec configuration
@@ -37,6 +40,7 @@ func NewConfig() *Config {
 		ID:           stringid.GenerateNonCryptoID(),
 		ID:           stringid.GenerateNonCryptoID(),
 		StreamConfig: runconfig.NewStreamConfig(),
 		StreamConfig: runconfig.NewStreamConfig(),
 		waitStart:    make(chan struct{}),
 		waitStart:    make(chan struct{}),
+		waitResize:   make(chan struct{}),
 	}
 	}
 }
 }
 
 
@@ -106,13 +110,29 @@ func (c *Config) Wait(cErr chan error) error {
 	return nil
 	return nil
 }
 }
 
 
+// WaitResize waits until terminal resize finishes or time out.
+func (c *Config) WaitResize() error {
+	select {
+	case <-c.waitResize:
+	case <-time.After(time.Second):
+		return fmt.Errorf("Terminal resize for exec %s time out.", c.ID)
+	}
+	return nil
+}
+
 // Close closes the wait channel for the progress.
 // Close closes the wait channel for the progress.
 func (c *Config) Close() {
 func (c *Config) Close() {
 	close(c.waitStart)
 	close(c.waitStart)
 }
 }
 
 
+// CloseResize closes the wait channel for resizing terminal.
+func (c *Config) CloseResize() {
+	close(c.waitResize)
+}
+
 // Resize changes the size of the terminal for the exec process.
 // Resize changes the size of the terminal for the exec process.
 func (c *Config) Resize(h, w int) error {
 func (c *Config) Resize(h, w int) error {
+	defer c.CloseResize()
 	select {
 	select {
 	case <-c.waitStart:
 	case <-c.waitStart:
 	case <-time.After(time.Second):
 	case <-time.After(time.Second):