Skip to content

Commit 955222b

Browse files
committed
Locate the limactl executable via os.Args[0] instead of os.Executable()
We need the symlink directory to find the the ../share/lima directory relative to it. os.Executable() uses /proc/self/exe on Linux and returns the symlink target, which will break things with a Homebrew installation. This commit also removes the dead code that pretends to resolve any symlinks. It never worked because it called os.Stat() instead of os.Lstat(). If it had worked, then code would have been broken on macOS too. Signed-off-by: Jan Dubois <[email protected]>
1 parent ca17b25 commit 955222b

File tree

1 file changed

+30
-12
lines changed

1 file changed

+30
-12
lines changed

pkg/usrlocalsharelima/usrlocalsharelima.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,47 @@ import (
88
"fmt"
99
"io/fs"
1010
"os"
11+
"os/exec"
1112
"path/filepath"
1213
"runtime"
14+
"sync"
1315

1416
"github.com/lima-vm/lima/pkg/debugutil"
1517
"github.com/lima-vm/lima/pkg/limayaml"
1618
"github.com/sirupsen/logrus"
1719
)
1820

19-
func Dir() (string, error) {
20-
self, err := os.Executable()
21-
if err != nil {
22-
return "", err
21+
// executableViaArgs0 returns the absolute path to the executable used to start this process.
22+
// It will also append the file extension on Windows, if necessary.
23+
// This function is different from os.Executable(), which will use /proc/self/exe on Linux
24+
// and therefore will resolve any symlink used to locate the executable. This function will
25+
// return the symlink instead because we want to locate ../share/lima relative to the location
26+
// of the symlink, and not the actual executable. This is important when using Homebrew.
27+
//
28+
// If os.Args[0] is invalid, this function still falls back on os.Executable().
29+
var executableViaArgs0 = sync.OnceValues(func() (string, error) {
30+
if os.Args[0] == "" {
31+
logrus.Warn("os.Args[0] has not been set")
32+
} else {
33+
executable, err := exec.LookPath(os.Args[0])
34+
if err == nil {
35+
// LookPath() will add the `.exe` file extension on Windows, but will not return an
36+
// absolute path if the argument contained any of `:/\` (or just `/` on Unix).
37+
return filepath.Abs(executable)
38+
}
39+
logrus.Warnf("os.Args[0] is invalid: %v", err)
2340
}
24-
selfSt, err := os.Stat(self)
41+
return os.Executable()
42+
})
43+
44+
// Dir returns the location of the <PREFIX>/lima/share directory, relative to the location
45+
// of the current executable. It checks for multiple possible filesystem layouts and returns
46+
// the first candidate that contains the native guest agent binary.
47+
func Dir() (string, error) {
48+
self, err := executableViaArgs0()
2549
if err != nil {
2650
return "", err
2751
}
28-
if selfSt.Mode()&fs.ModeSymlink != 0 {
29-
self, err = os.Readlink(self)
30-
if err != nil {
31-
return "", err
32-
}
33-
}
3452

3553
ostype := limayaml.NewOS("linux")
3654
arch := limayaml.NewArch(runtime.GOARCH)
@@ -80,7 +98,7 @@ func Dir() (string, error) {
8098
ostype, arch, self, gaCandidates)
8199
}
82100

83-
// GuestAgentBinary returns the guest agent binary, possibly with ".gz" suffix.
101+
// GuestAgentBinary returns the absolute path of the guest agent binary, possibly with ".gz" suffix.
84102
func GuestAgentBinary(ostype limayaml.OS, arch limayaml.Arch) (string, error) {
85103
if ostype == "" {
86104
return "", errors.New("os must be set")

0 commit comments

Comments
 (0)