github.com/rawahars/moby@v24.0.4+incompatible/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  	v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
    14  	"github.com/docker/docker/api/types"
    15  	"github.com/docker/docker/daemon/config"
    16  	"github.com/docker/docker/errdefs"
    17  	"github.com/docker/docker/libcontainerd/shimopts"
    18  	"github.com/pkg/errors"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  const (
    23  	defaultRuntimeName = "runc"
    24  
    25  	linuxShimV2 = "io.containerd.runc.v2"
    26  )
    27  
    28  func configureRuntimes(conf *config.Config) {
    29  	if conf.DefaultRuntime == "" {
    30  		conf.DefaultRuntime = config.StockRuntimeName
    31  	}
    32  	if conf.Runtimes == nil {
    33  		conf.Runtimes = make(map[string]types.Runtime)
    34  	}
    35  	conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, ShimConfig: defaultV2ShimConfig(conf, defaultRuntimeName)}
    36  	conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName]
    37  }
    38  
    39  func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig {
    40  	return &types.ShimConfig{
    41  		Binary: linuxShimV2,
    42  		Opts: &v2runcoptions.Options{
    43  			BinaryName:    runtimePath,
    44  			Root:          filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName),
    45  			SystemdCgroup: UsingSystemd(conf),
    46  			NoPivotRoot:   os.Getenv("DOCKER_RAMDISK") != "",
    47  		},
    48  	}
    49  }
    50  
    51  func (daemon *Daemon) loadRuntimes() error {
    52  	return daemon.initRuntimes(daemon.configStore.Runtimes)
    53  }
    54  
    55  func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) {
    56  	runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes")
    57  	runtimeOldDir := runtimeDir + "-old"
    58  	// Remove old temp directory if any
    59  	os.RemoveAll(runtimeOldDir)
    60  	tmpDir, err := os.MkdirTemp(daemon.configStore.Root, "gen-runtimes")
    61  	if err != nil {
    62  		return errors.Wrap(err, "failed to get temp dir to generate runtime scripts")
    63  	}
    64  	defer func() {
    65  		if err != nil {
    66  			if err1 := os.RemoveAll(tmpDir); err1 != nil {
    67  				logrus.WithError(err1).WithField("dir", tmpDir).
    68  					Warn("failed to remove tmp dir")
    69  			}
    70  			return
    71  		}
    72  
    73  		if err = os.Rename(runtimeDir, runtimeOldDir); err != nil {
    74  			logrus.WithError(err).WithField("dir", runtimeDir).
    75  				Warn("failed to rename runtimes dir to old. Will try to removing it")
    76  			if err = os.RemoveAll(runtimeDir); err != nil {
    77  				logrus.WithError(err).WithField("dir", runtimeDir).
    78  					Warn("failed to remove old runtimes dir")
    79  				return
    80  			}
    81  		}
    82  		if err = os.Rename(tmpDir, runtimeDir); err != nil {
    83  			err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start")
    84  			return
    85  		}
    86  		if err = os.RemoveAll(runtimeOldDir); err != nil {
    87  			logrus.WithError(err).WithField("dir", runtimeOldDir).
    88  				Warn("failed to remove old runtimes dir")
    89  		}
    90  	}()
    91  
    92  	for name := range runtimes {
    93  		rt := runtimes[name]
    94  		if rt.Path == "" && rt.Type == "" {
    95  			return errors.Errorf("runtime %s: either a runtimeType or a path must be configured", name)
    96  		}
    97  		if rt.Path != "" {
    98  			if rt.Type != "" {
    99  				return errors.Errorf("runtime %s: cannot configure both path and runtimeType for the same runtime", name)
   100  			}
   101  			if len(rt.Options) > 0 {
   102  				return errors.Errorf("runtime %s: options cannot be used with a path runtime", name)
   103  			}
   104  
   105  			if len(rt.Args) > 0 {
   106  				script := filepath.Join(tmpDir, name)
   107  				content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " "))
   108  				if err := os.WriteFile(script, []byte(content), 0700); err != nil {
   109  					return err
   110  				}
   111  			}
   112  			rt.ShimConfig = defaultV2ShimConfig(daemon.configStore, daemon.rewriteRuntimePath(name, rt.Path, rt.Args))
   113  		} else {
   114  			if len(rt.Args) > 0 {
   115  				return errors.Errorf("runtime %s: args cannot be used with a runtimeType runtime", name)
   116  			}
   117  			// Unlike implicit runtimes, there is no restriction on configuring a shim by path.
   118  			rt.ShimConfig = &types.ShimConfig{Binary: rt.Type}
   119  			if len(rt.Options) > 0 {
   120  				// It has to be a pointer type or there'll be a panic in containerd/typeurl when we try to start the container.
   121  				rt.ShimConfig.Opts, err = shimopts.Generate(rt.Type, rt.Options)
   122  				if err != nil {
   123  					return errors.Wrapf(err, "runtime %v", name)
   124  				}
   125  			}
   126  		}
   127  		runtimes[name] = rt
   128  	}
   129  	return nil
   130  }
   131  
   132  // rewriteRuntimePath is used for runtimes which have custom arguments supplied.
   133  // This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments.
   134  // To support this case, the daemon wraps the specified runtime in a script that passes through those arguments.
   135  func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) string {
   136  	if len(args) == 0 {
   137  		return p
   138  	}
   139  
   140  	return filepath.Join(daemon.configStore.Root, "runtimes", name)
   141  }
   142  
   143  func (daemon *Daemon) getRuntime(name string) (shim string, opts interface{}, err error) {
   144  	rt := daemon.configStore.GetRuntime(name)
   145  	if rt == nil {
   146  		if !config.IsPermissibleC8dRuntimeName(name) {
   147  			return "", nil, errdefs.InvalidParameter(errors.Errorf("unknown or invalid runtime name: %s", name))
   148  		}
   149  		return name, nil, nil
   150  	}
   151  
   152  	if len(rt.Args) > 0 {
   153  		// Check that the path of the runtime which the script wraps actually exists so
   154  		// that we can return a well known error which references the configured path
   155  		// instead of the wrapper script's.
   156  		if _, err := exec.LookPath(rt.Path); err != nil {
   157  			return "", nil, errors.Wrap(err, "error while looking up the specified runtime path")
   158  		}
   159  	}
   160  
   161  	if rt.ShimConfig == nil {
   162  		// Should never happen as daemon.initRuntimes always sets
   163  		// ShimConfig and config reloading is synchronized.
   164  		err := errdefs.System(errors.Errorf("BUG: runtime %s: rt.ShimConfig == nil", name))
   165  		logrus.Error(err)
   166  		return "", nil, err
   167  	}
   168  
   169  	return rt.ShimConfig.Binary, rt.ShimConfig.Opts, nil
   170  }