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