diff --git a/cmd/nvidia-container-toolkit/main.go b/cmd/nvidia-container-toolkit/main.go index 81c86572..232f17b5 100644 --- a/cmd/nvidia-container-toolkit/main.go +++ b/cmd/nvidia-container-toolkit/main.go @@ -6,21 +6,20 @@ import ( "log" "os" "os/exec" - "path" "path/filepath" "runtime" "runtime/debug" "strconv" "strings" "syscall" + + "github.com/NVIDIA/nvidia-container-toolkit/internal/lookup" ) var ( debugflag = flag.Bool("debug", false, "enable debug output") forceflag = flag.Bool("force", false, "force execution of prestart hook in experimental mode") configflag = flag.String("config", "", "configuration file") - - defaultPATH = []string{"/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"} ) func exit() { @@ -36,28 +35,16 @@ func exit() { os.Exit(0) } -func getPATH(config CLIConfig) string { - dirs := filepath.SplitList(os.Getenv("PATH")) - // directories from the hook environment have higher precedence - dirs = append(dirs, defaultPATH...) - - if config.Root != nil { - rootDirs := []string{} - for _, dir := range dirs { - rootDirs = append(rootDirs, path.Join(*config.Root, dir)) - } - // directories with the root prefix have higher precedence - dirs = append(rootDirs, dirs...) - } - return strings.Join(dirs, ":") -} - func getCLIPath(config CLIConfig) string { if config.Path != nil { return *config.Path } - if err := os.Setenv("PATH", getPATH(config)); err != nil { + var root string + if config.Root != nil { + root = *config.Root + } + if err := os.Setenv("PATH", lookup.GetPath(root)); err != nil { log.Panicln("couldn't set PATH variable:", err) } diff --git a/internal/lookup/executable.go b/internal/lookup/executable.go index 3109c440..615d608c 100644 --- a/internal/lookup/executable.go +++ b/internal/lookup/executable.go @@ -25,24 +25,13 @@ import ( log "github.com/sirupsen/logrus" ) -const ( - envPath = "PATH" -) - -var defaultPaths = []string{"/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"} - type executable struct { file } // NewExecutableLocator creates a locator to fine executable files in the path. A logger can also be specified. func NewExecutableLocator(logger *log.Logger, root string) Locator { - pathEnv := os.Getenv(envPath) - paths := filepath.SplitList(pathEnv) - - if root != "" { - paths = append(paths, defaultPaths...) - } + paths := GetPaths(root) var prefixes []string for _, dir := range paths { diff --git a/internal/lookup/path.go b/internal/lookup/path.go new file mode 100644 index 00000000..ce692f8c --- /dev/null +++ b/internal/lookup/path.go @@ -0,0 +1,69 @@ +/** +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +**/ + +package lookup + +import ( + "os" + "path" + "path/filepath" + "strings" +) + +const ( + envPath = "PATH" +) + +var ( + defaultPATH = []string{"/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"} +) + +// GetPaths returns a list of paths for a specified root. These are constructed from the +// PATH environment variable, a default path list, and the supplied root. +func GetPaths(root string) []string { + dirs := filepath.SplitList(os.Getenv(envPath)) + + inDirs := make(map[string]bool) + for _, d := range dirs { + inDirs[d] = true + } + + // directories from the environment have higher precedence + for _, d := range defaultPATH { + if inDirs[d] { + // We don't add paths that are already included + continue + } + dirs = append(dirs, d) + } + + if root != "" && root != "/" { + rootDirs := []string{} + for _, dir := range dirs { + rootDirs = append(rootDirs, path.Join(root, dir)) + } + // directories with the root prefix have higher precedence + dirs = append(rootDirs, dirs...) + } + + return dirs +} + +// GetPath returns a colon-separated path value that can be used to set the PATH +// environment variable +func GetPath(root string) string { + return strings.Join(GetPaths(root), ":") +} diff --git a/internal/oci/runtime_low_level.go b/internal/oci/runtime_low_level.go index cfc3f635..795c4e14 100644 --- a/internal/oci/runtime_low_level.go +++ b/internal/oci/runtime_low_level.go @@ -18,8 +18,8 @@ package oci import ( "fmt" - "os/exec" + "github.com/NVIDIA/nvidia-container-toolkit/internal/lookup" log "github.com/sirupsen/logrus" ) @@ -43,14 +43,15 @@ func findRuntime(logger *log.Logger, candidates []string) (string, error) { return "", fmt.Errorf("at least one runtime candidate must be specified") } + locator := lookup.NewExecutableLocator(logger, "/") for _, candidate := range candidates { logger.Debugf("Looking for runtime binary '%v'", candidate) - runcPath, err := exec.LookPath(candidate) - if err == nil { - logger.Debugf("Found runtime binary '%v'", runcPath) - return runcPath, nil + targets, err := locator.Locate(candidate) + if err == nil && len(targets) > 0 { + logger.Debugf("Found runtime binary '%v'", targets) + return targets[0], nil } - logger.Debugf("Runtime binary '%v' not found: %v", candidate, err) + logger.Debugf("Runtime binary '%v' not found: %v (targets=%v)", candidate, err, targets) } return "", fmt.Errorf("no runtime binary found from candidate list: %v", candidates)