Skip to content

Commit 853e43b

Browse files
committed
Support for native guest agent connection for each driver
Signed-off-by: Balaji Vijayakumar <[email protected]>
1 parent 79cfe42 commit 853e43b

19 files changed

+289
-190
lines changed

cmd/lima-guestagent/daemon_linux.go

+19-17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/gorilla/mux"
1111
"github.com/lima-vm/lima/pkg/guestagent"
1212
"github.com/lima-vm/lima/pkg/guestagent/api/server"
13+
"github.com/lima-vm/lima/pkg/guestagent/serialport"
1314
"github.com/mdlayher/vsock"
1415
"github.com/sirupsen/logrus"
1516
"github.com/spf13/cobra"
@@ -26,16 +27,24 @@ func newDaemonCommand() *cobra.Command {
2627
return daemonCommand
2728
}
2829

30+
var (
31+
vSockPort = 0
32+
33+
qemuFile = "/dev/virtio-ports/lima.guest_agent.0"
34+
)
35+
2936
func daemonAction(cmd *cobra.Command, _ []string) error {
30-
socket := "/run/lima-guestagent.sock"
3137
tick, err := cmd.Flags().GetDuration("tick")
3238
if err != nil {
3339
return err
3440
}
35-
vSockPort, err := cmd.Flags().GetInt("vsock-port")
41+
vSockPortOverride, err := cmd.Flags().GetInt("vsock-port")
3642
if err != nil {
3743
return err
3844
}
45+
if vSockPortOverride != 0 {
46+
vSockPort = vSockPortOverride
47+
}
3948
if tick == 0 {
4049
return errors.New("tick must be specified")
4150
}
@@ -62,29 +71,22 @@ func daemonAction(cmd *cobra.Command, _ []string) error {
6271
r := mux.NewRouter()
6372
server.AddRoutes(r, backend)
6473
srv := &http.Server{Handler: r}
65-
err = os.RemoveAll(socket)
66-
if err != nil {
67-
return err
68-
}
6974

7075
var l net.Listener
71-
if vSockPort != 0 {
72-
vsockL, err := vsock.Listen(uint32(vSockPort), nil)
76+
if _, err := os.Stat(qemuFile); err == nil {
77+
qemuL, err := serialport.Listen(qemuFile)
7378
if err != nil {
7479
return err
7580
}
76-
l = vsockL
77-
logrus.Infof("serving the guest agent on vsock port: %d", vSockPort)
78-
} else {
79-
socketL, err := net.Listen("unix", socket)
81+
l = qemuL
82+
logrus.Infof("serving the guest agent on qemu serial file: %s", qemuFile)
83+
} else if vSockPort != 0 {
84+
vsockL, err := vsock.Listen(uint32(vSockPort), nil)
8085
if err != nil {
8186
return err
8287
}
83-
if err := os.Chmod(socket, 0o777); err != nil {
84-
return err
85-
}
86-
l = socketL
87-
logrus.Infof("serving the guest agent on %q", socket)
88+
l = vsockL
89+
logrus.Infof("serving the guest agent on vsock port: %d", vSockPort)
8890
}
8991
return srv.Serve(l)
9092
}

pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh

+1-5
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,5 @@ else
4040
# Remove legacy systemd service
4141
rm -f "${LIMA_CIDATA_HOME}/.config/systemd/user/lima-guestagent.service"
4242

43-
if [ "$LIMA_CIDATA_VMTYPE" = "wsl2" ]; then
44-
sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd --vsock-port "${LIMA_CIDATA_VSOCK_PORT}"
45-
else
46-
sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd
47-
fi
43+
sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd --vsock-port "${LIMA_CIDATA_VSOCK_PORT}"
4844
fi

pkg/driver/driver.go

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package driver
33
import (
44
"context"
55
"fmt"
6+
"net"
67

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

6465
ListSnapshots(_ context.Context) (string, error)
66+
67+
GuestAgentConn(_ context.Context) (net.Conn, error)
6568
}
6669

6770
type BaseDriver struct {
6871
Instance *store.Instance
6972
Yaml *limayaml.LimaYAML
7073

7174
SSHLocalPort int
75+
VSockPort int
7276
}
7377

7478
var _ Driver = (*BaseDriver)(nil)
@@ -132,3 +136,7 @@ func (d *BaseDriver) DeleteSnapshot(_ context.Context, _ string) error {
132136
func (d *BaseDriver) ListSnapshots(_ context.Context) (string, error) {
133137
return "", fmt.Errorf("unimplemented")
134138
}
139+
140+
func (d *BaseDriver) GuestAgentConn(_ context.Context) (net.Conn, error) {
141+
return nil, fmt.Errorf("unimplemented")
142+
}

pkg/guestagent/api/client/client.go

+4-31
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"fmt"
1010
"net"
1111
"net/http"
12-
"strconv"
1312

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

24-
type Proto = string
25-
26-
const (
27-
UNIX Proto = "unix"
28-
VSOCK Proto = "vsock"
29-
)
30-
3123
// NewGuestAgentClient creates a client.
3224
// remote is a path to the UNIX socket, without unix:// prefix or a remote hostname/IP address.
33-
func NewGuestAgentClient(remote string, proto Proto, instanceName string) (GuestAgentClient, error) {
34-
var hc *http.Client
35-
switch proto {
36-
case UNIX:
37-
hcSock, err := httpclientutil.NewHTTPClientWithSocketPath(remote)
38-
if err != nil {
39-
return nil, err
40-
}
41-
hc = hcSock
42-
case VSOCK:
43-
_, p, err := net.SplitHostPort(remote)
44-
if err != nil {
45-
return nil, err
46-
}
47-
port, err := strconv.Atoi(p)
48-
if err != nil {
49-
return nil, err
50-
}
51-
hc, err = newVSockGuestAgentClient(port, instanceName)
52-
if err != nil {
53-
return nil, err
54-
}
25+
func NewGuestAgentClient(conn net.Conn) (GuestAgentClient, error) {
26+
hc, err := httpclientutil.NewHTTPClientWithConn(conn)
27+
if err != nil {
28+
return nil, err
5529
}
56-
5730
return NewGuestAgentClientWithHTTPClient(hc), nil
5831
}
5932

pkg/guestagent/api/client/client_others.go

-15
This file was deleted.

pkg/guestagent/api/client/client_windows.go

-16
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package serialport
2+
3+
import (
4+
"net"
5+
"time"
6+
)
7+
8+
type SerialConn struct {
9+
serialDevice string
10+
port *Port
11+
}
12+
13+
var _ net.Conn = (*SerialConn)(nil)
14+
15+
func DialSerial(serialDevice string) (*SerialConn, error) {
16+
s, err := openPort(serialDevice)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
return &SerialConn{port: s, serialDevice: serialDevice}, nil
22+
}
23+
24+
func (c *SerialConn) Read(b []byte) (n int, err error) {
25+
return c.port.Read(b)
26+
}
27+
28+
func (c *SerialConn) Write(b []byte) (n int, err error) {
29+
return c.port.Write(b)
30+
}
31+
32+
func (c *SerialConn) Close() error {
33+
// There is no need to close the serial port every time.
34+
// So just do nothing.
35+
return nil
36+
}
37+
38+
func (c *SerialConn) LocalAddr() net.Addr {
39+
return &net.UnixAddr{Name: "virtio-port:" + c.serialDevice, Net: "virtio"}
40+
}
41+
42+
func (c *SerialConn) RemoteAddr() net.Addr {
43+
return &net.UnixAddr{Name: "qemu-host", Net: "virtio"}
44+
}
45+
46+
func (c *SerialConn) SetDeadline(_ time.Time) error {
47+
return nil
48+
}
49+
50+
func (c *SerialConn) SetReadDeadline(_ time.Time) error {
51+
return nil
52+
}
53+
54+
func (c *SerialConn) SetWriteDeadline(_ time.Time) error {
55+
return nil
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package serialport
2+
3+
import (
4+
"net"
5+
"sync"
6+
"syscall"
7+
8+
"golang.org/x/net/netutil"
9+
)
10+
11+
type SerialListener struct {
12+
mu sync.Mutex
13+
conn *SerialConn
14+
closed bool
15+
}
16+
17+
func Listen(serialDevice string) (net.Listener, error) {
18+
c, err := DialSerial(serialDevice)
19+
if err != nil {
20+
return nil, &net.OpError{Op: "dial", Net: "virtio", Source: c.LocalAddr(), Addr: nil, Err: err}
21+
}
22+
23+
return netutil.LimitListener(&SerialListener{conn: c}, 1), nil
24+
}
25+
26+
func (ln *SerialListener) ok() bool {
27+
return ln != nil && ln.conn != nil && !ln.closed
28+
}
29+
30+
func (ln *SerialListener) Accept() (net.Conn, error) {
31+
ln.mu.Lock()
32+
defer ln.mu.Unlock()
33+
34+
if !ln.ok() {
35+
return nil, syscall.EINVAL
36+
}
37+
38+
return ln.conn, nil
39+
}
40+
41+
func (ln *SerialListener) Close() error {
42+
ln.mu.Lock()
43+
defer ln.mu.Unlock()
44+
45+
if !ln.ok() {
46+
return syscall.EINVAL
47+
}
48+
49+
if ln.closed {
50+
return nil
51+
}
52+
ln.closed = true
53+
54+
return nil
55+
}
56+
57+
func (ln *SerialListener) Addr() net.Addr {
58+
if ln.ok() {
59+
return ln.conn.LocalAddr()
60+
}
61+
62+
return nil
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package serialport
2+
3+
import (
4+
"os"
5+
6+
"golang.org/x/sys/unix"
7+
)
8+
9+
func openPort(name string) (p *Port, err error) {
10+
f, err := os.OpenFile(name, unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0o666)
11+
if err != nil {
12+
return nil, err
13+
}
14+
15+
defer func() {
16+
if err != nil && f != nil {
17+
f.Close()
18+
}
19+
}()
20+
21+
fd := f.Fd()
22+
if err = unix.SetNonblock(int(fd), false); err != nil {
23+
return nil, err
24+
}
25+
26+
return &Port{f: f}, nil
27+
}
28+
29+
type Port struct {
30+
f *os.File
31+
}
32+
33+
func (p *Port) Read(b []byte) (n int, err error) {
34+
return p.f.Read(b)
35+
}
36+
37+
func (p *Port) Write(b []byte) (n int, err error) {
38+
return p.f.Write(b)
39+
}
40+
41+
func (p *Port) Close() (err error) {
42+
return p.f.Close()
43+
}

0 commit comments

Comments
 (0)