The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6745

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) ===
Adds `WindowResize` to `instance.Cmd` interface and then moves both containerLXC and qemu logic into their separate implementations.
From 254bed8b320f9bb774c0b3b7742cdfc2673b9aa7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 20 Jan 2020 14:47:17 +0000
Subject: [PATCH 01/12] lxd/container/exec: Removes duplication of env map now
 its being stored back into post data

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_exec.go | 44 +++++++++++++++++++------------------------
 1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 8bef4a1d4b..8107478c8f 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -391,52 +391,46 @@ func containerExecPost(d *Daemon, r *http.Request) 
response.Response {
        }
 
        // Process environment.
-       env := map[string]string{}
-       for k, v := range inst.ExpandedConfig() {
-               if strings.HasPrefix(k, "environment.") {
-                       env[strings.TrimPrefix(k, "environment.")] = v
-               }
+       if post.Environment == nil {
+               post.Environment = map[string]string{}
        }
 
-       if post.Environment != nil {
-               for k, v := range post.Environment {
-                       env[k] = v
+       for k, v := range inst.ExpandedConfig() {
+               if strings.HasPrefix(k, "environment.") {
+                       post.Environment[strings.TrimPrefix(k, "environment.")] 
= v
                }
        }
 
-       // Set default value for PATH
-       _, ok := env["PATH"]
+       // Set default value for PATH.
+       _, ok := post.Environment["PATH"]
        if !ok {
-               env["PATH"] = 
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+               post.Environment["PATH"] = 
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
                if inst.FileExists("/snap") == nil {
-                       env["PATH"] = fmt.Sprintf("%s:/snap/bin", env["PATH"])
+                       post.Environment["PATH"] = fmt.Sprintf("%s:/snap/bin", 
post.Environment["PATH"])
                }
        }
 
-       // If running as root, set some env variables
+       // If running as root, set some env variables.
        if post.User == 0 {
-               // Set default value for HOME
-               _, ok = env["HOME"]
+               // Set default value for HOME.
+               _, ok = post.Environment["HOME"]
                if !ok {
-                       env["HOME"] = "/root"
+                       post.Environment["HOME"] = "/root"
                }
 
-               // Set default value for USER
-               _, ok = env["USER"]
+               // Set default value for USER.
+               _, ok = post.Environment["USER"]
                if !ok {
-                       env["USER"] = "root"
+                       post.Environment["USER"] = "root"
                }
        }
 
-       // Set default value for LANG
-       _, ok = env["LANG"]
+       // Set default value for LANG.
+       _, ok = post.Environment["LANG"]
        if !ok {
-               env["LANG"] = "C.UTF-8"
+               post.Environment["LANG"] = "C.UTF-8"
        }
 
-       // Apply to request.
-       post.Environment = env
-
        if post.WaitForWS {
                ws := &execWs{}
                ws.fds = map[int]string{}

From 540ee8a891d8bdd2e36bbc3369e4d18018535d28 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 10:02:26 +0000
Subject: [PATCH 02/12] Revert "lxd/exec: Forward control messages"

This reverts commit 86c70c80d5f6a0665087c54dd7552361efc77f33.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_exec.go                   | 18 ++-----
 lxd/container_lxc.go                    | 13 +++--
 lxd/instance/drivers/driver_qemu.go     | 72 ++++++++++++++++++-------
 lxd/instance/drivers/driver_qemu_cmd.go | 16 +++---
 lxd/instance/instance_interface.go      |  4 +-
 5 files changed, 72 insertions(+), 51 deletions(-)

diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 8107478c8f..40c59bbd1b 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -40,7 +40,6 @@ type execWs struct {
        allConnected     chan bool
        controlConnected chan bool
        fds              map[int]string
-       remoteControl    *websocket.Conn
 }
 
 func (s *execWs) Metadata() interface{} {
@@ -163,16 +162,6 @@ func (s *execWs) Do(op *operations.Operation) error {
                                return
                        }
 
-                       // Handle cases where the instance provides us a 
control websocket.
-                       if s.remoteControl != nil {
-                               s.connsLock.Lock()
-                               conn := s.conns[-1]
-                               s.connsLock.Unlock()
-
-                               <-shared.WebsocketProxy(conn, s.remoteControl)
-                               return
-                       }
-
                        logger.Debugf("Interactive child process handler 
started for child PID %d", attachedChild.PID())
                        for {
                                s.connsLock.Lock()
@@ -321,7 +310,7 @@ func (s *execWs) Do(op *operations.Operation) error {
                return cmdErr
        }
 
-       cmd, wsControl, err := s.instance.Exec(s.req, stdin, stdout, stderr)
+       cmd, err := s.instance.Exec(s.req, stdin, stdout, stderr)
        if err != nil {
                return err
        }
@@ -329,7 +318,6 @@ func (s *execWs) Do(op *operations.Operation) error {
        if s.req.Interactive {
                // Start the interactive process handler.
                attachedChildIsBorn <- cmd
-               s.remoteControl = wsControl
        }
 
        exitCode, err := cmd.Wait()
@@ -495,7 +483,7 @@ func containerExecPost(d *Daemon, r *http.Request) 
response.Response {
                        defer stderr.Close()
 
                        // Run the command
-                       cmd, _, err := inst.Exec(post, nil, stdout, stderr)
+                       cmd, err := inst.Exec(post, nil, stdout, stderr)
                        if err != nil {
                                return err
                        }
@@ -512,7 +500,7 @@ func containerExecPost(d *Daemon, r *http.Request) 
response.Response {
                                "2": fmt.Sprintf("/%s/containers/%s/logs/%s", 
version.APIVersion, inst.Name(), filepath.Base(stderr.Name())),
                        }
                } else {
-                       cmd, _, err := inst.Exec(post, nil, nil, nil)
+                       cmd, err := inst.Exec(post, nil, nil, nil)
                        if err != nil {
                                return err
                        }
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 4e8f3eb3ba..895ca69ee9 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -19,7 +19,6 @@ import (
        "time"
 
        "github.com/flosch/pongo2"
-       "github.com/gorilla/websocket"
        "github.com/pkg/errors"
        "golang.org/x/sys/unix"
        lxc "gopkg.in/lxc/go-lxc.v2"
@@ -5687,7 +5686,7 @@ func (c *containerLXC) ConsoleLog(opts 
lxc.ConsoleLogOptions) (string, error) {
        return string(msg), nil
 }
 
-func (c *containerLXC) Exec(req api.InstanceExecPost, stdin *os.File, stdout 
*os.File, stderr *os.File) (instance.Cmd, *websocket.Conn, error) {
+func (c *containerLXC) Exec(req api.InstanceExecPost, stdin *os.File, stdout 
*os.File, stderr *os.File) (instance.Cmd, error) {
        // Prepare the environment
        envSlice := []string{}
 
@@ -5699,7 +5698,7 @@ func (c *containerLXC) Exec(req api.InstanceExecPost, 
stdin *os.File, stdout *os
        logPath := filepath.Join(c.LogPath(), "forkexec.log")
        logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 
0644)
        if err != nil {
-               return nil, nil, err
+               return nil, err
        }
 
        // Prepare the subcommand
@@ -5752,21 +5751,21 @@ func (c *containerLXC) Exec(req api.InstanceExecPost, 
stdin *os.File, stdout *os
        rStatus, wStatus, err := shared.Pipe()
        defer rStatus.Close()
        if err != nil {
-               return nil, nil, err
+               return nil, err
        }
 
        cmd.ExtraFiles = []*os.File{stdin, stdout, stderr, wStatus}
        err = cmd.Start()
        if err != nil {
                wStatus.Close()
-               return nil, nil, err
+               return nil, err
        }
        wStatus.Close()
 
        attachedPid := -1
        if err := json.NewDecoder(rStatus).Decode(&attachedPid); err != nil {
                logger.Errorf("Failed to retrieve PID of executing child 
process: %s", err)
-               return nil, nil, err
+               return nil, err
        }
 
        instCmd := &ContainerLXCCmd{
@@ -5774,7 +5773,7 @@ func (c *containerLXC) Exec(req api.InstanceExecPost, 
stdin *os.File, stdout *os
                attachedChildPid: attachedPid,
        }
 
-       return instCmd, nil, nil
+       return instCmd, nil
 }
 
 func (c *containerLXC) cpuState() api.InstanceStateCPU {
diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index c20d3f784b..6bdd96f5f6 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -2,6 +2,7 @@ package drivers
 
 import (
        "bytes"
+       "encoding/json"
        "fmt"
        "io"
        "io/ioutil"
@@ -2783,8 +2784,30 @@ func (vm *qemu) Console() (*os.File, chan error, error) {
        return console, chDisconnect, nil
 }
 
+func (vm *qemu) forwardSignal(control *websocket.Conn, sig unix.Signal) error {
+       logger.Debugf("Forwarding signal to lxd-agent: %s", sig)
+
+       w, err := control.NextWriter(websocket.TextMessage)
+       if err != nil {
+               return err
+       }
+
+       msg := api.InstanceExecControl{}
+       msg.Command = "signal"
+       msg.Signal = int(sig)
+
+       buf, err := json.Marshal(msg)
+       if err != nil {
+               return err
+       }
+       _, err = w.Write(buf)
+
+       w.Close()
+       return err
+}
+
 // Exec a command inside the instance.
-func (vm *qemu) Exec(req api.InstanceExecPost, stdin *os.File, stdout 
*os.File, stderr *os.File) (instance.Cmd, *websocket.Conn, error) {
+func (vm *qemu) Exec(req api.InstanceExecPost, stdin *os.File, stdout 
*os.File, stderr *os.File) (instance.Cmd, error) {
        var instCmd *Cmd
 
        // Because this function will exit before the remote command has 
finished, we create a
@@ -2809,13 +2832,13 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
 
        client, err := vm.getAgentClient()
        if err != nil {
-               return nil, nil, err
+               return nil, err
        }
 
        agent, err := lxdClient.ConnectLXDHTTP(nil, client)
        if err != nil {
                logger.Errorf("Failed to connect to lxd-agent on %s: %v", 
vm.Name(), err)
-               return nil, nil, fmt.Errorf("Failed to connect to lxd-agent")
+               return nil, fmt.Errorf("Failed to connect to lxd-agent")
        }
        cleanupFuncs = append(cleanupFuncs, agent.Disconnect)
 
@@ -2824,7 +2847,7 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
                // Set console to raw.
                oldttystate, err := termios.MakeRaw(int(stdin.Fd()))
                if err != nil {
-                       return nil, nil, err
+                       return nil, err
                }
                cleanupFuncs = append(cleanupFuncs, func() {
                        termios.Restore(int(stdin.Fd()), oldttystate)
@@ -2832,13 +2855,24 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
        }
 
        dataDone := make(chan bool)
-
-       // Retrieve the raw control websocket and pass it to the generic exec 
handler.
-       var wsControl *websocket.Conn
-       chControl := make(chan struct{})
-       controlHander := func(conn *websocket.Conn) {
-               wsControl = conn
-               close(chControl)
+       signalSendCh := make(chan unix.Signal)
+       signalResCh := make(chan error)
+
+       // This is the signal control handler, it receives signals from lxc CLI 
and forwards them
+       // to the VM agent.
+       controlHander := func(control *websocket.Conn) {
+               closeMsg := 
websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")
+               defer control.WriteMessage(websocket.CloseMessage, closeMsg)
+
+               for {
+                       select {
+                       case signal := <-signalSendCh:
+                               err := vm.forwardSignal(control, signal)
+                               signalResCh <- err
+                       case <-dataDone:
+                               return
+                       }
+               }
        }
 
        args := lxdClient.InstanceExecArgs{
@@ -2851,19 +2885,19 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
 
        op, err := agent.ExecInstance("", req, &args)
        if err != nil {
-               return nil, nil, err
+               return nil, err
        }
 
-       // Wait for the control websocket to be connected.
-       <-chControl
-
        instCmd = &Cmd{
-               cmd:         op,
-               dataDone:    args.DataDone,
-               cleanupFunc: cleanupFunc,
+               cmd:              op,
+               attachedChildPid: -1, // Process is not running on LXD host.
+               dataDone:         args.DataDone,
+               cleanupFunc:      cleanupFunc,
+               signalSendCh:     signalSendCh,
+               signalResCh:      signalResCh,
        }
 
-       return instCmd, wsControl, nil
+       return instCmd, nil
 }
 
 // Render returns info about the instance.
diff --git a/lxd/instance/drivers/driver_qemu_cmd.go 
b/lxd/instance/drivers/driver_qemu_cmd.go
index dc1c7d6c6c..2b117334f0 100644
--- a/lxd/instance/drivers/driver_qemu_cmd.go
+++ b/lxd/instance/drivers/driver_qemu_cmd.go
@@ -1,8 +1,6 @@
 package drivers
 
 import (
-       "fmt"
-
        "golang.org/x/sys/unix"
 
        lxdClient "github.com/lxc/lxd/client"
@@ -10,19 +8,23 @@ import (
 
 // Cmd represents a running command for an Qemu VM.
 type Cmd struct {
-       cmd         lxdClient.Operation
-       dataDone    chan bool
-       cleanupFunc func()
+       attachedChildPid int
+       cmd              lxdClient.Operation
+       dataDone         chan bool
+       signalSendCh     chan unix.Signal
+       signalResCh      chan error
+       cleanupFunc      func()
 }
 
 // PID returns the attached child's process ID.
 func (c *Cmd) PID() int {
-       return -1
+       return c.attachedChildPid
 }
 
 // Signal sends a signal to the command.
 func (c *Cmd) Signal(sig unix.Signal) error {
-       return fmt.Errorf("Not supported")
+       c.signalSendCh <- sig
+       return <-c.signalResCh
 }
 
 // Wait for the command to end and returns its exit code and any error.
diff --git a/lxd/instance/instance_interface.go 
b/lxd/instance/instance_interface.go
index 192420923b..59a39167ad 100644
--- a/lxd/instance/instance_interface.go
+++ b/lxd/instance/instance_interface.go
@@ -5,8 +5,6 @@ import (
        "os"
        "time"
 
-       "github.com/gorilla/websocket"
-
        "github.com/lxc/lxd/lxd/backup"
        "github.com/lxc/lxd/lxd/db"
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
@@ -53,7 +51,7 @@ type Instance interface {
 
        // Console - Allocate and run a console tty.
        Console() (*os.File, chan error, error)
-       Exec(req api.InstanceExecPost, stdin *os.File, stdout *os.File, stderr 
*os.File) (Cmd, *websocket.Conn, error)
+       Exec(req api.InstanceExecPost, stdin *os.File, stdout *os.File, stderr 
*os.File) (Cmd, error)
 
        // Status
        Render() (interface{}, interface{}, error)

From f36bc0ce605c7e222f091faec7b7d8963a0137e9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 10:15:14 +0000
Subject: [PATCH 03/12] lxd/instance/drivers/driver/qemu/cmd: Makes qemu cmd
 struct qemu specific

Now its in a shared drivers package.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_qemu_cmd.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu_cmd.go 
b/lxd/instance/drivers/driver_qemu_cmd.go
index 2b117334f0..ff1db96133 100644
--- a/lxd/instance/drivers/driver_qemu_cmd.go
+++ b/lxd/instance/drivers/driver_qemu_cmd.go
@@ -7,7 +7,7 @@ import (
 )
 
 // Cmd represents a running command for an Qemu VM.
-type Cmd struct {
+type qemuCmd struct {
        attachedChildPid int
        cmd              lxdClient.Operation
        dataDone         chan bool
@@ -17,18 +17,18 @@ type Cmd struct {
 }
 
 // PID returns the attached child's process ID.
-func (c *Cmd) PID() int {
+func (c *qemuCmd) PID() int {
        return c.attachedChildPid
 }
 
 // Signal sends a signal to the command.
-func (c *Cmd) Signal(sig unix.Signal) error {
+func (c *qemuCmd) Signal(sig unix.Signal) error {
        c.signalSendCh <- sig
        return <-c.signalResCh
 }
 
 // Wait for the command to end and returns its exit code and any error.
-func (c *Cmd) Wait() (int, error) {
+func (c *qemuCmd) Wait() (int, error) {
        if c.cleanupFunc != nil {
                defer c.cleanupFunc()
        }

From 697b54ed5821f64c7fa4eb62b4634fe8fb904cf9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 10:28:07 +0000
Subject: [PATCH 04/12] lxd/instance/drivers/driver/qemu: Simplifies Exec with
 revert

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 43 ++++++++---------------------
 1 file changed, 12 insertions(+), 31 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index 6bdd96f5f6..0443ff9b76 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -37,6 +37,7 @@ import (
        "github.com/lxc/lxd/lxd/maas"
        "github.com/lxc/lxd/lxd/operations"
        "github.com/lxc/lxd/lxd/project"
+       "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/lxd/state"
        storagePools "github.com/lxc/lxd/lxd/storage"
        storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
@@ -2808,27 +2809,8 @@ func (vm *qemu) forwardSignal(control *websocket.Conn, 
sig unix.Signal) error {
 
 // Exec a command inside the instance.
 func (vm *qemu) Exec(req api.InstanceExecPost, stdin *os.File, stdout 
*os.File, stderr *os.File) (instance.Cmd, error) {
-       var instCmd *Cmd
-
-       // Because this function will exit before the remote command has 
finished, we create a
-       // cleanup function that will be passed to the instance function if 
successfully started to
-       // perform any cleanup needed when finished.
-       cleanupFuncs := []func(){}
-       cleanupFunc := func() {
-               for _, f := range cleanupFuncs {
-                       f()
-               }
-       }
-
-       defer func() {
-               // If no instance command has been been created it means 
something went wrong
-               // starting the remote command, so we should cleanup as this 
function ends.
-               // If the instance command is non-nil then we let the instance 
command itself run
-               // the cleanup functions when it is done.
-               if instCmd == nil {
-                       cleanupFunc()
-               }
-       }()
+       revert := revert.New()
+       defer revert.Fail()
 
        client, err := vm.getAgentClient()
        if err != nil {
@@ -2840,7 +2822,7 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
                logger.Errorf("Failed to connect to lxd-agent on %s: %v", 
vm.Name(), err)
                return nil, fmt.Errorf("Failed to connect to lxd-agent")
        }
-       cleanupFuncs = append(cleanupFuncs, agent.Disconnect)
+       revert.Add(agent.Disconnect)
 
        req.WaitForWS = true
        if req.Interactive {
@@ -2849,18 +2831,16 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
                if err != nil {
                        return nil, err
                }
-               cleanupFuncs = append(cleanupFuncs, func() {
-                       termios.Restore(int(stdin.Fd()), oldttystate)
-               })
+
+               revert.Add(func() { termios.Restore(int(stdin.Fd()), 
oldttystate) })
        }
 
        dataDone := make(chan bool)
        signalSendCh := make(chan unix.Signal)
        signalResCh := make(chan error)
 
-       // This is the signal control handler, it receives signals from lxc CLI 
and forwards them
-       // to the VM agent.
-       controlHander := func(control *websocket.Conn) {
+       // This is the signal control handler, it receives signals from lxc CLI 
and forwards them to the VM agent.
+       controlHandler := func(control *websocket.Conn) {
                closeMsg := 
websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")
                defer control.WriteMessage(websocket.CloseMessage, closeMsg)
 
@@ -2880,7 +2860,7 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
                Stdout:   stdout,
                Stderr:   stderr,
                DataDone: dataDone,
-               Control:  controlHander,
+               Control:  controlHandler,
        }
 
        op, err := agent.ExecInstance("", req, &args)
@@ -2888,15 +2868,16 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
                return nil, err
        }
 
-       instCmd = &Cmd{
+       instCmd := &qemuCmd{
                cmd:              op,
                attachedChildPid: -1, // Process is not running on LXD host.
                dataDone:         args.DataDone,
-               cleanupFunc:      cleanupFunc,
+               cleanupFunc:      revert.Clone().Fail, // Pass revert function 
clone as clean up function.
                signalSendCh:     signalSendCh,
                signalResCh:      signalResCh,
        }
 
+       revert.Success()
        return instCmd, nil
 }
 

From 4ffe1d15524235a480f6dad917aac8b4416ed0ad Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:25:34 +0000
Subject: [PATCH 05/12] lxd/container/exec: Cleaned up logging

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_exec.go | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 40c59bbd1b..14a1ae9548 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -162,7 +162,7 @@ func (s *execWs) Do(op *operations.Operation) error {
                                return
                        }
 
-                       logger.Debugf("Interactive child process handler 
started for child PID %d", attachedChild.PID())
+                       logger.Debugf(`Interactive child process handler 
started for child PID "%d"`, attachedChild.PID())
                        for {
                                s.connsLock.Lock()
                                conn := s.conns[-1]
@@ -174,7 +174,7 @@ func (s *execWs) Do(op *operations.Operation) error {
                                }
 
                                if err != nil {
-                                       logger.Debugf("Got error getting next 
reader %s", err)
+                                       logger.Debugf("Got error getting next 
reader: %v", err)
                                        er, ok := err.(*websocket.CloseError)
                                        if !ok {
                                                break
@@ -187,51 +187,50 @@ func (s *execWs) Do(op *operations.Operation) error {
                                        // If an abnormal closure occurred, 
kill the attached child.
                                        err := 
attachedChild.Signal(unix.SIGKILL)
                                        if err != nil {
-                                               logger.Debugf("Failed to send 
SIGKILL to PID %d: %v", attachedChild.PID(), err)
+                                               logger.Debugf(`Failed to send 
SIGKILL to PID "%d": %v`, attachedChild.PID(), err)
                                        } else {
-                                               logger.Debugf("Sent SIGKILL to 
PID %d", attachedChild.PID())
+                                               logger.Debugf(`Sent SIGKILL to 
PID "%d"`, attachedChild.PID())
                                        }
                                        return
                                }
 
                                buf, err := ioutil.ReadAll(r)
                                if err != nil {
-                                       logger.Debugf("Failed to read message 
%s", err)
+                                       logger.Debugf("Failed to read message: 
%v", err)
                                        break
                                }
 
                                command := api.InstanceExecControl{}
 
                                if err := json.Unmarshal(buf, &command); err != 
nil {
-                                       logger.Debugf("Failed to unmarshal 
control socket command: %s", err)
+                                       logger.Debugf("Failed to unmarshal 
control socket command: %v", err)
                                        continue
                                }
 
                                if command.Command == "window-resize" {
                                        winchWidth, err := 
strconv.Atoi(command.Args["width"])
                                        if err != nil {
-                                               logger.Debugf("Unable to 
extract window width: %s", err)
+                                               logger.Debugf("Unable to 
extract window width: %v", err)
                                                continue
                                        }
 
                                        winchHeight, err := 
strconv.Atoi(command.Args["height"])
                                        if err != nil {
-                                               logger.Debugf("Unable to 
extract window height: %s", err)
+                                               logger.Debugf("Unable to 
extract window height: %v", err)
                                                continue
                                        }
 
                                        err = shared.SetSize(int(ptys[0].Fd()), 
winchWidth, winchHeight)
                                        if err != nil {
-                                               logger.Debugf("Failed to set 
window size to: %dx%d", winchWidth, winchHeight)
+                                               logger.Debugf(`Failed to set 
window size to "%dx%d": %v`, winchWidth, winchHeight, err)
                                                continue
                                        }
                                } else if command.Command == "signal" {
                                        err := 
attachedChild.Signal(unix.Signal(command.Signal))
                                        if err != nil {
-                                               logger.Debugf("Failed 
forwarding signal '%d' to PID %d: %v", command.Signal, attachedChild.PID(), err)
+                                               logger.Debugf(`Failed 
forwarding signal "%d" to PID "%d": %v`, command.Signal, attachedChild.PID(), 
err)
                                                continue
                                        }
-                                       logger.Debugf("Forwarded signal '%d' to 
PID %d", command.Signal, attachedChild.PID())
                                }
                        }
                }()

From e501531add0be6806384b5928aefbb4caf43042d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:25:46 +0000
Subject: [PATCH 06/12] lxd/container/exec: Switches to use instance command
 for resizing window

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_exec.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 14a1ae9548..5313bc3e83 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -220,7 +220,7 @@ func (s *execWs) Do(op *operations.Operation) error {
                                                continue
                                        }
 
-                                       err = shared.SetSize(int(ptys[0].Fd()), 
winchWidth, winchHeight)
+                                       err = 
attachedChild.WindowResize(int(ptys[0].Fd()), winchWidth, winchHeight)
                                        if err != nil {
                                                logger.Debugf(`Failed to set 
window size to "%dx%d": %v`, winchWidth, winchHeight, err)
                                                continue

From 3f85f1e3022e9918743b26f90aabe3463290f1c9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:26:17 +0000
Subject: [PATCH 07/12] lxd/container/lxc/exec/cmd: Adds WindowResize

And cleans up Signal function.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_lxc_exec_cmd.go | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/lxd/container_lxc_exec_cmd.go b/lxd/container_lxc_exec_cmd.go
index 9a1bb64a9b..ea9543022a 100644
--- a/lxd/container_lxc_exec_cmd.go
+++ b/lxd/container_lxc_exec_cmd.go
@@ -5,6 +5,9 @@ import (
        "syscall"
 
        "golang.org/x/sys/unix"
+
+       "github.com/lxc/lxd/shared"
+       "github.com/lxc/lxd/shared/logger"
 )
 
 // ContainerLXCCmd represents a running command for an LXC container.
@@ -19,12 +22,13 @@ func (c *ContainerLXCCmd) PID() int {
 }
 
 // Signal sends a signal to the command.
-func (c *ContainerLXCCmd) Signal(s unix.Signal) error {
-       err := unix.Kill(c.attachedChildPid, s)
+func (c *ContainerLXCCmd) Signal(sig unix.Signal) error {
+       err := unix.Kill(c.attachedChildPid, sig)
        if err != nil {
                return err
        }
 
+       logger.Debugf(`Forwarded signal "%d" to PID "%d"`, sig, c.PID())
        return nil
 }
 
@@ -50,3 +54,14 @@ func (c *ContainerLXCCmd) Wait() (int, error) {
 
        return 0, nil
 }
+
+// WindowResize resizes the running command's window.
+func (c *ContainerLXCCmd) WindowResize(fd, winchWidth, winchHeight int) error {
+       err := shared.SetSize(fd, winchWidth, winchHeight)
+       if err != nil {
+               return err
+       }
+
+       logger.Debugf(`Set window size "%dx%d" of PID "%d"`, winchWidth, 
winchHeight, c.PID())
+       return nil
+}

From 79d6811a20fb20e4bd0bd2b5116113e85beb018e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:26:45 +0000
Subject: [PATCH 08/12] lxd/instance/instance/exec/cmd: Adds WindowResize
 function to signature

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/instance_exec_cmd.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/instance/instance_exec_cmd.go 
b/lxd/instance/instance_exec_cmd.go
index b3ce642107..fe493a7964 100644
--- a/lxd/instance/instance_exec_cmd.go
+++ b/lxd/instance/instance_exec_cmd.go
@@ -9,4 +9,5 @@ type Cmd interface {
        Wait() (int, error)
        PID() int
        Signal(s unix.Signal) error
+       WindowResize(fd, winchWidth, winchHeight int) error
 }

From 403bfa2b0a6375f550e09a22c4870a3a5ac095c9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:27:08 +0000
Subject: [PATCH 09/12] lxd/instance/drivers/driver/qemu: Reworks command
 control

Switches to general control command forwarder to support resize window.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 24 +++++++++---------------
 1 file changed, 9 insertions(+), 15 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index 0443ff9b76..5a25c3c468 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -2785,19 +2785,13 @@ func (vm *qemu) Console() (*os.File, chan error, error) 
{
        return console, chDisconnect, nil
 }
 
-func (vm *qemu) forwardSignal(control *websocket.Conn, sig unix.Signal) error {
-       logger.Debugf("Forwarding signal to lxd-agent: %s", sig)
-
+func (vm *qemu) forwardControlCommand(control *websocket.Conn, cmd 
api.InstanceExecControl) error {
        w, err := control.NextWriter(websocket.TextMessage)
        if err != nil {
                return err
        }
 
-       msg := api.InstanceExecControl{}
-       msg.Command = "signal"
-       msg.Signal = int(sig)
-
-       buf, err := json.Marshal(msg)
+       buf, err := json.Marshal(cmd)
        if err != nil {
                return err
        }
@@ -2836,8 +2830,8 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
        }
 
        dataDone := make(chan bool)
-       signalSendCh := make(chan unix.Signal)
-       signalResCh := make(chan error)
+       controlSendCh := make(chan api.InstanceExecControl)
+       controlResCh := make(chan error)
 
        // This is the signal control handler, it receives signals from lxc CLI 
and forwards them to the VM agent.
        controlHandler := func(control *websocket.Conn) {
@@ -2846,9 +2840,9 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
 
                for {
                        select {
-                       case signal := <-signalSendCh:
-                               err := vm.forwardSignal(control, signal)
-                               signalResCh <- err
+                       case cmd := <-controlSendCh:
+                               err := vm.forwardControlCommand(control, cmd)
+                               controlResCh <- err
                        case <-dataDone:
                                return
                        }
@@ -2873,8 +2867,8 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
                attachedChildPid: -1, // Process is not running on LXD host.
                dataDone:         args.DataDone,
                cleanupFunc:      revert.Clone().Fail, // Pass revert function 
clone as clean up function.
-               signalSendCh:     signalSendCh,
-               signalResCh:      signalResCh,
+               controlSendCh:    controlSendCh,
+               controlResCh:     controlResCh,
        }
 
        revert.Success()

From 3de080956973501361edbebe713284cfe17a20f5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:27:43 +0000
Subject: [PATCH 10/12] lxd/instance/drivers/driver/qemu/cmd: Adds WindowResize
 support

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_qemu_cmd.go | 42 ++++++++++++++++++++++---
 1 file changed, 38 insertions(+), 4 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu_cmd.go 
b/lxd/instance/drivers/driver_qemu_cmd.go
index ff1db96133..edc7e5ed25 100644
--- a/lxd/instance/drivers/driver_qemu_cmd.go
+++ b/lxd/instance/drivers/driver_qemu_cmd.go
@@ -1,9 +1,13 @@
 package drivers
 
 import (
+       "strconv"
+
        "golang.org/x/sys/unix"
 
        lxdClient "github.com/lxc/lxd/client"
+       "github.com/lxc/lxd/shared/api"
+       "github.com/lxc/lxd/shared/logger"
 )
 
 // Cmd represents a running command for an Qemu VM.
@@ -11,8 +15,8 @@ type qemuCmd struct {
        attachedChildPid int
        cmd              lxdClient.Operation
        dataDone         chan bool
-       signalSendCh     chan unix.Signal
-       signalResCh      chan error
+       controlSendCh    chan api.InstanceExecControl
+       controlResCh     chan error
        cleanupFunc      func()
 }
 
@@ -23,8 +27,19 @@ func (c *qemuCmd) PID() int {
 
 // Signal sends a signal to the command.
 func (c *qemuCmd) Signal(sig unix.Signal) error {
-       c.signalSendCh <- sig
-       return <-c.signalResCh
+       command := api.InstanceExecControl{
+               Command: "signal",
+               Signal:  int(sig),
+       }
+
+       c.controlSendCh <- command
+       err := <-c.controlResCh
+       if err != nil {
+               return err
+       }
+
+       logger.Debugf(`Forwarded signal "%d" to lxd-agent`, sig)
+       return nil
 }
 
 // Wait for the command to end and returns its exit code and any error.
@@ -44,3 +59,22 @@ func (c *qemuCmd) Wait() (int, error) {
 
        return exitCode, nil
 }
+
+// WindowResize resizes the running command's window.
+func (c *qemuCmd) WindowResize(fd, winchWidth, winchHeight int) error {
+       command := api.InstanceExecControl{
+               Command: "window-resize",
+               Args: map[string]string{
+                       "width":  strconv.Itoa(winchWidth),
+                       "height": strconv.Itoa(winchHeight),
+               },
+       }
+
+       c.controlSendCh <- command
+       err := <-c.controlResCh
+       if err != nil {
+               return err
+       }
+       logger.Debugf(`Forwarded window resize "%dx%d" to lxd-agent`, 
winchWidth, winchHeight)
+       return nil
+}

From 6b2627ef408b28243b0d7fbf3abeca6720d8be5e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:39:10 +0000
Subject: [PATCH 11/12] lxd/instance/drivers/driver/qemu: Sets PID to 0 for VM
 commands

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index 5a25c3c468..036c161dc1 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -2864,7 +2864,7 @@ func (vm *qemu) Exec(req api.InstanceExecPost, stdin 
*os.File, stdout *os.File,
 
        instCmd := &qemuCmd{
                cmd:              op,
-               attachedChildPid: -1, // Process is not running on LXD host.
+               attachedChildPid: 0, // Process is not running on LXD host.
                dataDone:         args.DataDone,
                cleanupFunc:      revert.Clone().Fail, // Pass revert function 
clone as clean up function.
                controlSendCh:    controlSendCh,

From b3b896725d800ef9d856627be6c86bbe8fd536b2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 21 Jan 2020 11:40:59 +0000
Subject: [PATCH 12/12] lxd/instance/drivers/driver/qemu: comment on
 forwardControlCommand

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index 036c161dc1..0639b907b2 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -2785,6 +2785,7 @@ func (vm *qemu) Console() (*os.File, chan error, error) {
        return console, chDisconnect, nil
 }
 
+// forwardControlCommand is used to send command control messages to the 
lxd-agent.
 func (vm *qemu) forwardControlCommand(control *websocket.Conn, cmd 
api.InstanceExecControl) error {
        w, err := control.NextWriter(websocket.TextMessage)
        if err != nil {
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to