Skip to content

Commit 49d6db2

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

18 files changed

+313
-185
lines changed

cmd/lima-guestagent/daemon_linux.go

Lines changed: 19 additions & 17 deletions
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/driver/driver.go

Lines changed: 8 additions & 0 deletions
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

Lines changed: 4 additions & 31 deletions
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

Lines changed: 0 additions & 15 deletions
This file was deleted.

pkg/guestagent/api/client/client_windows.go

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
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+
devpath string
10+
port *Port
11+
}
12+
13+
var _ net.Conn = (*SerialConn)(nil)
14+
15+
func DialSerial(devpath string) (*SerialConn, error) {
16+
s, err := openPort(devpath)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
return &SerialConn{port: s, devpath: devpath}, 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.devpath, 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+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
}
64+
65+
func (ln *SerialListener) CloseConn() error {
66+
if !ln.ok() {
67+
return syscall.EINVAL
68+
}
69+
70+
return ln.conn.Close()
71+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
rate := unix.B115200
11+
12+
f, err := os.OpenFile(name, unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0o666)
13+
if err != nil {
14+
return nil, err
15+
}
16+
17+
defer func() {
18+
if err != nil && f != nil {
19+
f.Close()
20+
}
21+
}()
22+
23+
// Base settings
24+
cflagToUse := unix.CREAD | unix.CLOCAL | rate
25+
cflagToUse |= unix.CS8
26+
27+
fd := f.Fd()
28+
29+
var readTimeoutInDeci int64
30+
t := unix.Termios{
31+
Iflag: unix.IGNPAR,
32+
Cflag: uint32(cflagToUse),
33+
Ispeed: uint32(rate),
34+
Ospeed: uint32(rate),
35+
}
36+
t.Cc[unix.VMIN] = uint8(1)
37+
t.Cc[unix.VTIME] = uint8(readTimeoutInDeci)
38+
39+
if err = unix.SetNonblock(int(fd), false); err != nil {
40+
return nil, err
41+
}
42+
43+
return &Port{f: f}, nil
44+
}
45+
46+
type Port struct {
47+
f *os.File
48+
}
49+
50+
func (p *Port) Read(b []byte) (n int, err error) {
51+
return p.f.Read(b)
52+
}
53+
54+
func (p *Port) Write(b []byte) (n int, err error) {
55+
return p.f.Write(b)
56+
}
57+
58+
func (p *Port) Close() (err error) {
59+
return p.f.Close()
60+
}

0 commit comments

Comments
 (0)