diff --git a/cmd/limactl/start.go b/cmd/limactl/start.go index 0d78bc1e0cd..ede6f4c1712 100644 --- a/cmd/limactl/start.go +++ b/cmd/limactl/start.go @@ -329,6 +329,7 @@ func chooseNextCreatorState(ctx context.Context, tmpl *limatmpl.Template, yq str hdr += editutil.GenerateEditorWarningHeader() var err error tmpl.Bytes, err = editutil.OpenEditor(tmpl.Bytes, hdr) + tmpl.Config = nil if err != nil { return tmpl, err } @@ -337,6 +338,10 @@ func chooseNextCreatorState(ctx context.Context, tmpl *limatmpl.Template, yq str logrus.Info(msg) return nil, exitSuccessError{Msg: msg} } + err = tmpl.Embed(ctx, true, true) + if err != nil { + return nil, err + } return tmpl, nil case 2: // "Choose another template..." templates, err := filterHiddenTemplates() diff --git a/pkg/instance/start.go b/pkg/instance/start.go index e465fa2755f..960b1ae1ae9 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -12,13 +12,11 @@ import ( "os" "os/exec" "path/filepath" - "syscall" "text/template" "time" "github.com/docker/go-units" "github.com/lima-vm/go-qcow2reader" - "github.com/mattn/go-isatty" "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/downloader" @@ -29,7 +27,6 @@ import ( hostagentevents "github.com/lima-vm/lima/pkg/hostagent/events" "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/limayaml" - "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/usrlocalsharelima" @@ -249,25 +246,7 @@ func Start(ctx context.Context, inst *store.Instance, limactl string, launchHost begin := time.Now() // used for logrus propagation if launchHostAgentForeground { - logrus.Info("Running the host agent in the foreground") - if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { - // Write message to standard log files to avoid confusing users - message := "This log file is not used because `limactl start` was launched in the terminal with the `--foreground` option." - if _, err := haStdoutW.WriteString(message); err != nil { - return err - } - if _, err := haStderrW.WriteString(message); err != nil { - return err - } - } else { - if err := osutil.Dup2(int(haStdoutW.Fd()), syscall.Stdout); err != nil { - return err - } - if err := osutil.Dup2(int(haStderrW.Fd()), syscall.Stderr); err != nil { - return err - } - } - if err := syscall.Exec(limactl, haCmd.Args, haCmd.Environ()); err != nil { + if err := execHostAgentForeground(limactl, haCmd); err != nil { return err } } else if err := haCmd.Start(); err != nil { diff --git a/pkg/instance/start_unix.go b/pkg/instance/start_unix.go new file mode 100644 index 00000000000..e1c44d71dd0 --- /dev/null +++ b/pkg/instance/start_unix.go @@ -0,0 +1,48 @@ +//go:build !windows + +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package instance + +import ( + "fmt" + "os" + "os/exec" + "syscall" + + "github.com/mattn/go-isatty" + "github.com/sirupsen/logrus" + + "github.com/lima-vm/lima/pkg/osutil" +) + +func execHostAgentForeground(limactl string, haCmd *exec.Cmd) error { + haStdoutW, ok := haCmd.Stdout.(*os.File) + if !ok { + return fmt.Errorf("expected haCmd.Stdout to be *os.File, got %T", haCmd.Stdout) + } + haStderrW, ok := haCmd.Stderr.(*os.File) + if !ok { + return fmt.Errorf("expected haCmd.Stderr to be *os.File, got %T", haCmd.Stderr) + } + logrus.Info("Running the host agent in the foreground") + if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) { + // Write message to standard log files to avoid confusing users + message := "This log file is not used because `limactl start` was launched in the terminal with the `--foreground` option." + if _, err := haStdoutW.WriteString(message); err != nil { + return err + } + if _, err := haStderrW.WriteString(message); err != nil { + return err + } + } else { + if err := osutil.Dup2(int(haStdoutW.Fd()), syscall.Stdout); err != nil { + return err + } + if err := osutil.Dup2(int(haStderrW.Fd()), syscall.Stderr); err != nil { + return err + } + } + return syscall.Exec(limactl, haCmd.Args, haCmd.Environ()) +} diff --git a/pkg/instance/start_windows.go b/pkg/instance/start_windows.go new file mode 100644 index 00000000000..2f85c03aec9 --- /dev/null +++ b/pkg/instance/start_windows.go @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package instance + +import ( + "errors" + "os/exec" +) + +func execHostAgentForeground(_ string, _ *exec.Cmd) error { + return errors.New("`limactl start --foreground` is not supported on Windows") +} diff --git a/pkg/portfwd/client.go b/pkg/portfwd/client.go index 81c5c21e17b..944032945a4 100644 --- a/pkg/portfwd/client.go +++ b/pkg/portfwd/client.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "net" + "sync/atomic" "time" "github.com/containers/gvisor-tap-vsock/pkg/services/forwarder" @@ -40,33 +41,35 @@ func HandleTCPConnection(ctx context.Context, client *guestagentclient.GuestAgen } func HandleUDPConnection(ctx context.Context, client *guestagentclient.GuestAgentClient, conn net.PacketConn, guestAddr string) { - id := fmt.Sprintf("udp-%s", conn.LocalAddr().String()) - - stream, err := client.Tunnel(ctx) - if err != nil { - logrus.Errorf("could not open udp tunnel for id: %s error:%v", id, err) - return - } - - // Handshake message to start tunnel - if err := stream.Send(&api.TunnelMessage{Id: id, Protocol: "udp", GuestAddr: guestAddr}); err != nil { - logrus.Errorf("could not start udp tunnel for id: %s error:%v", id, err) - return - } + var udpConnectionCounter atomic.Uint32 + initialID := fmt.Sprintf("udp-%s", conn.LocalAddr().String()) + // gvisor-tap-vsock's UDPProxy demultiplexes client connections internally based on their source address. + // It calls this dialer function only when it receives a datagram from a new, unrecognized client. + // For each new client, we must return a new net.Conn, which in our case is a new gRPC stream. + // The atomic counter ensures that each stream has a unique ID to distinguish them on the server side. proxy, err := forwarder.NewUDPProxy(conn, func() (net.Conn, error) { + id := fmt.Sprintf("%s-%d", initialID, udpConnectionCounter.Add(1)) + stream, err := client.Tunnel(ctx) + if err != nil { + return nil, fmt.Errorf("could not open udp tunnel for id: %s error:%w", id, err) + } + // Handshake message to start tunnel + if err := stream.Send(&api.TunnelMessage{Id: id, Protocol: "udp", GuestAddr: guestAddr}); err != nil { + return nil, fmt.Errorf("could not start udp tunnel for id: %s error:%w", id, err) + } rw := &GrpcClientRW{stream: stream, id: id, addr: guestAddr, protocol: "udp"} return rw, nil }) if err != nil { - logrus.Errorf("error in udp tunnel proxy for id: %s error:%v", id, err) + logrus.Errorf("error in udp tunnel proxy for id: %s error:%v", initialID, err) return } defer func() { err := proxy.Close() if err != nil { - logrus.Errorf("error in closing udp tunnel proxy for id: %s error:%v", id, err) + logrus.Errorf("error in closing udp tunnel proxy for id: %s error:%v", initialID, err) } }() proxy.Run() diff --git a/pkg/qemuimgutil/qemuimgutil.go b/pkg/qemuimgutil/qemuimgutil.go index fc065450c59..8872eb56d52 100644 --- a/pkg/qemuimgutil/qemuimgutil.go +++ b/pkg/qemuimgutil/qemuimgutil.go @@ -103,6 +103,31 @@ type InfoFormatSpecificDataVmdkExtent struct { } func convertToRaw(source, dest string) error { + if source != dest { + return execQemuImgConvert(source, dest) + } + + // If source == dest, we need to use a temporary file to avoid file locking issues + + info, err := getInfo(source) + if err != nil { + return fmt.Errorf("failed to get info for source disk %q: %w", source, err) + } + if info.Format == "raw" { + return nil + } + + tempFile := dest + ".lima-qemu-convert.tmp" + defer os.Remove(tempFile) + + if err := execQemuImgConvert(source, tempFile); err != nil { + return err + } + + return os.Rename(tempFile, dest) +} + +func execQemuImgConvert(source, dest string) error { var stdout, stderr bytes.Buffer cmd := exec.Command("qemu-img", "convert", "-O", "raw", source, dest) cmd.Stdout = &stdout