Skip to content

Allow running agents with tcp as well as unix #980

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions cmd/lima-guestagent/daemon_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"errors"
"fmt"
"net"
"net/http"
"os"
Expand All @@ -21,11 +22,18 @@ func newDaemonCommand() *cobra.Command {
RunE: daemonAction,
}
daemonCommand.Flags().Duration("tick", 3*time.Second, "tick for polling events")
daemonCommand.Flags().Int("port", 0, "tcp port")
return daemonCommand
}

func daemonAction(cmd *cobra.Command, args []string) error {
unix := true
socket := "/run/lima-guestagent.sock"
port, err := cmd.Flags().GetInt("port")
if err == nil && port != 0 {
unix = false
socket = fmt.Sprintf("127.0.0.1:%d", port)
}
tick, err := cmd.Flags().GetDuration("tick")
if err != nil {
return err
Expand Down Expand Up @@ -56,16 +64,24 @@ func daemonAction(cmd *cobra.Command, args []string) error {
r := mux.NewRouter()
server.AddRoutes(r, backend)
srv := &http.Server{Handler: r}
err = os.RemoveAll(socket)
if err != nil {
return err
}
l, err := net.Listen("unix", socket)
if err != nil {
return err
}
if err := os.Chmod(socket, 0777); err != nil {
return err
var l net.Listener
if unix {
err = os.RemoveAll(socket)
if err != nil {
return err
}
l, err = net.Listen("unix", socket)
if err != nil {
return err
}
if err := os.Chmod(socket, 0777); err != nil {
return err
}
} else {
l, err = net.Listen("tcp4", socket)
if err != nil {
return err
}
}
logrus.Infof("serving the guest agent on %q", socket)
return srv.Serve(l)
Expand Down
15 changes: 13 additions & 2 deletions cmd/lima-guestagent/install_systemd_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
_ "embed"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
Expand All @@ -19,11 +20,16 @@ func newInstallSystemdCommand() *cobra.Command {
Short: "install a systemd unit (user)",
RunE: installSystemdAction,
}
installSystemdCommand.Flags().Int("port", 0, "guestagent tcp port")
return installSystemdCommand
}

func installSystemdAction(cmd *cobra.Command, args []string) error {
unit, err := generateSystemdUnit()
port, err := cmd.Flags().GetInt("port")
if err != nil {
return err
}
unit, err := generateSystemdUnit(port)
if err != nil {
return err
}
Expand Down Expand Up @@ -60,13 +66,18 @@ func installSystemdAction(cmd *cobra.Command, args []string) error {
//go:embed lima-guestagent.TEMPLATE.service
var systemdUnitTemplate string

func generateSystemdUnit() ([]byte, error) {
func generateSystemdUnit(port int) ([]byte, error) {
selfExeAbs, err := os.Executable()
if err != nil {
return nil, err
}
portString := ""
if port != 0 {
portString = fmt.Sprintf("%d", port)
}
m := map[string]string{
"Binary": selfExeAbs,
"Port": portString,
}
return templateutil.Execute(systemdUnitTemplate, m)
}
2 changes: 1 addition & 1 deletion cmd/lima-guestagent/lima-guestagent.TEMPLATE.service
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Description=lima-guestagent

[Service]
ExecStart={{.Binary}} daemon
ExecStart={{.Binary}} daemon{{- if .Port}} --port {{.Port}}{{- end}}
Type=simple
Restart=on-failure

Expand Down
36 changes: 27 additions & 9 deletions cmd/limactl/hostagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func newHostagentCommand() *cobra.Command {
}
hostagentCommand.Flags().StringP("pidfile", "p", "", "write pid to file")
hostagentCommand.Flags().String("socket", "", "hostagent socket")
hostagentCommand.Flags().Int("port", 0, "hostagent tcp port")
hostagentCommand.Flags().String("nerdctl-archive", "", "local file path (not URL) of nerdctl-full-VERSION-linux-GOARCH.tar.gz")
return hostagentCommand
}
Expand All @@ -45,12 +46,21 @@ func hostagentAction(cmd *cobra.Command, args []string) error {
}
defer os.RemoveAll(pidfile)
}
unix := true
socket, err := cmd.Flags().GetString("socket")
if err != nil {
return err
}
if socket == "" {
return fmt.Errorf("socket must be specified (limactl version mismatch?)")
port, err := cmd.Flags().GetInt("port")
if err != nil {
return err
}
if socket == "" && port == 0 {
return fmt.Errorf("socket or port must be specified (limactl version mismatch?)")
}
if port != 0 {
unix = false
socket = fmt.Sprintf(":%d", port)
}

instName := args[0]
Expand Down Expand Up @@ -81,13 +91,21 @@ func hostagentAction(cmd *cobra.Command, args []string) error {
r := mux.NewRouter()
server.AddRoutes(r, backend)
srv := &http.Server{Handler: r}
err = os.RemoveAll(socket)
if err != nil {
return err
}
l, err := net.Listen("unix", socket)
if err != nil {
return err
var l net.Listener
if unix {
err = os.RemoveAll(socket)
if err != nil {
return err
}
l, err = net.Listen("unix", socket)
if err != nil {
return err
}
} else {
l, err = net.Listen("tcp", socket)
if err != nil {
return err
}
}
go func() {
defer os.RemoveAll(socket)
Expand Down
12 changes: 9 additions & 3 deletions pkg/cidata/cidata.TEMPLATE.d/boot/25-guestagent-base.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,23 @@ fi
# Install or update the guestagent binary
install -m 755 "${LIMA_CIDATA_MNT}"/lima-guestagent /usr/local/bin/lima-guestagent

port=""
if [ -n "${LIMA_CIDATA_GUEST_AGENT_PORT}" ]; then
port=" --port ${LIMA_CIDATA_GUEST_AGENT_PORT}"
fi

# Launch the guestagent service
if [ -f /sbin/openrc-run ]; then
# Install the openrc lima-guestagent service script
cat >/etc/init.d/lima-guestagent <<'EOF'
cat >/etc/init.d/lima-guestagent <<EOF
#!/sbin/openrc-run
supervisor=supervise-daemon

name="lima-guestagent"
description="Forward ports to the lima-hostagent"

command=/usr/local/bin/lima-guestagent
command_args="daemon"
command_args="daemon$port"
command_background=true
pidfile="/run/lima-guestagent.pid"
EOF
Expand All @@ -40,5 +45,6 @@ else
# Remove legacy systemd service
rm -f "/home/${LIMA_CIDATA_USER}.linux/.config/systemd/user/lima-guestagent.service"

sudo /usr/local/bin/lima-guestagent install-systemd
# shellcheck disable=SC2086
sudo /usr/local/bin/lima-guestagent install-systemd$port
fi
3 changes: 3 additions & 0 deletions pkg/cidata/cidata.TEMPLATE.d/lima.env
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ LIMA_CIDATA_SLIRP_GATEWAY={{.SlirpGateway}}
LIMA_CIDATA_SLIRP_IP_ADDRESS={{.SlirpIPAddress}}
LIMA_CIDATA_UDP_DNS_LOCAL_PORT={{.UDPDNSLocalPort}}
LIMA_CIDATA_TCP_DNS_LOCAL_PORT={{.TCPDNSLocalPort}}
{{- if .GuestAgentPort}}
LIMA_CIDATA_GUEST_AGENT_PORT={{.GuestAgentPort}}
{{- end}}
5 changes: 5 additions & 0 deletions pkg/cidata/cidata.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -219,6 +220,10 @@ func GenerateISO9660(instDir, name string, y *limayaml.LimaYAML, udpDNSLocalPort
args.CACerts.Trusted = append(args.CACerts.Trusted, cert)
}

if runtime.GOOS == "windows" {
args.GuestAgentPort = 1111
}

if err := ValidateTemplateArgs(args); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/cidata/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type TemplateArgs struct {
SlirpIPAddress string
UDPDNSLocalPort int
TCPDNSLocalPort int
GuestAgentPort int
Env map[string]string
DNSAddresses []string
CACerts CACerts
Expand Down
31 changes: 21 additions & 10 deletions pkg/hostagent/api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strconv"

"github.com/lima-vm/lima/pkg/hostagent/api"
"github.com/lima-vm/lima/pkg/httpclientutil"
Expand All @@ -21,35 +22,45 @@ type HostAgentClient interface {
// NewHostAgentClient creates a client.
// socketPath is a path to the UNIX socket, without unix:// prefix.
func NewHostAgentClient(socketPath string) (HostAgentClient, error) {
hc, err := httpclientutil.NewHTTPClientWithSocketPath(socketPath)
port, err := strconv.Atoi(socketPath)
if err != nil {
return nil, err
hc, err := httpclientutil.NewHTTPClientWithSocketPath(socketPath)
if err != nil {
return nil, err
}
return NewHostAgentClientWithHTTPClient(hc, "lima-hostagent"), nil
} else {
hc, err := httpclientutil.NewHTTPClient()
if err != nil {
return nil, err
}
address := fmt.Sprintf("127.0.0.1:%d", port)
return NewHostAgentClientWithHTTPClient(hc, address), nil
}
return NewHostAgentClientWithHTTPClient(hc), nil
}

func NewHostAgentClientWithHTTPClient(hc *http.Client) HostAgentClient {
func NewHostAgentClientWithHTTPClient(hc *http.Client, address string) HostAgentClient {
return &client{
Client: hc,
version: "v1",
dummyHost: "lima-hostagent",
Client: hc,
version: "v1",
address: address,
}
}

type client struct {
*http.Client
// version is always "v1"
// TODO(AkihiroSuda): negotiate the version
version string
dummyHost string
version string
address string
}

func (c *client) HTTPClient() *http.Client {
return c.Client
}

func (c *client) Info(ctx context.Context) (*api.Info, error) {
u := fmt.Sprintf("http://%s/%s/info", c.dummyHost, c.version)
u := fmt.Sprintf("http://%s/%s/info", c.address, c.version)
resp, err := httpclientutil.Get(ctx, c.HTTPClient(), u)
if err != nil {
return nil, err
Expand Down
46 changes: 39 additions & 7 deletions pkg/hostagent/hostagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -427,20 +428,41 @@ func (a *HostAgent) close() error {
func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
// TODO: use vSock (when QEMU for macOS gets support for vSock)

// Prefer unix sockets over tcp
unix := runtime.GOOS != "windows"

// Setup all socket forwards and defer their teardown
logrus.Debugf("Forwarding unix sockets")
if unix {
logrus.Debugf("Forwarding unix sockets")
}
for _, rule := range a.y.PortForwards {
if rule.GuestSocket != "" {
local := hostAddress(rule, guestagentapi.IPPort{})
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, local, rule.GuestSocket, verbForward, rule.Reverse)
}
}

localUnix := filepath.Join(a.instDir, filenames.GuestAgentSock)
remoteUnix := "/run/lima-guestagent.sock"
var localUnix, remoteUnix string
var localPort, remotePort int
if unix {
localUnix = filepath.Join(a.instDir, filenames.GuestAgentSock)
remoteUnix = "/run/lima-guestagent.sock"
} else {
localPort, _ = findFreeTCPLocalPort()
localUnix = fmt.Sprintf("127.0.0.1:%d", localPort)
logrus.Debugf("Guest Agent Port is %d", localPort)
portfile := filepath.Join(a.instDir, filenames.GuestAgentPort)
if err := os.WriteFile(portfile, []byte(strconv.Itoa(localPort)+"\n"), 0644); err != nil {
logrus.WithError(err).Warn("could not write guest agent port file")
}
remotePort = 1111
remoteUnix = fmt.Sprintf("127.0.0.1:%d", remotePort)
}

a.onClose = append(a.onClose, func() error {
logrus.Debugf("Stop forwarding unix sockets")
if unix {
logrus.Debugf("Stop forwarding unix sockets")
}
var mErr error
for _, rule := range a.y.PortForwards {
if rule.GuestSocket != "" {
Expand All @@ -451,15 +473,25 @@ func (a *HostAgent) watchGuestAgentEvents(ctx context.Context) {
}
}
}
if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel, false); err != nil {
mErr = multierror.Append(mErr, err)
if unix {
if err := forwardSSH(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel, false); err != nil {
mErr = multierror.Append(mErr, err)
}
} else {
if err := forwardTCP(context.Background(), a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbCancel); err != nil {
mErr = multierror.Append(mErr, err)
}
}
return mErr
})

for {
if !isGuestAgentSocketAccessible(ctx, localUnix) {
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward, false)
if unix {
_ = forwardSSH(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward, false)
} else {
_ = forwardTCP(ctx, a.sshConfig, a.sshLocalPort, localUnix, remoteUnix, verbForward)
}
}
if err := a.processGuestAgentEvents(ctx, localUnix); err != nil {
if !errors.Is(err, context.Canceled) {
Expand Down
Loading