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>
| ... | ... |
@@ -305,6 +305,9 @@ func (d *Daemon) monitorExec(container *container.Container, execConfig *exec.Co |
| 305 | 305 |
} |
| 306 | 306 |
|
| 307 | 307 |
if execConfig.ProcessConfig.Terminal != nil {
|
| 308 |
+ if err := execConfig.WaitResize(); err != nil {
|
|
| 309 |
+ logrus.Errorf("Error waiting for resize: %v", err)
|
|
| 310 |
+ } |
|
| 308 | 311 |
if err := execConfig.ProcessConfig.Terminal.Close(); err != nil {
|
| 309 | 312 |
logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
|
| 310 | 313 |
} |
| ... | ... |
@@ -29,6 +29,9 @@ type Config struct {
|
| 29 | 29 |
|
| 30 | 30 |
// waitStart will be closed immediately after the exec is really started. |
| 31 | 31 |
waitStart chan struct{}
|
| 32 |
+ |
|
| 33 |
+ // waitResize will be closed after Resize is finished. |
|
| 34 |
+ waitResize chan struct{}
|
|
| 32 | 35 |
} |
| 33 | 36 |
|
| 34 | 37 |
// NewConfig initializes the a new exec configuration |
| ... | ... |
@@ -37,6 +40,7 @@ func NewConfig() *Config {
|
| 37 | 37 |
ID: stringid.GenerateNonCryptoID(), |
| 38 | 38 |
StreamConfig: runconfig.NewStreamConfig(), |
| 39 | 39 |
waitStart: make(chan struct{}),
|
| 40 |
+ waitResize: make(chan struct{}),
|
|
| 40 | 41 |
} |
| 41 | 42 |
} |
| 42 | 43 |
|
| ... | ... |
@@ -106,13 +110,29 @@ func (c *Config) Wait(cErr chan error) error {
|
| 106 | 106 |
return nil |
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 |
+// WaitResize waits until terminal resize finishes or time out. |
|
| 110 |
+func (c *Config) WaitResize() error {
|
|
| 111 |
+ select {
|
|
| 112 |
+ case <-c.waitResize: |
|
| 113 |
+ case <-time.After(time.Second): |
|
| 114 |
+ return fmt.Errorf("Terminal resize for exec %s time out.", c.ID)
|
|
| 115 |
+ } |
|
| 116 |
+ return nil |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 109 | 119 |
// Close closes the wait channel for the progress. |
| 110 | 120 |
func (c *Config) Close() {
|
| 111 | 121 |
close(c.waitStart) |
| 112 | 122 |
} |
| 113 | 123 |
|
| 124 |
+// CloseResize closes the wait channel for resizing terminal. |
|
| 125 |
+func (c *Config) CloseResize() {
|
|
| 126 |
+ close(c.waitResize) |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 114 | 129 |
// Resize changes the size of the terminal for the exec process. |
| 115 | 130 |
func (c *Config) Resize(h, w int) error {
|
| 131 |
+ defer c.CloseResize() |
|
| 116 | 132 |
select {
|
| 117 | 133 |
case <-c.waitStart: |
| 118 | 134 |
case <-time.After(time.Second): |