diff --git a/daemon/config/config_linux.go b/daemon/config/config_linux.go index f505df6539..3fd8315fc8 100644 --- a/daemon/config/config_linux.go +++ b/daemon/config/config_linux.go @@ -118,6 +118,34 @@ func (conf *Config) GetInitPath() string { return DefaultInitBinary } +// LookupInitPath returns an absolute path to the "docker-init" binary by searching relevant "libexec" directories (per FHS 3.0 & 2.3) followed by PATH +func (conf *Config) LookupInitPath() (string, error) { + binary := conf.GetInitPath() + if filepath.IsAbs(binary) { + return binary, nil + } + + for _, dir := range []string{ + // FHS 3.0: "/usr/libexec includes internal binaries that are not intended to be executed directly by users or shell scripts. Applications may use a single subdirectory under /usr/libexec." + // https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s07.html + "/usr/local/libexec/docker", + "/usr/libexec/docker", + + // FHS 2.3: "/usr/lib includes object files, libraries, and internal binaries that are not intended to be executed directly by users or shell scripts." + // https://refspecs.linuxfoundation.org/FHS_2.3/fhs-2.3.html#USRLIBLIBRARIESFORPROGRAMMINGANDPA + "/usr/local/lib/docker", + "/usr/lib/docker", + } { + // exec.LookPath has a fast-path short-circuit for paths that contain "/" (skipping the PATH lookup) that then verifies whether the given path is likely to be an actual executable binary (so we invoke that instead of reimplementing the same checks) + if file, err := exec.LookPath(filepath.Join(dir, binary)); err == nil { + return file, nil + } + } + + // if we checked all the "libexec" directories and found no matches, fall back to PATH + return exec.LookPath(binary) +} + // GetResolvConf returns the appropriate resolv.conf // Check setupResolvConf on how this is selected func (conf *Config) GetResolvConf() string { diff --git a/daemon/info_unix.go b/daemon/info_unix.go index 12e9180366..f8474191d1 100644 --- a/daemon/info_unix.go +++ b/daemon/info_unix.go @@ -40,7 +40,6 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) } v.Runtimes = daemon.configStore.GetAllRuntimes() v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName() - v.InitBinary = daemon.configStore.GetInitPath() v.RuncCommit.ID = "N/A" v.ContainerdCommit.ID = "N/A" v.InitCommit.ID = "N/A" @@ -63,15 +62,17 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) logrus.Warnf("failed to retrieve containerd version: %v", err) } - defaultInitBinary := daemon.configStore.GetInitPath() - if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil { + v.InitBinary = daemon.configStore.GetInitPath() + if initBinary, err := daemon.configStore.LookupInitPath(); err != nil { + logrus.Warnf("failed to find docker-init: %s", err) + } else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil { if _, commit, err := parseInitVersion(string(rv)); err != nil { - logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err) + logrus.Warnf("failed to parse %s version: %s", initBinary, err) } else { v.InitCommit.ID = commit } } else { - logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err) + logrus.Warnf("failed to retrieve %s version: %s", initBinary, err) } // Set expected and actual commits to the same value to prevent the client @@ -195,13 +196,14 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) { } } - defaultInitBinary := daemon.configStore.GetInitPath() - if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil { + if initBinary, err := daemon.configStore.LookupInitPath(); err != nil { + logrus.Warnf("failed to find docker-init: %s", err) + } else if rv, err := exec.Command(initBinary, "--version").Output(); err == nil { if ver, commit, err := parseInitVersion(string(rv)); err != nil { - logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err) + logrus.Warnf("failed to parse %s version: %s", initBinary, err) } else { v.Components = append(v.Components, types.ComponentVersion{ - Name: filepath.Base(defaultInitBinary), + Name: filepath.Base(initBinary), Version: ver, Details: map[string]string{ "GitCommit": commit, @@ -209,7 +211,7 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) { }) } } else { - logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err) + logrus.Warnf("failed to retrieve %s version: %s", initBinary, err) } daemon.fillRootlessVersion(v) diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 252cd885b7..015a429944 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "os" - "os/exec" "path/filepath" "sort" "strconv" @@ -748,12 +747,9 @@ func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts { if (c.HostConfig.Init != nil && *c.HostConfig.Init) || (c.HostConfig.Init == nil && daemon.configStore.Init) { s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...) - path := daemon.configStore.InitPath - if path == "" { - path, err = exec.LookPath(dconfig.DefaultInitBinary) - if err != nil { - return err - } + path, err := daemon.configStore.LookupInitPath() // this will fall back to DefaultInitBinary and return an absolute path + if err != nil { + return err } s.Mounts = append(s.Mounts, specs.Mount{ Destination: inContainerInitPath,