diff options
Diffstat (limited to 'libpod/runtime.go')
-rw-r--r-- | libpod/runtime.go | 643 |
1 files changed, 415 insertions, 228 deletions
diff --git a/libpod/runtime.go b/libpod/runtime.go index 1f8dd98b4..28958e932 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -5,13 +5,17 @@ import ( "fmt" "io/ioutil" "os" + "os/user" "path/filepath" + "strings" "sync" "syscall" + "time" "github.com/BurntSushi/toml" is "github.com/containers/image/storage" "github.com/containers/image/types" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/libpod/lock" @@ -42,11 +46,20 @@ const ( SQLiteStateStore RuntimeStateStore = iota // BoltDBStateStore is a state backed by a BoltDB database BoltDBStateStore RuntimeStateStore = iota +) + +var ( + // InstallPrefix is the prefix where podman will be installed. + // It can be overridden at build time. + installPrefix = "/usr/local" + // EtcDir is the sysconfdir where podman should look for system config files. + // It can be overridden at build time. + etcDir = "/etc" // SeccompDefaultPath defines the default seccomp path - SeccompDefaultPath = "/usr/share/containers/seccomp.json" + SeccompDefaultPath = installPrefix + "/share/containers/seccomp.json" // SeccompOverridePath if this exists it overrides the default seccomp path - SeccompOverridePath = "/etc/crio/seccomp.json" + SeccompOverridePath = etcDir + "/crio/seccomp.json" // ConfigPath is the path to the libpod configuration file // This file is loaded to replace the builtin default config before @@ -54,24 +67,24 @@ const ( // If it is not present, the builtin default config is used instead // This path can be overridden when the runtime is created by using // NewRuntimeFromConfig() instead of NewRuntime() - ConfigPath = "/usr/share/containers/libpod.conf" + ConfigPath = installPrefix + "/share/containers/libpod.conf" // OverrideConfigPath is the path to an override for the default libpod // configuration file. If OverrideConfigPath exists, it will be used in // place of the configuration file pointed to by ConfigPath. - OverrideConfigPath = "/etc/containers/libpod.conf" + OverrideConfigPath = etcDir + "/containers/libpod.conf" // DefaultInfraImage to use for infra container - DefaultInfraImage = "k8s.gcr.io/pause:3.1" - // DefaultInfraCommand to be run in an infra container - DefaultInfraCommand = "/pause" - // DefaultInitPath is the default path to the container-init binary - DefaultInitPath = "/usr/libexec/podman/catatonit" + // DefaultInfraCommand to be run in an infra container // DefaultSHMLockPath is the default path for SHM locks DefaultSHMLockPath = "/libpod_lock" // DefaultRootlessSHMLockPath is the default path for rootless SHM locks DefaultRootlessSHMLockPath = "/libpod_rootless_lock" + + // DefaultDetachKeys is the default keys sequence for detaching a + // container + DefaultDetachKeys = "ctrl-p,ctrl-q" ) // A RuntimeOption is a functional option which alters the Runtime created by @@ -82,18 +95,18 @@ type RuntimeOption func(*Runtime) error type Runtime struct { config *RuntimeConfig - state State - store storage.Store - storageService *storageService - imageContext *types.SystemContext - ociRuntime *OCIRuntime - netPlugin ocicni.CNIPlugin - ociRuntimePath OCIRuntimePath - conmonPath string - imageRuntime *image.Runtime - firewallBackend firewall.FirewallBackend - lockManager lock.Manager - configuredFrom *runtimeConfiguredFrom + state State + store storage.Store + storageService *storageService + imageContext *types.SystemContext + defaultOCIRuntime *OCIRuntime + ociRuntimes map[string]*OCIRuntime + netPlugin ocicni.CNIPlugin + conmonPath string + imageRuntime *image.Runtime + firewallBackend firewall.FirewallBackend + lockManager lock.Manager + configuredFrom *runtimeConfiguredFrom // doRenumber indicates that the runtime should perform a lock renumber // during initialization. @@ -112,14 +125,9 @@ type Runtime struct { // mechanism to read and write even logs eventer events.Eventer -} -// OCIRuntimePath contains information about an OCI runtime. -type OCIRuntimePath struct { - // Name of the runtime to refer to by the --runtime flag - Name string `toml:"name"` - // Paths to check for this executable - Paths []string `toml:"paths"` + // noStore indicates whether we need to interact with a store or not + noStore bool } // RuntimeConfig contains configuration options used to set up the runtime @@ -151,6 +159,8 @@ type RuntimeConfig struct { OCIRuntime string `toml:"runtime"` // OCIRuntimes are the set of configured OCI runtimes (default is runc) OCIRuntimes map[string][]string `toml:"runtimes"` + // RuntimeSupportsJSON is the list of the OCI runtimes that support --format=json + RuntimeSupportsJSON []string `toml:"runtime_supports_json"` // RuntimePath is the path to OCI runtime binary for launching // containers. // The first path pointing to a valid file will be used @@ -229,10 +239,15 @@ type RuntimeConfig struct { // pods. NumLocks uint32 `toml:"num_locks,omitempty"` + // LockType is the type of locking to use. + LockType string `toml:"lock_type,omitempty"` + // EventsLogger determines where events should be logged EventsLogger string `toml:"events_logger"` // EventsLogFilePath is where the events log is stored. - EventsLogFilePath string `toml:-"events_logfile_path"` + EventsLogFilePath string `toml:"-events_logfile_path"` + //DetachKeys is the sequence of keys used to detach a container + DetachKeys string `toml:"detach_keys"` } // runtimeConfiguredFrom is a struct used during early runtime init to help @@ -250,6 +265,7 @@ type runtimeConfiguredFrom struct { volPathSet bool conmonPath bool conmonEnvVars bool + initPath bool ociRuntimes bool runtimePath bool cniPluginDir bool @@ -281,33 +297,74 @@ func defaultRuntimeConfig() (RuntimeConfig, error) { }, ConmonPath: []string{ "/usr/libexec/podman/conmon", - "/usr/libexec/crio/conmon", "/usr/local/lib/podman/conmon", - "/usr/local/libexec/crio/conmon", "/usr/bin/conmon", "/usr/sbin/conmon", - "/usr/lib/crio/bin/conmon", + "/usr/local/bin/conmon", + "/usr/local/sbin/conmon", }, ConmonEnvVars: []string{ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", }, - InitPath: DefaultInitPath, + InitPath: define.DefaultInitPath, CgroupManager: SystemdCgroupsManager, StaticDir: filepath.Join(storeOpts.GraphRoot, "libpod"), TmpDir: "", MaxLogSize: -1, NoPivotRoot: false, - CNIConfigDir: "/etc/cni/net.d/", + CNIConfigDir: etcDir + "/cni/net.d/", CNIPluginDir: []string{"/usr/libexec/cni", "/usr/lib/cni", "/usr/local/lib/cni", "/opt/cni/bin"}, - InfraCommand: DefaultInfraCommand, - InfraImage: DefaultInfraImage, + InfraCommand: define.DefaultInfraCommand, + InfraImage: define.DefaultInfraImage, EnablePortReservation: true, EnableLabeling: true, NumLocks: 2048, EventsLogger: events.DefaultEventerType.String(), + DetachKeys: DefaultDetachKeys, + LockType: "shm", }, nil } +// SetXdgRuntimeDir ensures the XDG_RUNTIME_DIR env variable is set +// containers/image uses XDG_RUNTIME_DIR to locate the auth file. +// It internally calls EnableLinger() so that the user's processes are not +// killed once the session is terminated. EnableLinger() also attempts to +// get the runtime directory when XDG_RUNTIME_DIR is not specified. +func SetXdgRuntimeDir() error { + if !rootless.IsRootless() { + return nil + } + + runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + + runtimeDirLinger, err := rootless.EnableLinger() + if err != nil { + return errors.Wrapf(err, "error enabling user session") + } + if runtimeDir == "" && runtimeDirLinger != "" { + if _, err := os.Stat(runtimeDirLinger); err != nil && os.IsNotExist(err) { + chWait := make(chan error) + defer close(chWait) + if _, err := WaitForFile(runtimeDirLinger, chWait, time.Second*10); err != nil { + return errors.Wrapf(err, "waiting for directory '%s'", runtimeDirLinger) + } + } + runtimeDir = runtimeDirLinger + } + + if runtimeDir == "" { + var err error + runtimeDir, err = util.GetRootlessRuntimeDir() + if err != nil { + return err + } + } + if err := os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil { + return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") + } + return nil +} + func getDefaultTmpDir() (string, error) { if !rootless.IsRootless() { return "/var/run/libpod", nil @@ -330,25 +387,6 @@ func getDefaultTmpDir() (string, error) { return filepath.Join(libpodRuntimeDir, "tmp"), nil } -// SetXdgRuntimeDir ensures the XDG_RUNTIME_DIR env variable is set -// containers/image uses XDG_RUNTIME_DIR to locate the auth file. -func SetXdgRuntimeDir(val string) error { - if !rootless.IsRootless() { - return nil - } - if val == "" { - var err error - val, err = util.GetRootlessRuntimeDir() - if err != nil { - return err - } - } - if err := os.Setenv("XDG_RUNTIME_DIR", val); err != nil { - return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") - } - return nil -} - // NewRuntime creates a new container runtime // Options can be passed to override the default configuration for the runtime func NewRuntime(ctx context.Context, options ...RuntimeOption) (runtime *Runtime, err error) { @@ -367,6 +405,73 @@ func NewRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. return newRuntimeFromConfig(ctx, userConfigPath, options...) } +func homeDir() (string, error) { + home := os.Getenv("HOME") + if home == "" { + usr, err := user.LookupId(fmt.Sprintf("%d", rootless.GetRootlessUID())) + if err != nil { + return "", errors.Wrapf(err, "unable to resolve HOME directory") + } + home = usr.HomeDir + } + return home, nil +} + +func getRootlessConfigPath() (string, error) { + home, err := homeDir() + if err != nil { + return "", err + } + + return filepath.Join(home, ".config/containers/libpod.conf"), nil +} + +func getConfigPath() (string, error) { + if rootless.IsRootless() { + path, err := getRootlessConfigPath() + if err != nil { + return "", err + } + if _, err := os.Stat(path); err == nil { + return path, nil + } + return "", err + } + if _, err := os.Stat(OverrideConfigPath); err == nil { + // Use the override configuration path + return OverrideConfigPath, nil + } + if _, err := os.Stat(ConfigPath); err == nil { + return ConfigPath, nil + } + return "", nil +} + +// DefaultRuntimeConfig reads default config path and returns the RuntimeConfig +func DefaultRuntimeConfig() (*RuntimeConfig, error) { + configPath, err := getConfigPath() + if err != nil { + return nil, err + } + + contents, err := ioutil.ReadFile(configPath) + if err != nil { + return nil, errors.Wrapf(err, "error reading configuration file %s", configPath) + } + + // This is ugly, but we need to decode twice. + // Once to check if libpod static and tmp dirs were explicitly + // set (not enough to check if they're not the default value, + // might have been explicitly configured to the default). + // A second time to actually get a usable config. + tmpConfig := new(RuntimeConfig) + if _, err := toml.Decode(string(contents), tmpConfig); err != nil { + return nil, errors.Wrapf(err, "error decoding configuration file %s", + configPath) + } + return tmpConfig, nil +} + func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ...RuntimeOption) (runtime *Runtime, err error) { runtime = new(Runtime) runtime.config = new(RuntimeConfig) @@ -395,31 +500,21 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod") runtime.config.VolumePath = filepath.Join(storageConf.GraphRoot, "volumes") - configPath := ConfigPath - foundConfig := true - rootlessConfigPath := "" + configPath, err := getConfigPath() + if err != nil { + return nil, err + } if rootless.IsRootless() { - home := os.Getenv("HOME") + home, err := homeDir() + if err != nil { + return nil, err + } if runtime.config.SignaturePolicyPath == "" { newPath := filepath.Join(home, ".config/containers/policy.json") if _, err := os.Stat(newPath); err == nil { runtime.config.SignaturePolicyPath = newPath } } - - rootlessConfigPath = filepath.Join(home, ".config/containers/libpod.conf") - - runtimeDir, err := util.GetRootlessRuntimeDir() - if err != nil { - return nil, err - } - - // containers/image uses XDG_RUNTIME_DIR to locate the auth file. - // So make sure the env variable is set. - if err := SetXdgRuntimeDir(runtimeDir); err != nil { - return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") - } - } if userConfigPath != "" { @@ -429,21 +524,10 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. // when it doesn't exist return nil, errors.Wrapf(err, "cannot stat %s", configPath) } - } else if rootless.IsRootless() { - configPath = rootlessConfigPath - if _, err := os.Stat(configPath); err != nil { - foundConfig = false - } - } else if _, err := os.Stat(OverrideConfigPath); err == nil { - // Use the override configuration path - configPath = OverrideConfigPath - } else if _, err := os.Stat(ConfigPath); err != nil { - // Both stat checks failed, no config found - foundConfig = false } // If we have a valid configuration file, load it in - if foundConfig { + if configPath != "" { contents, err := ioutil.ReadFile(configPath) if err != nil { return nil, errors.Wrapf(err, "error reading configuration file %s", configPath) @@ -475,6 +559,9 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. if tmpConfig.ConmonEnvVars != nil { runtime.configuredFrom.conmonEnvVars = true } + if tmpConfig.InitPath != "" { + runtime.configuredFrom.initPath = true + } if tmpConfig.OCIRuntimes != nil { runtime.configuredFrom.ociRuntimes = true } @@ -512,6 +599,9 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. if !runtime.configuredFrom.conmonEnvVars { runtime.config.ConmonEnvVars = tmpConfig.ConmonEnvVars } + if !runtime.configuredFrom.initPath { + runtime.config.InitPath = tmpConfig.InitPath + } if !runtime.configuredFrom.ociRuntimes { runtime.config.OCIRuntimes = tmpConfig.OCIRuntimes } @@ -534,7 +624,13 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. return nil, errors.Wrapf(err, "error configuring runtime") } } - if rootlessConfigPath != "" { + + if rootless.IsRootless() && configPath == "" { + configPath, err := getRootlessConfigPath() + if err != nil { + return nil, err + } + // storage.conf storageConfFile, err := storage.DefaultConfigFile(rootless.IsRootless()) if err != nil { @@ -546,17 +642,21 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. } } - if !foundConfig { - os.MkdirAll(filepath.Dir(rootlessConfigPath), 0755) - file, err := os.OpenFile(rootlessConfigPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if configPath != "" { + if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil { + return nil, err + } + file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) if err != nil && !os.IsExist(err) { - return nil, errors.Wrapf(err, "cannot open file %s", rootlessConfigPath) + return nil, errors.Wrapf(err, "cannot open file %s", configPath) } if err == nil { defer file.Close() enc := toml.NewEncoder(file) if err := enc.Encode(runtime.config); err != nil { - os.Remove(rootlessConfigPath) + if removeErr := os.Remove(configPath); removeErr != nil { + logrus.Debugf("unable to remove %s: %q", configPath, err) + } } } } @@ -567,66 +667,65 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. return runtime, nil } -// Make a new runtime based on the given configuration -// Sets up containers/storage, state store, OCI runtime -func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { - // Backward compatibility for `runtime_path` - if runtime.config.RuntimePath != nil { - // Don't print twice in rootless mode. - if os.Geteuid() == 0 { - logrus.Warningf("The configuration is using `runtime_path`, which is deprecated and will be removed in future. Please use `runtimes` and `runtime`") - logrus.Warningf("If you are using both `runtime_path` and `runtime`, the configuration from `runtime_path` is used") - } +func getLockManager(runtime *Runtime) (lock.Manager, error) { + var err error + var manager lock.Manager - // Transform `runtime_path` into `runtimes` and `runtime`. - name := filepath.Base(runtime.config.RuntimePath[0]) - runtime.config.OCIRuntime = name - runtime.config.OCIRuntimes = map[string][]string{name: runtime.config.RuntimePath} - } - - // Find a working OCI runtime binary - foundRuntime := false - // If runtime is an absolute path, then use it as it is. - if runtime.config.OCIRuntime != "" && runtime.config.OCIRuntime[0] == '/' { - foundRuntime = true - runtime.ociRuntimePath = OCIRuntimePath{Name: filepath.Base(runtime.config.OCIRuntime), Paths: []string{runtime.config.OCIRuntime}} - stat, err := os.Stat(runtime.config.OCIRuntime) + switch runtime.config.LockType { + case "file": + lockPath := filepath.Join(runtime.config.TmpDir, "locks") + manager, err = lock.OpenFileLockManager(lockPath) if err != nil { - if os.IsNotExist(err) { - return errors.Wrapf(err, "the specified OCI runtime %s does not exist", runtime.config.OCIRuntime) + if os.IsNotExist(errors.Cause(err)) { + manager, err = lock.NewFileLockManager(lockPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to get new file lock manager") + } + } else { + return nil, err } - return errors.Wrapf(err, "cannot stat the OCI runtime path %s", runtime.config.OCIRuntime) } - if !stat.Mode().IsRegular() { - return fmt.Errorf("the specified OCI runtime %s is not a valid file", runtime.config.OCIRuntime) + + case "", "shm": + lockPath := DefaultSHMLockPath + if rootless.IsRootless() { + lockPath = fmt.Sprintf("%s_%d", DefaultRootlessSHMLockPath, rootless.GetRootlessUID()) } - } else { - // If not, look it up in the configuration. - paths := runtime.config.OCIRuntimes[runtime.config.OCIRuntime] - if paths != nil { - for _, path := range paths { - stat, err := os.Stat(path) + // Set up the lock manager + manager, err = lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) if err != nil { - if os.IsNotExist(err) { - continue - } - return errors.Wrapf(err, "cannot stat %s", path) + return nil, errors.Wrapf(err, "failed to get new shm lock manager") } - if !stat.Mode().IsRegular() { - continue + } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber { + logrus.Debugf("Number of locks does not match - removing old locks") + + // ERANGE indicates a lock numbering mismatch. + // Since we're renumbering, this is not fatal. + // Remove the earlier set of locks and recreate. + if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil { + return nil, errors.Wrapf(err, "error removing libpod locks file %s", lockPath) } - foundRuntime = true - runtime.ociRuntimePath = OCIRuntimePath{Name: runtime.config.OCIRuntime, Paths: []string{path}} - break + + manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + return nil, err + } + } else { + return nil, err } } + default: + return nil, errors.Wrapf(define.ErrInvalidArg, "unknown lock type %s", runtime.config.LockType) } - if !foundRuntime { - return errors.Wrapf(ErrInvalidArg, - "could not find a working binary (configured options: %v)", - runtime.config.OCIRuntimes) - } + return manager, nil +} +// Make a new runtime based on the given configuration +// Sets up containers/storage, state store, OCI runtime +func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { // Find a working conmon binary foundConmon := false for _, path := range runtime.config.ConmonPath { @@ -642,7 +741,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { break } if !foundConmon { - return errors.Wrapf(ErrInvalidArg, + return errors.Wrapf(define.ErrInvalidArg, "could not find a working conmon binary (configured options: %v)", runtime.config.ConmonPath) } @@ -665,7 +764,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { } runtime.state = state case SQLiteStateStore: - return errors.Wrapf(ErrInvalidArg, "SQLite state is currently disabled") + return errors.Wrapf(define.ErrInvalidArg, "SQLite state is currently disabled") case BoltDBStateStore: dbPath := filepath.Join(runtime.config.StaticDir, "bolt_state.db") @@ -675,7 +774,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { } runtime.state = state default: - return errors.Wrapf(ErrInvalidArg, "unrecognized state type passed") + return errors.Wrapf(define.ErrInvalidArg, "unrecognized state type passed") } // Grab config from the database so we can reset some defaults @@ -752,39 +851,23 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { var store storage.Store if os.Geteuid() != 0 { logrus.Debug("Not configuring container store") + } else if runtime.noStore { + logrus.Debug("No store required. Not opening container store.") } else { - store, err = storage.GetStore(runtime.config.StorageConfig) - if err != nil { + if err := runtime.configureStore(); err != nil { return err } - - defer func() { - if err != nil && store != nil { - // Don't forcibly shut down - // We could be opening a store in use by another libpod - _, err2 := store.Shutdown(false) - if err2 != nil { - logrus.Errorf("Error removing store for partially-created runtime: %s", err2) - } - } - }() } - - runtime.store = store - is.Transport.SetStore(store) - - // Set up image runtime and store in runtime - ir := image.NewImageRuntimeFromStore(runtime.store) - - runtime.imageRuntime = ir - - // Setting signaturepolicypath - ir.SignaturePolicyPath = runtime.config.SignaturePolicyPath - - // Set logfile path for events - ir.EventsLogFilePath = runtime.config.EventsLogFilePath - // Set logger type - ir.EventsLogger = runtime.config.EventsLogger + defer func() { + if err != nil && store != nil { + // Don't forcibly shut down + // We could be opening a store in use by another libpod + _, err2 := store.Shutdown(false) + if err2 != nil { + logrus.Errorf("Error removing store for partially-created runtime: %s", err2) + } + } + }() // Setup the eventer eventer, err := runtime.newEventer() @@ -792,15 +875,9 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { return err } runtime.eventer = eventer - ir.Eventer = eventer - - // Set up a storage service for creating container root filesystems from - // images - storageService, err := getStorageService(runtime.store) - if err != nil { - return err + if runtime.imageRuntime != nil { + runtime.imageRuntime.Eventer = eventer } - runtime.storageService = storageService // Set up containers/image runtime.imageContext = &types.SystemContext{ @@ -823,16 +900,107 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { } } - // Make an OCI runtime to perform container operations - ociRuntime, err := newOCIRuntime(runtime.ociRuntimePath, - runtime.conmonPath, runtime.config.ConmonEnvVars, - runtime.config.CgroupManager, runtime.config.TmpDir, - runtime.config.MaxLogSize, runtime.config.NoPivotRoot, - runtime.config.EnablePortReservation) - if err != nil { - return err + // Get us at least one working OCI runtime. + runtime.ociRuntimes = make(map[string]*OCIRuntime) + + // Is the old runtime_path defined? + if runtime.config.RuntimePath != nil { + // Don't print twice in rootless mode. + if os.Geteuid() == 0 { + logrus.Warningf("The configuration is using `runtime_path`, which is deprecated and will be removed in future. Please use `runtimes` and `runtime`") + logrus.Warningf("If you are using both `runtime_path` and `runtime`, the configuration from `runtime_path` is used") + } + + if len(runtime.config.RuntimePath) == 0 { + return errors.Wrapf(define.ErrInvalidArg, "empty runtime path array passed") + } + + name := filepath.Base(runtime.config.RuntimePath[0]) + + supportsJSON := false + for _, r := range runtime.config.RuntimeSupportsJSON { + if r == name { + supportsJSON = true + break + } + } + + ociRuntime, err := newOCIRuntime(name, runtime.config.RuntimePath, runtime.conmonPath, runtime.config, supportsJSON) + if err != nil { + return err + } + + runtime.ociRuntimes[name] = ociRuntime + runtime.defaultOCIRuntime = ociRuntime + } + + // Initialize remaining OCI runtimes + for name, paths := range runtime.config.OCIRuntimes { + if len(paths) == 0 { + return errors.Wrapf(define.ErrInvalidArg, "must provide at least 1 path to OCI runtime %s", name) + } + + supportsJSON := false + for _, r := range runtime.config.RuntimeSupportsJSON { + if r == name { + supportsJSON = true + break + } + } + + ociRuntime, err := newOCIRuntime(name, paths, runtime.conmonPath, runtime.config, supportsJSON) + if err != nil { + // Don't fatally error. + // This will allow us to ship configs including optional + // runtimes that might not be installed (crun, kata). + // Only a warnf so default configs don't spec errors. + logrus.Warnf("Error initializing configured OCI runtime %s: %v", name, err) + continue + } + + runtime.ociRuntimes[name] = ociRuntime + } + + // Do we have a default OCI runtime? + if runtime.config.OCIRuntime != "" { + // If the string starts with / it's a path to a runtime + // executable. + if strings.HasPrefix(runtime.config.OCIRuntime, "/") { + name := filepath.Base(runtime.config.OCIRuntime) + + supportsJSON := false + for _, r := range runtime.config.RuntimeSupportsJSON { + if r == name { + supportsJSON = true + break + } + } + + ociRuntime, err := newOCIRuntime(name, []string{runtime.config.OCIRuntime}, runtime.conmonPath, runtime.config, supportsJSON) + if err != nil { + return err + } + + runtime.ociRuntimes[name] = ociRuntime + runtime.defaultOCIRuntime = ociRuntime + } else { + ociRuntime, ok := runtime.ociRuntimes[runtime.config.OCIRuntime] + if !ok { + return errors.Wrapf(define.ErrInvalidArg, "default OCI runtime %q not found", runtime.config.OCIRuntime) + } + runtime.defaultOCIRuntime = ociRuntime + } + } + + // Do we have at least one valid OCI runtime? + if len(runtime.ociRuntimes) == 0 { + return errors.Wrapf(define.ErrInvalidArg, "no OCI runtime has been configured") + } + + // Do we have a default runtime? + if runtime.defaultOCIRuntime == nil { + return errors.Wrapf(define.ErrInvalidArg, "no default OCI runtime was configured") } - runtime.ociRuntime = ociRuntime // Make the per-boot files directory if it does not exist if err := os.MkdirAll(runtime.config.TmpDir, 0755); err != nil { @@ -915,37 +1083,10 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { } } - lockPath := DefaultSHMLockPath - if rootless.IsRootless() { - lockPath = fmt.Sprintf("%s_%d", DefaultRootlessSHMLockPath, rootless.GetRootlessUID()) - } - // Set up the lock manager - manager, err := lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) + runtime.lockManager, err = getLockManager(runtime) if err != nil { - if os.IsNotExist(errors.Cause(err)) { - manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { - return errors.Wrapf(err, "failed to get new shm lock manager") - } - } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber { - logrus.Debugf("Number of locks does not match - removing old locks") - - // ERANGE indicates a lock numbering mismatch. - // Since we're renumbering, this is not fatal. - // Remove the earlier set of locks and recreate. - if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil { - return errors.Wrapf(err, "error removing libpod locks file %s", lockPath) - } - - manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { - return err - } - } else { - return err - } + return err } - runtime.lockManager = manager // If we're renumbering locks, do it now. // It breaks out of normal runtime init, and will not return a valid @@ -959,6 +1100,13 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { // If we need to refresh the state, do it now - things are guaranteed to // be set up by now. if doRefresh { + // Ensure we have a store before refresh occurs + if runtime.store == nil { + if err := runtime.configureStore(); err != nil { + return err + } + } + if err2 := runtime.refresh(runtimeAliveFile); err2 != nil { return err2 } @@ -983,7 +1131,7 @@ func (r *Runtime) GetConfig() (*RuntimeConfig, error) { defer r.lock.RUnlock() if !r.valid { - return nil, ErrRuntimeStopped + return nil, define.ErrRuntimeStopped } config := new(RuntimeConfig) @@ -996,6 +1144,13 @@ func (r *Runtime) GetConfig() (*RuntimeConfig, error) { return config, nil } +// DeferredShutdown shuts down the runtime without exposing any +// errors. This is only meant to be used when the runtime is being +// shutdown within a defer statement; else use Shutdown +func (r *Runtime) DeferredShutdown(force bool) { + _ = r.Shutdown(force) +} + // Shutdown shuts down the runtime and associated containers and storage // If force is true, containers and mounted storage will be shut down before // cleaning up; if force is false, an error will be returned if there are @@ -1005,7 +1160,7 @@ func (r *Runtime) Shutdown(force bool) error { defer r.lock.Unlock() if !r.valid { - return ErrRuntimeStopped + return define.ErrRuntimeStopped } r.valid = false @@ -1017,7 +1172,7 @@ func (r *Runtime) Shutdown(force bool) error { logrus.Errorf("Error retrieving containers from database: %v", err) } else { for _, ctr := range ctrs { - if err := ctr.StopWithTimeout(CtrRemoveTimeout); err != nil { + if err := ctr.StopWithTimeout(define.CtrRemoveTimeout); err != nil { logrus.Errorf("Error stopping container %s: %v", ctr.ID(), err) } } @@ -1025,6 +1180,8 @@ func (r *Runtime) Shutdown(force bool) error { } var lastError error + // If no store was requested, it can bew nil and there is no need to + // attempt to shut it down if r.store != nil { if _, err := r.store.Shutdown(force); err != nil { lastError = errors.Wrapf(err, "Error shutting down container storage") @@ -1092,21 +1249,21 @@ func (r *Runtime) refresh(alivePath string) error { } // Info returns the store and host information -func (r *Runtime) Info() ([]InfoData, error) { - info := []InfoData{} +func (r *Runtime) Info() ([]define.InfoData, error) { + info := []define.InfoData{} // get host information hostInfo, err := r.hostInfo() if err != nil { return nil, errors.Wrapf(err, "error getting host info") } - info = append(info, InfoData{Type: "host", Data: hostInfo}) + info = append(info, define.InfoData{Type: "host", Data: hostInfo}) // get store information storeInfo, err := r.storeInfo() if err != nil { return nil, errors.Wrapf(err, "error getting store info") } - info = append(info, InfoData{Type: "store", Data: storeInfo}) + info = append(info, define.InfoData{Type: "store", Data: storeInfo}) reg, err := sysreg.GetRegistries() if err != nil { @@ -1126,7 +1283,7 @@ func (r *Runtime) Info() ([]InfoData, error) { return nil, errors.Wrapf(err, "error getting registries") } registries["blocked"] = breg - info = append(info, InfoData{Type: "registries", Data: registries}) + info = append(info, define.InfoData{Type: "registries", Data: registries}) return info, nil } @@ -1138,7 +1295,7 @@ func (r *Runtime) generateName() (string, error) { if _, err := r.state.LookupContainer(name); err == nil { continue } else { - if errors.Cause(err) != ErrNoSuchCtr { + if errors.Cause(err) != define.ErrNoSuchCtr { return "", err } } @@ -1146,7 +1303,7 @@ func (r *Runtime) generateName() (string, error) { if _, err := r.state.LookupPod(name); err == nil { continue } else { - if errors.Cause(err) != ErrNoSuchPod { + if errors.Cause(err) != define.ErrNoSuchPod { return "", err } } @@ -1155,7 +1312,37 @@ func (r *Runtime) generateName() (string, error) { // The code should never reach here. } -// ImageRuntime returns the imageruntime for image resolution +// Configure store and image runtime +func (r *Runtime) configureStore() error { + store, err := storage.GetStore(r.config.StorageConfig) + if err != nil { + return err + } + + r.store = store + is.Transport.SetStore(store) + + // Set up a storage service for creating container root filesystems from + // images + storageService, err := getStorageService(r.store) + if err != nil { + return err + } + r.storageService = storageService + + ir := image.NewImageRuntimeFromStore(r.store) + ir.SignaturePolicyPath = r.config.SignaturePolicyPath + ir.EventsLogFilePath = r.config.EventsLogFilePath + ir.EventsLogger = r.config.EventsLogger + + r.imageRuntime = ir + + return nil +} + +// ImageRuntime returns the imageruntime for image operations. +// If WithNoStore() was used, no image runtime will be available, and this +// function will return nil. func (r *Runtime) ImageRuntime() *image.Runtime { return r.imageRuntime } |