Skip to content

Commit 44f3da4

Browse files
authored
Merge pull request #3566 from jandubois/executable-symlink
Locate the limactl executable via os.Args[0] instead of os.Executable()
2 parents ca17b25 + 955222b commit 44f3da4

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)