From 96ba8a8de4425fc0fa0312957ef0eb541895b840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Fri, 17 Nov 2023 17:47:10 +0100 Subject: [PATCH] Restore guest agent unix socket functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make both vsock and virtio explicit, instead of hardcoding internal filenames in the agents. Fallback to to the unix. Signed-off-by: Anders F Björklund --- cmd/lima-guestagent/daemon_linux.go | 30 +++++++++++---- .../boot/25-guestagent-base.sh | 10 ++++- pkg/cidata/cidata.TEMPLATE.d/lima.env | 1 + pkg/cidata/cidata.go | 3 +- pkg/cidata/template.go | 1 + pkg/driver/driver.go | 13 ++++++- pkg/hostagent/hostagent.go | 37 ++++++++++++++++--- .../content/en/docs/dev/Internals/_index.md | 4 +- 8 files changed, 81 insertions(+), 18 deletions(-) diff --git a/cmd/lima-guestagent/daemon_linux.go b/cmd/lima-guestagent/daemon_linux.go index 8507a5d7f6cb..e77f448f2fe4 100644 --- a/cmd/lima-guestagent/daemon_linux.go +++ b/cmd/lima-guestagent/daemon_linux.go @@ -11,7 +11,6 @@ import ( "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/lima-vm/lima/pkg/store/filenames" "github.com/mdlayher/vsock" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -25,21 +24,23 @@ func newDaemonCommand() *cobra.Command { } daemonCommand.Flags().Duration("tick", 3*time.Second, "tick for polling events") daemonCommand.Flags().Int("vsock-port", 0, "use vsock server instead a UNIX socket") + daemonCommand.Flags().String("virtio-port", "", "use virtio server instead a UNIX socket") return daemonCommand } func daemonAction(cmd *cobra.Command, _ []string) error { + socket := "/run/lima-guestagent.sock" tick, err := cmd.Flags().GetDuration("tick") if err != nil { return err } - vSockPortOverride, err := cmd.Flags().GetInt("vsock-port") + vSockPort, err := cmd.Flags().GetInt("vsock-port") if err != nil { return err } - vSockPort := 0 - if vSockPortOverride != 0 { - vSockPort = vSockPortOverride + virtioPort, err := cmd.Flags().GetString("virtio-port") + if err != nil { + return err } if tick == 0 { return errors.New("tick must be specified") @@ -67,11 +68,14 @@ 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 - virtioPort := "/dev/virtio-ports/" + filenames.VirtioPort - if _, err := os.Stat(virtioPort); err == nil { - qemuL, err := serialport.Listen(virtioPort) + if virtioPort != "" { + qemuL, err := serialport.Listen("/dev/virtio-ports/" + virtioPort) if err != nil { return err } @@ -84,6 +88,16 @@ func daemonAction(cmd *cobra.Command, _ []string) error { } l = vsockL logrus.Infof("serving the guest agent on vsock port: %d", vSockPort) + } else { + socketL, err := net.Listen("unix", socket) + 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) } return srv.Serve(l) } diff --git a/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh b/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh index 968fc20e06f5..04d97c64a8ed 100644 --- a/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh +++ b/pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh @@ -28,7 +28,7 @@ name="lima-guestagent" description="Forward ports to the lima-hostagent" command=${LIMA_CIDATA_GUEST_INSTALL_PREFIX}/bin/lima-guestagent -command_args="daemon --vsock-port "${LIMA_CIDATA_VSOCK_PORT}"" +command_args="daemon --vsock-port "${LIMA_CIDATA_VSOCK_PORT}" --virtio-port "${LIMA_CIDATA_VIRTIO_PORT}"" command_background=true pidfile="/run/lima-guestagent.pid" EOF @@ -40,5 +40,11 @@ else # Remove legacy systemd service rm -f "${LIMA_CIDATA_HOME}/.config/systemd/user/lima-guestagent.service" - sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd --vsock-port "${LIMA_CIDATA_VSOCK_PORT}" + if [ "${LIMA_CIDATA_VSOCK_PORT}" != "0" ]; then + sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd --vsock-port "${LIMA_CIDATA_VSOCK_PORT}" + elif [ "${LIMA_CIDATA_VIRTIO_PORT}" != "" ]; then + sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd --virtio-port "${LIMA_CIDATA_VIRTIO_PORT}" + else + sudo "${LIMA_CIDATA_GUEST_INSTALL_PREFIX}"/bin/lima-guestagent install-systemd + fi fi diff --git a/pkg/cidata/cidata.TEMPLATE.d/lima.env b/pkg/cidata/cidata.TEMPLATE.d/lima.env index e43a33291752..c0c8c7d11ee3 100644 --- a/pkg/cidata/cidata.TEMPLATE.d/lima.env +++ b/pkg/cidata/cidata.TEMPLATE.d/lima.env @@ -41,6 +41,7 @@ LIMA_CIDATA_SKIP_DEFAULT_DEPENDENCY_RESOLUTION= {{- end}} LIMA_CIDATA_VMTYPE={{ .VMType }} LIMA_CIDATA_VSOCK_PORT={{ .VSockPort }} +LIMA_CIDATA_VIRTIO_PORT={{ .VirtioPort}} {{- if .Plain}} LIMA_CIDATA_PLAIN=1 {{- else}} diff --git a/pkg/cidata/cidata.go b/pkg/cidata/cidata.go index 40d8187f7a03..9c7342b6a1b6 100644 --- a/pkg/cidata/cidata.go +++ b/pkg/cidata/cidata.go @@ -110,7 +110,7 @@ func setupEnv(y *limayaml.LimaYAML, args TemplateArgs) (map[string]string, error return env, nil } -func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML, udpDNSLocalPort, tcpDNSLocalPort int, nerdctlArchive string, vsockPort int) error { +func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML, udpDNSLocalPort, tcpDNSLocalPort int, nerdctlArchive string, vsockPort int, virtioPort string) error { if err := limayaml.Validate(*y, false); err != nil { return err } @@ -135,6 +135,7 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML, udpDNSLocalPort RosettaBinFmt: *y.Rosetta.BinFmt, VMType: *y.VMType, VSockPort: vsockPort, + VirtioPort: virtioPort, Plain: *y.Plain, } diff --git a/pkg/cidata/template.go b/pkg/cidata/template.go index 089a085f76ba..649220117710 100644 --- a/pkg/cidata/template.go +++ b/pkg/cidata/template.go @@ -81,6 +81,7 @@ type TemplateArgs struct { SkipDefaultDependencyResolution bool VMType string VSockPort int + VirtioPort string Plain bool } diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go index 8677249f458a..d6049c7f40e7 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go @@ -64,6 +64,10 @@ type Driver interface { ListSnapshots(_ context.Context) (string, error) + // ForwardGuestAgent returns if the guest agent sock needs forwarding by host agent. + ForwardGuestAgent() bool + + // GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh). GuestAgentConn(_ context.Context) (net.Conn, error) } @@ -73,6 +77,7 @@ type BaseDriver struct { SSHLocalPort int VSockPort int + VirtioPort string } var _ Driver = (*BaseDriver)(nil) @@ -137,6 +142,12 @@ func (d *BaseDriver) ListSnapshots(_ context.Context) (string, error) { return "", fmt.Errorf("unimplemented") } +func (d *BaseDriver) ForwardGuestAgent() bool { + // if driver is not providing, use host agent + return d.VSockPort == 0 && d.VirtioPort == "" +} + func (d *BaseDriver) GuestAgentConn(_ context.Context) (net.Conn, error) { - return nil, fmt.Errorf("unimplemented") + // use the unix socket forwarded by host agent + return nil, nil } diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go index b7f59fefbccd..681aef866c69 100644 --- a/pkg/hostagent/hostagent.go +++ b/pkg/hostagent/hostagent.go @@ -53,7 +53,8 @@ type HostAgent struct { eventEnc *json.Encoder eventEncMu sync.Mutex - vSockPort int + vSockPort int + virtioPort string clientMu sync.RWMutex client guestagentclient.GuestAgentClient @@ -117,6 +118,7 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt } vSockPort := 0 + virtioPort := "" if *y.VMType == limayaml.VZ { vSockPort = 2222 } else if *y.VMType == limayaml.WSL2 { @@ -125,9 +127,11 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt logrus.WithError(err).Error("failed to get free VSock port") } vSockPort = port + } else if *y.VMType == limayaml.QEMU { + virtioPort = filenames.VirtioPort } - if err := cidata.GenerateISO9660(inst.Dir, instName, y, udpDNSLocalPort, tcpDNSLocalPort, o.nerdctlArchive, vSockPort); err != nil { + if err := cidata.GenerateISO9660(inst.Dir, instName, y, udpDNSLocalPort, tcpDNSLocalPort, o.nerdctlArchive, vSockPort, virtioPort); err != nil { return nil, err } @@ -160,6 +164,7 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt Yaml: y, SSHLocalPort: sshLocalPort, VSockPort: vSockPort, + VirtioPort: virtioPort, }) a := &HostAgent{ @@ -176,6 +181,7 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt sigintCh: sigintCh, eventEnc: json.NewEncoder(stdout), vSockPort: vSockPort, + virtioPort: virtioPort, guestAgentAliveCh: make(chan struct{}), } return a, nil @@ -548,8 +554,6 @@ func (a *HostAgent) close() error { } func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) { - // TODO: use vSock (when QEMU for macOS gets support for vSock) - // Setup all socket forwards and defer their teardown if *a.y.VMType != limayaml.WSL2 { logrus.Debugf("Forwarding unix sockets") @@ -561,6 +565,9 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) { } } + localUnix := filepath.Join(a.instDir, filenames.GuestAgentSock) + remoteUnix := "/run/lima-guestagent.sock" + a.onClose = append(a.onClose, func() error { logrus.Debugf("Stop forwarding unix sockets") var errs []error @@ -573,9 +580,19 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) { } } } + if a.driver.ForwardGuestAgent() { + if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel, false); err != nil { + errs = append(errs, err) + } + } return errors.Join(errs...) }) for { + if a.client == nil || !isGuestAgentSocketAccessible(ctx, a.client) { + if a.driver.ForwardGuestAgent() { + _ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward, false) + } + } client, err := a.getOrCreateClient(ctx) if err == nil { if err := a.processGuestAgentEvents(ctx, client); err != nil { @@ -610,8 +627,18 @@ func (a *HostAgent) getOrCreateClient(ctx context.Context) (guestagentclient.Gue return a.client, err } -func (a *HostAgent) createClient(ctx context.Context) (guestagentclient.GuestAgentClient, error) { +func (a *HostAgent) createConnection(ctx context.Context) (net.Conn, error) { conn, err := a.driver.GuestAgentConn(ctx) + // default to forwarded sock + if conn == nil && err == nil { + var d net.Dialer + conn, err = d.DialContext(ctx, "unix", filepath.Join(a.instDir, filenames.GuestAgentSock)) + } + return conn, err +} + +func (a *HostAgent) createClient(ctx context.Context) (guestagentclient.GuestAgentClient, error) { + conn, err := a.createConnection(ctx) if err != nil { return nil, err } diff --git a/website/content/en/docs/dev/Internals/_index.md b/website/content/en/docs/dev/Internals/_index.md index add318d24cbb..f4151b295908 100644 --- a/website/content/en/docs/dev/Internals/_index.md +++ b/website/content/en/docs/dev/Internals/_index.md @@ -72,10 +72,12 @@ VNC: Guest agent: -Each drivers use their own mode of communication +Each drivers use their own mode of communication - `qemu`: uses virtio-port `io.lima-vm.guest_agent.0` - `vz`: uses vsock port 2222 - `wsl2`: uses free random vsock port +The fallback is to use port forward over ssh port +- `ga.sock`: Forwarded to `/run/lima-guestagent.sock` in the guest, via SSH Host agent: - `ha.pid`: hostagent PID