summaryrefslogtreecommitdiff
path: root/libpod/runtime.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/runtime.go')
-rw-r--r--libpod/runtime.go643
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
}