The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6517
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From 880fca008dd619f6f9e0add0f42aeb00b5970187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 26 Nov 2019 19:59:36 -0500 Subject: [PATCH 1/4] shared: Cleanup console on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- shared/network.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/network.go b/shared/network.go index beb927e66c..089480b318 100644 --- a/shared/network.go +++ b/shared/network.go @@ -474,6 +474,7 @@ func WebsocketConsoleMirror(conn *websocket.Conn, w io.WriteCloser, r io.ReadClo if !ok { r.Close() logger.Debugf("sending write barrier") + conn.WriteMessage(websocket.BinaryMessage, []byte("\r")) conn.WriteMessage(websocket.TextMessage, []byte{}) readDone <- true return @@ -491,6 +492,7 @@ func WebsocketConsoleMirror(conn *websocket.Conn, w io.WriteCloser, r io.ReadClo break } } + closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "") conn.WriteMessage(websocket.CloseMessage, closeMsg) readDone <- true From 95eaf283341200b3c065ea1cb1fe9ec74454e6c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 26 Nov 2019 20:00:00 -0500 Subject: [PATCH 2/4] lxd: Cleanup console on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/container_console.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lxd/container_console.go b/lxd/container_console.go index 41440db208..791682db0d 100644 --- a/lxd/container_console.go +++ b/lxd/container_console.go @@ -222,9 +222,10 @@ func (s *consoleWs) Do(op *operations.Operation) error { // Get the console websocket and close it. s.connsLock.Lock() - consolConn := s.conns[0] + consoleConn := s.conns[0] s.connsLock.Unlock() - consolConn.Close() + consoleConn.WriteMessage(websocket.BinaryMessage, []byte("\n\r")) + consoleConn.Close() // Get the control websocket and close it. s.connsLock.Lock() From d91543582f6b3efc03ae1455311b84da1888d82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 26 Nov 2019 20:00:36 -0500 Subject: [PATCH 3/4] lxd/console: Improve disconnection handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #6512 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/container_console.go | 5 +++-- lxd/container_lxc.go | 23 ++++++++++++++++++----- lxd/instance/instance_interface.go | 2 +- lxd/vm_qemu.go | 18 ++++++++++-------- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/lxd/container_console.go b/lxd/container_console.go index 791682db0d..e63d599dc0 100644 --- a/lxd/container_console.go +++ b/lxd/container_console.go @@ -112,7 +112,7 @@ func (s *consoleWs) Do(op *operations.Operation) error { <-s.allConnected // Get console from instance. - console, err := s.instance.Console() + console, consoleDisconnectCh, err := s.instance.Console() if err != nil { return err } @@ -204,14 +204,15 @@ func (s *consoleWs) Do(op *operations.Operation) error { <-readDone logger.Debugf("Finished mirroring console to websocket") <-writeDone - conn.Close() close(mirrorDoneCh) }() // Wait until either the console or the websocket is done. select { case <-mirrorDoneCh: + close(consoleDisconnectCh) case <-consoleDoneCh: + close(consoleDisconnectCh) } // Write a reset escape sequence to the console to cancel any ongoing reads to the handle diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index 4a8a41a203..c15289ac17 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -5837,7 +5837,9 @@ func (c *containerLXC) FileRemove(path string) error { return nil } -func (c *containerLXC) Console() (*os.File, error) { +func (c *containerLXC) Console() (*os.File, chan error, error) { + chDisconnect := make(chan error, 1) + args := []string{ c.state.OS.ExecPath, "forkconsole", @@ -5849,7 +5851,7 @@ func (c *containerLXC) Console() (*os.File, error) { idmapset, err := c.CurrentIdmap() if err != nil { - return nil, err + return nil, nil, err } var rootUID, rootGID int64 @@ -5859,7 +5861,7 @@ func (c *containerLXC) Console() (*os.File, error) { master, slave, err := shared.OpenPty(rootUID, rootGID) if err != nil { - return nil, err + return nil, nil, err } cmd := exec.Cmd{} @@ -5871,10 +5873,21 @@ func (c *containerLXC) Console() (*os.File, error) { err = cmd.Start() if err != nil { - return nil, err + return nil, nil, err } - return master, nil + go func() { + err = cmd.Wait() + master.Close() + slave.Close() + }() + + go func() { + <-chDisconnect + cmd.Process.Kill() + }() + + return master, chDisconnect, nil } func (c *containerLXC) ConsoleLog(opts lxc.ConsoleLogOptions) (string, error) { diff --git a/lxd/instance/instance_interface.go b/lxd/instance/instance_interface.go index ae0c2e3daa..9c56b0ddfe 100644 --- a/lxd/instance/instance_interface.go +++ b/lxd/instance/instance_interface.go @@ -51,7 +51,7 @@ type Instance interface { FileRemove(path string) error // Console - Allocate and run a console tty. - Console() (*os.File, error) + Console() (*os.File, chan error, error) Exec(command []string, env map[string]string, stdin *os.File, stdout *os.File, stderr *os.File, cwd string, uid uint32, gid uint32) (Cmd, error) // Status diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go index c68ca7e95e..2a8101575a 100644 --- a/lxd/vm_qemu.go +++ b/lxd/vm_qemu.go @@ -2240,23 +2240,25 @@ func (vm *vmQemu) FileRemove(path string) error { return fmt.Errorf("FileRemove Not implemented") } -func (vm *vmQemu) Console() (*os.File, error) { +func (vm *vmQemu) Console() (*os.File, chan error, error) { + chError := make(chan error, 1) + // Connect to the monitor. monitor, err := qmp.NewSocketMonitor("unix", vm.getMonitorPath(), vmVsockTimeout) if err != nil { - return nil, err // The VM isn't running as no monitor socket available. + return nil, nil, err // The VM isn't running as no monitor socket available. } err = monitor.Connect() if err != nil { - return nil, err // The capabilities handshake failed. + return nil, nil, err // The capabilities handshake failed. } defer monitor.Disconnect() // Send the status command. respRaw, err := monitor.Run([]byte("{'execute': 'query-chardev'}")) if err != nil { - return nil, err // Status command failed. + return nil, nil, err // Status command failed. } var respDecoded struct { @@ -2268,7 +2270,7 @@ func (vm *vmQemu) Console() (*os.File, error) { err = json.Unmarshal(respRaw, &respDecoded) if err != nil { - return nil, err // JSON decode failed. + return nil, nil, err // JSON decode failed. } var ptsPath string @@ -2280,15 +2282,15 @@ func (vm *vmQemu) Console() (*os.File, error) { } if ptsPath == "" { - return nil, fmt.Errorf("No PTS path found") + return nil, nil, fmt.Errorf("No PTS path found") } console, err := os.OpenFile(ptsPath, os.O_RDWR, 0600) if err != nil { - return nil, err + return nil, nil, err } - return console, nil + return console, chError, nil } func (vm *vmQemu) forwardSignal(control *websocket.Conn, sig unix.Signal) error { From 3065865ac24b65e610ff195e08b739c570a433a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Tue, 26 Nov 2019 20:17:08 -0500 Subject: [PATCH 4/4] lxd/vm: Add locking around console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/vm_qemu.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go index 2a8101575a..e9002f18f9 100644 --- a/lxd/vm_qemu.go +++ b/lxd/vm_qemu.go @@ -13,6 +13,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "time" "github.com/digitalocean/go-qemu/qmp" @@ -49,6 +50,9 @@ import ( var vmVsockTimeout time.Duration = time.Second +var vmConsole = map[int]bool{} +var vmConsoleLock sync.Mutex + func vmQemuLoad(s *state.State, args db.InstanceArgs, profiles []api.Profile) (instance.Instance, error) { // Create the container struct. vm := vmQemuInstantiate(s, args) @@ -2241,7 +2245,15 @@ func (vm *vmQemu) FileRemove(path string) error { } func (vm *vmQemu) Console() (*os.File, chan error, error) { - chError := make(chan error, 1) + chDisconnect := make(chan error, 1) + + // Avoid duplicate connects. + vmConsoleLock.Lock() + if vmConsole[vm.id] { + vmConsoleLock.Unlock() + return nil, nil, fmt.Errorf("There is already an active console for this instance") + } + vmConsoleLock.Unlock() // Connect to the monitor. monitor, err := qmp.NewSocketMonitor("unix", vm.getMonitorPath(), vmVsockTimeout) @@ -2290,7 +2302,19 @@ func (vm *vmQemu) Console() (*os.File, chan error, error) { return nil, nil, err } - return console, chError, nil + vmConsoleLock.Lock() + vmConsole[vm.id] = true + vmConsoleLock.Unlock() + + go func() { + <-chDisconnect + + vmConsoleLock.Lock() + vmConsole[vm.id] = false + vmConsoleLock.Unlock() + }() + + return console, chDisconnect, nil } func (vm *vmQemu) forwardSignal(control *websocket.Conn, sig unix.Signal) error {
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel