gopkg.in/docker/docker.v20@v20.10.27/daemon/runtime_unix.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package daemon 5 6 import ( 7 "fmt" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strings" 12 13 "github.com/containerd/cgroups" 14 "github.com/containerd/containerd/runtime/linux/runctypes" 15 v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" 16 "github.com/docker/docker/api/types" 17 "github.com/docker/docker/daemon/config" 18 "github.com/docker/docker/errdefs" 19 "github.com/docker/docker/pkg/ioutils" 20 "github.com/pkg/errors" 21 "github.com/sirupsen/logrus" 22 ) 23 24 const ( 25 defaultRuntimeName = "runc" 26 27 linuxShimV1 = "io.containerd.runtime.v1.linux" 28 linuxShimV2 = "io.containerd.runc.v2" 29 ) 30 31 func configureRuntimes(conf *config.Config) { 32 if conf.DefaultRuntime == "" { 33 conf.DefaultRuntime = config.StockRuntimeName 34 } 35 if conf.Runtimes == nil { 36 conf.Runtimes = make(map[string]types.Runtime) 37 } 38 conf.Runtimes[config.LinuxV1RuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: defaultV1ShimConfig(conf, defaultRuntimeName)} 39 conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: defaultV2ShimConfig(conf, defaultRuntimeName)} 40 conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName] 41 } 42 43 func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig { 44 return &types.ShimConfig{ 45 Binary: linuxShimV2, 46 Opts: &v2runcoptions.Options{ 47 BinaryName: runtimePath, 48 Root: filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName), 49 SystemdCgroup: UsingSystemd(conf), 50 NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", 51 }, 52 } 53 } 54 55 func defaultV1ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig { 56 return &types.ShimConfig{ 57 Binary: linuxShimV1, 58 Opts: &runctypes.RuncOptions{ 59 Runtime: runtimePath, 60 RuntimeRoot: filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName), 61 SystemdCgroup: UsingSystemd(conf), 62 }, 63 } 64 } 65 66 func (daemon *Daemon) loadRuntimes() error { 67 return daemon.initRuntimes(daemon.configStore.Runtimes) 68 } 69 70 func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) { 71 runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes") 72 // Remove old temp directory if any 73 os.RemoveAll(runtimeDir + "-old") 74 tmpDir, err := ioutils.TempDir(daemon.configStore.Root, "gen-runtimes") 75 if err != nil { 76 return errors.Wrap(err, "failed to get temp dir to generate runtime scripts") 77 } 78 defer func() { 79 if err != nil { 80 if err1 := os.RemoveAll(tmpDir); err1 != nil { 81 logrus.WithError(err1).WithField("dir", tmpDir). 82 Warn("failed to remove tmp dir") 83 } 84 return 85 } 86 87 if err = os.Rename(runtimeDir, runtimeDir+"-old"); err != nil { 88 return 89 } 90 if err = os.Rename(tmpDir, runtimeDir); err != nil { 91 err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start") 92 return 93 } 94 if err = os.RemoveAll(runtimeDir + "-old"); err != nil { 95 logrus.WithError(err).WithField("dir", tmpDir). 96 Warn("failed to remove old runtimes dir") 97 } 98 }() 99 100 for name, rt := range runtimes { 101 if len(rt.Args) > 0 { 102 script := filepath.Join(tmpDir, name) 103 content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " ")) 104 if err := os.WriteFile(script, []byte(content), 0700); err != nil { 105 return err 106 } 107 } 108 if rt.Shim == nil { 109 rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path) 110 } 111 } 112 return nil 113 } 114 115 // rewriteRuntimePath is used for runtimes which have custom arguments supplied. 116 // This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments. 117 // To support this case, the daemon wraps the specified runtime in a script that passes through those arguments. 118 func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) (string, error) { 119 if len(args) == 0 { 120 return p, nil 121 } 122 123 // Check that the runtime path actually exists here so that we can return a well known error. 124 if _, err := exec.LookPath(p); err != nil { 125 return "", errors.Wrap(err, "error while looking up the specified runtime path") 126 } 127 128 return filepath.Join(daemon.configStore.Root, "runtimes", name), nil 129 } 130 131 func (daemon *Daemon) getRuntime(name string) (*types.Runtime, error) { 132 rt := daemon.configStore.GetRuntime(name) 133 if rt == nil { 134 return nil, errdefs.InvalidParameter(errors.Errorf("runtime not found in config: %s", name)) 135 } 136 137 if len(rt.Args) > 0 { 138 p, err := daemon.rewriteRuntimePath(name, rt.Path, rt.Args) 139 if err != nil { 140 return nil, err 141 } 142 rt.Path = p 143 rt.Args = nil 144 } 145 146 if rt.Shim == nil { 147 rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path) 148 } 149 150 if rt.Shim.Binary == linuxShimV1 { 151 if cgroups.Mode() == cgroups.Unified { 152 return nil, errdefs.InvalidParameter(errors.Errorf("runtime %q is not supported while cgroups v2 (unified hierarchy) is being used", name)) 153 } 154 logrus.Warnf("Configured runtime %q is deprecated and will be removed in the next release", name) 155 } 156 157 return rt, nil 158 }