From 030b7518103cfd7b930b54744d4a4510b659fdc2 Mon Sep 17 00:00:00 2001 From: Filipe Brandenburger Date: Sun, 21 Aug 2022 18:41:32 -0700 Subject: [PATCH 1/2] config: Allow specifying helper dirs with $BINDIR as base directory This should make it easier to locate helper binaries relative to where the main binary was installed, which should be useful in installations such as Homebrew which install packages under a versioned directory. Use a `$BINDIR` magic token as a prefix in the helper path to indicate it should be relative to the directory where the binary is located. This is somewhat familiar to the syntax used in the shell and Makefile and is still quite explicit about the behavior (as opposed to, say, making all relative paths be relative to the directory of the binary.) Tested: After updating the Darwin config to include a `$BINDIR/../libexec/podman` directory, updated vendored package in podman, built it with `podman-remote`, copied `gvproxy` to a `libexec/podman` at the same level as `bin/podman` and confirmed that `podman machine start` worked as expected. Also confirmed that having the `podman` in search path be a symlink to a binary elsewhere works as expected, the searched `../libexec/podman` directory is relative to the actual binary and not the symlink (which matches the Homebrew use case.) Signed-off-by: Filipe Brandenburger --- pkg/config/config.go | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/pkg/config/config.go b/pkg/config/config.go index 6db2e0ff8..b9ec57e56 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -27,6 +27,8 @@ const ( _configPath = "containers/containers.conf" // UserOverrideContainersConfig holds the containers config path overridden by the rootless user UserOverrideContainersConfig = ".config/" + _configPath + // Token prefix for looking for helper binary under $BINDIR + bindirPrefix = "$BINDIR" ) // RuntimeStateStore is a constant indicating which state store implementation @@ -1248,10 +1250,37 @@ func (c *Config) ActiveDestination() (uri, identity string, err error) { return "", "", errors.New("no service destination configured") } +var ( + bindirFailed = false + bindirCached = "" +) + +func findBindir() string { + if bindirCached != "" || bindirFailed { + return bindirCached + } + execPath, err := os.Executable() + if err == nil { + // Resolve symbolic links to find the actual binary file path. + execPath, err = filepath.EvalSymlinks(execPath) + } + if err != nil { + // If failed to find executable (unlikely to happen), warn about it. + // The bindirFailed flag will track this, so we only warn once. + logrus.Warnf("Failed to find $BINDIR: %v", err) + bindirFailed = true + return "" + } + bindirCached = filepath.Dir(execPath) + return bindirCached +} + // FindHelperBinary will search the given binary name in the configured directories. // If searchPATH is set to true it will also search in $PATH. func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error) { dirList := c.Engine.HelperBinariesDir + bindirPath := "" + bindirSearched := false // If set, search this directory first. This is used in testing. if dir, found := os.LookupEnv("CONTAINERS_HELPER_BINARY_DIR"); found { @@ -1259,6 +1288,24 @@ func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error) } for _, path := range dirList { + if path == bindirPrefix || strings.HasPrefix(path, bindirPrefix+string(filepath.Separator)) { + // Calculate the path to the executable first time we encounter a $BINDIR prefix. + if !bindirSearched { + bindirSearched = true + bindirPath = findBindir() + } + // If there's an error, don't stop the search for the helper binary. + // findBindir() will have warned once during the first failure. + if bindirPath == "" { + continue + } + // Replace the $BINDIR prefix with the path to the directory of the current binary. + if path == bindirPrefix { + path = bindirPath + } else { + path = filepath.Join(bindirPath, strings.TrimPrefix(path, bindirPrefix+string(filepath.Separator))) + } + } fullpath := filepath.Join(path, name) if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() { return fullpath, nil From 4e6828877b0067557b435ec8810bda7f5cb48a4f Mon Sep 17 00:00:00 2001 From: Filipe Brandenburger Date: Mon, 22 Aug 2022 10:50:10 -0700 Subject: [PATCH 2/2] config: Update Darwin config to include a directory relative to $BINDIR Look for helpers such as `gvproxy` under `$BINDIR/../libexec/podman`, which helps this work on distributions such as Homebrew without the need to hardcode paths into the binary, which makes a pre-built binary distribution work regardless of the installation prefix. Tested: Updated vendored package in podman, built it with `podman-remote`, copied `gvproxy` to a `libexec/podman` at the same level as `bin/podman` and confirmed that `podman machine start` worked as expected. Signed-off-by: Filipe Brandenburger --- pkg/config/config_darwin.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/config/config_darwin.go b/pkg/config/config_darwin.go index 0ab9e0294..5283665e1 100644 --- a/pkg/config/config_darwin.go +++ b/pkg/config/config_darwin.go @@ -35,4 +35,6 @@ var defaultHelperBinariesDir = []string{ "/usr/local/lib/podman", "/usr/libexec/podman", "/usr/lib/podman", + // Relative to the binary directory + "$BINDIR/../libexec/podman", }