[lxc-devel] [lxd/master] Fix console disconnection
stgraber on Github
lxc-bot at linuxcontainers.org
Wed Nov 27 01:18:35 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191126/08524b56/attachment.bin>
-------------- next part --------------
From 880fca008dd619f6f9e0add0f42aeb00b5970187 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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 {
More information about the lxc-devel
mailing list