Skip to content

Commit

Permalink
Support for native guest agent connection for each driver
Browse files Browse the repository at this point in the history
Signed-off-by: Balaji Vijayakumar <kuttibalaji.v6@gmail.com>
  • Loading branch information
balajiv113 committed Nov 14, 2023
1 parent 79cfe42 commit a6dca6a
Show file tree
Hide file tree
Showing 19 changed files with 289 additions and 190 deletions.
36 changes: 19 additions & 17 deletions cmd/lima-guestagent/daemon_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/gorilla/mux"
"github.com/lima-vm/lima/pkg/guestagent"
"github.com/lima-vm/lima/pkg/guestagent/api/server"
"github.com/lima-vm/lima/pkg/guestagent/serialport"
"github.com/mdlayher/vsock"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -26,16 +27,24 @@ func newDaemonCommand() *cobra.Command {
return daemonCommand
}

var (
vSockPort = 0

qemuFile = "/dev/virtio-ports/lima.guest_agent.0"
)

func daemonAction(cmd *cobra.Command, _ []string) error {
socket := "/run/lima-guestagent.sock"
tick, err := cmd.Flags().GetDuration("tick")
if err != nil {
return err
}
vSockPort, err := cmd.Flags().GetInt("vsock-port")
vSockPortOverride, err := cmd.Flags().GetInt("vsock-port")
if err != nil {
return err
}
if vSockPortOverride != 0 {
vSockPort = vSockPortOverride
}
if tick == 0 {
return errors.New("tick must be specified")
}
Expand All @@ -62,29 +71,22 @@ func daemonAction(cmd *cobra.Command, _ []string) error {
r := mux.NewRouter()
server.AddRoutes(r, backend)
srv := &http.Server{Handler: r}
err = os.RemoveAll(socket)
if err != nil {
return err
}

var l net.Listener
if vSockPort != 0 {
vsockL, err := vsock.Listen(uint32(vSockPort), nil)
if _, err := os.Stat(qemuFile); err == nil {
qemuL, err := serialport.Listen(qemuFile)
if err != nil {
return err
}
l = vsockL
logrus.Infof("serving the guest agent on vsock port: %d", vSockPort)
} else {
socketL, err := net.Listen("unix", socket)
l = qemuL
logrus.Infof("serving the guest agent on qemu serial file: %s", qemuFile)
} else if vSockPort != 0 {
vsockL, err := vsock.Listen(uint32(vSockPort), nil)
if err != nil {
return err
}
if err := os.Chmod(socket, 0o777); err != nil {
return err
}
l = socketL
logrus.Infof("serving the guest agent on %q", socket)
l = vsockL
logrus.Infof("serving the guest agent on vsock port: %d", vSockPort)
}
return srv.Serve(l)
}
6 changes: 1 addition & 5 deletions pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,5 @@ else
# Remove legacy systemd service
rm -f "${LIMA_CIDATA_HOME}/.config/systemd/user/lima-guestagent.service"

if [ "$LIMA_CIDATA_VMTYPE" = "wsl2" ]; then
sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd --vsock-port "${LIMA_CIDATA_VSOCK_PORT}"
else
sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd
fi
sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd --vsock-port "${LIMA_CIDATA_VSOCK_PORT}"
fi
8 changes: 8 additions & 0 deletions pkg/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package driver
import (
"context"
"fmt"
"net"

"github.com/lima-vm/lima/pkg/limayaml"
"github.com/lima-vm/lima/pkg/store"
Expand Down Expand Up @@ -62,13 +63,16 @@ type Driver interface {
DeleteSnapshot(_ context.Context, tag string) error

ListSnapshots(_ context.Context) (string, error)

GuestAgentConn(_ context.Context) (net.Conn, error)
}

type BaseDriver struct {
Instance *store.Instance
Yaml *limayaml.LimaYAML

SSHLocalPort int
VSockPort int
}

var _ Driver = (*BaseDriver)(nil)
Expand Down Expand Up @@ -132,3 +136,7 @@ func (d *BaseDriver) DeleteSnapshot(_ context.Context, _ string) error {
func (d *BaseDriver) ListSnapshots(_ context.Context) (string, error) {
return "", fmt.Errorf("unimplemented")
}

func (d *BaseDriver) GuestAgentConn(_ context.Context) (net.Conn, error) {
return nil, fmt.Errorf("unimplemented")
}
35 changes: 4 additions & 31 deletions pkg/guestagent/api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"fmt"
"net"
"net/http"
"strconv"

"github.com/lima-vm/lima/pkg/guestagent/api"
"github.com/lima-vm/lima/pkg/httpclientutil"
Expand All @@ -21,39 +20,13 @@ type GuestAgentClient interface {
Events(context.Context, func(api.Event)) error
}

type Proto = string

const (
UNIX Proto = "unix"
VSOCK Proto = "vsock"
)

// NewGuestAgentClient creates a client.
// remote is a path to the UNIX socket, without unix:// prefix or a remote hostname/IP address.
func NewGuestAgentClient(remote string, proto Proto, instanceName string) (GuestAgentClient, error) {
var hc *http.Client
switch proto {
case UNIX:
hcSock, err := httpclientutil.NewHTTPClientWithSocketPath(remote)
if err != nil {
return nil, err
}
hc = hcSock
case VSOCK:
_, p, err := net.SplitHostPort(remote)
if err != nil {
return nil, err
}
port, err := strconv.Atoi(p)
if err != nil {
return nil, err
}
hc, err = newVSockGuestAgentClient(port, instanceName)
if err != nil {
return nil, err
}
func NewGuestAgentClient(conn net.Conn) (GuestAgentClient, error) {
hc, err := httpclientutil.NewHTTPClientWithConn(conn)
if err != nil {
return nil, err
}

return NewGuestAgentClientWithHTTPClient(hc), nil
}

Expand Down
15 changes: 0 additions & 15 deletions pkg/guestagent/api/client/client_others.go

This file was deleted.

16 changes: 0 additions & 16 deletions pkg/guestagent/api/client/client_windows.go

This file was deleted.

56 changes: 56 additions & 0 deletions pkg/guestagent/serialport/serialconn_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package serialport

import (
"net"
"time"
)

type SerialConn struct {
serialDevice string
port *Port
}

var _ net.Conn = (*SerialConn)(nil)

func DialSerial(serialDevice string) (*SerialConn, error) {
s, err := openPort(serialDevice)
if err != nil {
return nil, err
}

return &SerialConn{port: s, serialDevice: serialDevice}, nil
}

func (c *SerialConn) Read(b []byte) (n int, err error) {
return c.port.Read(b)
}

func (c *SerialConn) Write(b []byte) (n int, err error) {
return c.port.Write(b)
}

func (c *SerialConn) Close() error {
// There is no need to close the serial port every time.
// So just do nothing.
return nil
}

func (c *SerialConn) LocalAddr() net.Addr {
return &net.UnixAddr{Name: "virtio-port:" + c.serialDevice, Net: "virtio"}
}

func (c *SerialConn) RemoteAddr() net.Addr {
return &net.UnixAddr{Name: "qemu-host", Net: "virtio"}
}

func (c *SerialConn) SetDeadline(_ time.Time) error {
return nil
}

func (c *SerialConn) SetReadDeadline(_ time.Time) error {
return nil
}

func (c *SerialConn) SetWriteDeadline(_ time.Time) error {
return nil
}
63 changes: 63 additions & 0 deletions pkg/guestagent/serialport/seriallistener_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package serialport

import (
"net"
"sync"
"syscall"

"golang.org/x/net/netutil"
)

type SerialListener struct {
mu sync.Mutex
conn *SerialConn
closed bool
}

func Listen(serialDevice string) (net.Listener, error) {
c, err := DialSerial(serialDevice)
if err != nil {
return nil, &net.OpError{Op: "dial", Net: "virtio", Source: c.LocalAddr(), Addr: nil, Err: err}
}

return netutil.LimitListener(&SerialListener{conn: c}, 1), nil
}

func (ln *SerialListener) ok() bool {
return ln != nil && ln.conn != nil && !ln.closed
}

func (ln *SerialListener) Accept() (net.Conn, error) {
ln.mu.Lock()
defer ln.mu.Unlock()

if !ln.ok() {
return nil, syscall.EINVAL
}

return ln.conn, nil
}

func (ln *SerialListener) Close() error {
ln.mu.Lock()
defer ln.mu.Unlock()

if !ln.ok() {
return syscall.EINVAL
}

if ln.closed {
return nil
}
ln.closed = true

return nil
}

func (ln *SerialListener) Addr() net.Addr {
if ln.ok() {
return ln.conn.LocalAddr()
}

return nil
}
43 changes: 43 additions & 0 deletions pkg/guestagent/serialport/serialport_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package serialport

import (
"os"

"golang.org/x/sys/unix"
)

func openPort(name string) (p *Port, err error) {
f, err := os.OpenFile(name, unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0o666)
if err != nil {
return nil, err
}

defer func() {
if err != nil && f != nil {
f.Close()
}
}()

fd := f.Fd()
if err = unix.SetNonblock(int(fd), false); err != nil {
return nil, err
}

return &Port{f: f}, nil
}

type Port struct {
f *os.File
}

func (p *Port) Read(b []byte) (n int, err error) {
return p.f.Read(b)
}

func (p *Port) Write(b []byte) (n int, err error) {
return p.f.Write(b)
}

func (p *Port) Close() (err error) {
return p.f.Close()
}
Loading

0 comments on commit a6dca6a

Please sign in to comment.