github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/runtime_libpod.go (about)

     1  //go:build !remote
     2  // +build !remote
     3  
     4  package infra
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os"
    10  	"os/signal"
    11  	"sync"
    12  	"syscall"
    13  
    14  	"github.com/containers/common/pkg/cgroups"
    15  	"github.com/hanks177/podman/v4/libpod"
    16  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    17  	"github.com/hanks177/podman/v4/pkg/namespaces"
    18  	"github.com/hanks177/podman/v4/pkg/rootless"
    19  	"github.com/containers/storage/pkg/idtools"
    20  	"github.com/containers/storage/types"
    21  	"github.com/pkg/errors"
    22  	"github.com/sirupsen/logrus"
    23  	flag "github.com/spf13/pflag"
    24  )
    25  
    26  var (
    27  	// runtimeSync only guards the non-specialized runtime
    28  	runtimeSync sync.Once
    29  	// The default GetRuntime() always returns the same object and error
    30  	runtimeLib *libpod.Runtime
    31  	runtimeErr error
    32  )
    33  
    34  type engineOpts struct {
    35  	name     string
    36  	renumber bool
    37  	migrate  bool
    38  	noStore  bool
    39  	withFDS  bool
    40  	reset    bool
    41  	config   *entities.PodmanConfig
    42  }
    43  
    44  // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers
    45  func GetRuntimeMigrate(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig, newRuntime string) (*libpod.Runtime, error) {
    46  	return getRuntime(ctx, fs, &engineOpts{
    47  		name:     newRuntime,
    48  		renumber: false,
    49  		migrate:  true,
    50  		noStore:  false,
    51  		withFDS:  true,
    52  		reset:    false,
    53  		config:   cfg,
    54  	})
    55  }
    56  
    57  // GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify
    58  func GetRuntimeDisableFDs(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) {
    59  	return getRuntime(ctx, fs, &engineOpts{
    60  		renumber: false,
    61  		migrate:  false,
    62  		noStore:  false,
    63  		withFDS:  false,
    64  		reset:    false,
    65  		config:   cfg,
    66  	})
    67  }
    68  
    69  // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber
    70  func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) {
    71  	return getRuntime(ctx, fs, &engineOpts{
    72  		renumber: true,
    73  		migrate:  false,
    74  		noStore:  false,
    75  		withFDS:  true,
    76  		reset:    false,
    77  		config:   cfg,
    78  	})
    79  }
    80  
    81  // GetRuntime generates a new libpod runtime configured by command line options
    82  func GetRuntime(ctx context.Context, flags *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) {
    83  	runtimeSync.Do(func() {
    84  		runtimeLib, runtimeErr = getRuntime(ctx, flags, &engineOpts{
    85  			renumber: false,
    86  			migrate:  false,
    87  			noStore:  false,
    88  			withFDS:  true,
    89  			reset:    false,
    90  			config:   cfg,
    91  		})
    92  	})
    93  	return runtimeLib, runtimeErr
    94  }
    95  
    96  // GetRuntimeNoStore generates a new libpod runtime configured by command line options
    97  func GetRuntimeNoStore(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) {
    98  	return getRuntime(ctx, fs, &engineOpts{
    99  		renumber: false,
   100  		migrate:  false,
   101  		noStore:  true,
   102  		withFDS:  true,
   103  		reset:    false,
   104  		config:   cfg,
   105  	})
   106  }
   107  
   108  func GetRuntimeReset(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) {
   109  	return getRuntime(ctx, fs, &engineOpts{
   110  		renumber: false,
   111  		migrate:  false,
   112  		noStore:  false,
   113  		withFDS:  true,
   114  		reset:    true,
   115  		config:   cfg,
   116  	})
   117  }
   118  
   119  func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) {
   120  	options := []libpod.RuntimeOption{}
   121  	storageOpts := types.StoreOptions{}
   122  	cfg := opts.config
   123  
   124  	storageSet := false
   125  
   126  	uidmapFlag := fs.Lookup("uidmap")
   127  	gidmapFlag := fs.Lookup("gidmap")
   128  	subuidname := fs.Lookup("subuidname")
   129  	subgidname := fs.Lookup("subgidname")
   130  	if (uidmapFlag != nil && gidmapFlag != nil && subuidname != nil && subgidname != nil) &&
   131  		(uidmapFlag.Changed || gidmapFlag.Changed || subuidname.Changed || subgidname.Changed) {
   132  		userns, _ := fs.GetString("userns")
   133  		uidmapVal, _ := fs.GetStringSlice("uidmap")
   134  		gidmapVal, _ := fs.GetStringSlice("gidmap")
   135  		subuidVal, _ := fs.GetString("subuidname")
   136  		subgidVal, _ := fs.GetString("subgidname")
   137  		mappings, err := ParseIDMapping(namespaces.UsernsMode(userns), uidmapVal, gidmapVal, subuidVal, subgidVal)
   138  		if err != nil {
   139  			return nil, err
   140  		}
   141  		storageOpts.UIDMap = mappings.UIDMap
   142  		storageOpts.GIDMap = mappings.GIDMap
   143  
   144  		storageSet = true
   145  	}
   146  
   147  	if fs.Changed("root") {
   148  		storageSet = true
   149  		storageOpts.GraphRoot = cfg.Engine.StaticDir
   150  		storageOpts.GraphDriverOptions = []string{}
   151  	}
   152  	if fs.Changed("runroot") {
   153  		storageSet = true
   154  		storageOpts.RunRoot = cfg.Runroot
   155  	}
   156  	if len(storageOpts.RunRoot) > 50 {
   157  		return nil, errors.New("the specified runroot is longer than 50 characters")
   158  	}
   159  	if fs.Changed("storage-driver") {
   160  		storageSet = true
   161  		storageOpts.GraphDriverName = cfg.StorageDriver
   162  		// Overriding the default storage driver caused GraphDriverOptions from storage.conf to be ignored
   163  		storageOpts.GraphDriverOptions = []string{}
   164  	}
   165  	// This should always be checked after storage-driver is checked
   166  	if len(cfg.StorageOpts) > 0 {
   167  		storageSet = true
   168  		if len(cfg.StorageOpts) == 1 && cfg.StorageOpts[0] == "" {
   169  			storageOpts.GraphDriverOptions = []string{}
   170  		} else {
   171  			storageOpts.GraphDriverOptions = cfg.StorageOpts
   172  		}
   173  	}
   174  	if opts.migrate {
   175  		options = append(options, libpod.WithMigrate())
   176  		if opts.name != "" {
   177  			options = append(options, libpod.WithMigrateRuntime(opts.name))
   178  		}
   179  	}
   180  
   181  	if opts.reset {
   182  		options = append(options, libpod.WithReset())
   183  	}
   184  
   185  	if opts.renumber {
   186  		options = append(options, libpod.WithRenumber())
   187  	}
   188  
   189  	if len(cfg.RuntimeFlags) > 0 {
   190  		runtimeFlags := []string{}
   191  		for _, arg := range cfg.RuntimeFlags {
   192  			runtimeFlags = append(runtimeFlags, "--"+arg)
   193  		}
   194  		options = append(options, libpod.WithRuntimeFlags(runtimeFlags))
   195  	}
   196  
   197  	// Only set this if the user changes storage config on the command line
   198  	if storageSet {
   199  		options = append(options, libpod.WithStorageConfig(storageOpts))
   200  	}
   201  
   202  	if !storageSet && opts.noStore {
   203  		options = append(options, libpod.WithNoStore())
   204  	}
   205  	// TODO CLI flags for image config?
   206  	// TODO CLI flag for signature policy?
   207  
   208  	if len(cfg.Engine.Namespace) > 0 {
   209  		options = append(options, libpod.WithNamespace(cfg.Engine.Namespace))
   210  	}
   211  
   212  	if fs.Changed("runtime") {
   213  		options = append(options, libpod.WithOCIRuntime(cfg.RuntimePath))
   214  	}
   215  
   216  	if fs.Changed("conmon") {
   217  		options = append(options, libpod.WithConmonPath(cfg.ConmonPath))
   218  	}
   219  	if fs.Changed("tmpdir") {
   220  		options = append(options, libpod.WithTmpDir(cfg.Engine.TmpDir))
   221  	}
   222  	if fs.Changed("network-cmd-path") {
   223  		options = append(options, libpod.WithNetworkCmdPath(cfg.Engine.NetworkCmdPath))
   224  	}
   225  	if fs.Changed("network-backend") {
   226  		options = append(options, libpod.WithNetworkBackend(cfg.Network.NetworkBackend))
   227  	}
   228  
   229  	if fs.Changed("events-backend") {
   230  		options = append(options, libpod.WithEventsLogger(cfg.Engine.EventsLogger))
   231  	}
   232  
   233  	if fs.Changed("volumepath") {
   234  		options = append(options, libpod.WithVolumePath(cfg.Engine.VolumePath))
   235  	}
   236  
   237  	if fs.Changed("cgroup-manager") {
   238  		options = append(options, libpod.WithCgroupManager(cfg.Engine.CgroupManager))
   239  	} else {
   240  		unified, err := cgroups.IsCgroup2UnifiedMode()
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  		if rootless.IsRootless() && !unified {
   245  			options = append(options, libpod.WithCgroupManager("cgroupfs"))
   246  		}
   247  	}
   248  
   249  	// TODO flag to set libpod static dir?
   250  	// TODO flag to set libpod tmp dir?
   251  
   252  	if fs.Changed("network-config-dir") {
   253  		options = append(options, libpod.WithCNIConfigDir(cfg.Network.NetworkConfigDir))
   254  	}
   255  	if fs.Changed("default-mounts-file") {
   256  		options = append(options, libpod.WithDefaultMountsFile(cfg.Containers.DefaultMountsFile))
   257  	}
   258  	if fs.Changed("hooks-dir") {
   259  		options = append(options, libpod.WithHooksDir(cfg.Engine.HooksDir...))
   260  	}
   261  	if fs.Changed("registries-conf") {
   262  		options = append(options, libpod.WithRegistriesConf(cfg.RegistriesConf))
   263  	}
   264  
   265  	// no need to handle the error, it will return false anyway
   266  	if syslog, _ := fs.GetBool("syslog"); syslog {
   267  		options = append(options, libpod.WithSyslog())
   268  	}
   269  
   270  	// TODO flag to set CNI plugins dir?
   271  
   272  	if !opts.withFDS {
   273  		options = append(options, libpod.WithEnableSDNotify())
   274  	}
   275  	return libpod.NewRuntime(ctx, options...)
   276  }
   277  
   278  // ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
   279  func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*types.IDMappingOptions, error) {
   280  	options := types.IDMappingOptions{
   281  		HostUIDMapping: true,
   282  		HostGIDMapping: true,
   283  	}
   284  
   285  	if mode.IsAuto() {
   286  		var err error
   287  		options.HostUIDMapping = false
   288  		options.HostGIDMapping = false
   289  		options.AutoUserNs = true
   290  		opts, err := mode.GetAutoOptions()
   291  		if err != nil {
   292  			return nil, err
   293  		}
   294  		options.AutoUserNsOpts = *opts
   295  		return &options, nil
   296  	}
   297  	if mode.IsKeepID() {
   298  		if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 {
   299  			return nil, errors.New("cannot specify custom mappings with --userns=keep-id")
   300  		}
   301  		if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
   302  			return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
   303  		}
   304  		if !rootless.IsRootless() {
   305  			return nil, errors.New("keep-id is only supported in rootless mode")
   306  		}
   307  		min := func(a, b int) int {
   308  			if a < b {
   309  				return a
   310  			}
   311  			return b
   312  		}
   313  
   314  		uid := rootless.GetRootlessUID()
   315  		gid := rootless.GetRootlessGID()
   316  
   317  		uids, gids, err := rootless.GetConfiguredMappings()
   318  		if err != nil {
   319  			return nil, errors.Wrapf(err, "cannot read mappings")
   320  		}
   321  		maxUID, maxGID := 0, 0
   322  		for _, u := range uids {
   323  			maxUID += u.Size
   324  		}
   325  		for _, g := range gids {
   326  			maxGID += g.Size
   327  		}
   328  
   329  		options.UIDMap, options.GIDMap = nil, nil
   330  
   331  		options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
   332  		options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
   333  		if maxUID > uid {
   334  			options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
   335  		}
   336  
   337  		options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
   338  		options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
   339  		if maxGID > gid {
   340  			options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
   341  		}
   342  
   343  		options.HostUIDMapping = false
   344  		options.HostGIDMapping = false
   345  		// Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
   346  		return &options, nil
   347  	}
   348  
   349  	if subGIDMap == "" && subUIDMap != "" {
   350  		subGIDMap = subUIDMap
   351  	}
   352  	if subUIDMap == "" && subGIDMap != "" {
   353  		subUIDMap = subGIDMap
   354  	}
   355  	if len(gidMapSlice) == 0 && len(uidMapSlice) != 0 {
   356  		gidMapSlice = uidMapSlice
   357  	}
   358  	if len(uidMapSlice) == 0 && len(gidMapSlice) != 0 {
   359  		uidMapSlice = gidMapSlice
   360  	}
   361  	if len(uidMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 {
   362  		uidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())}
   363  	}
   364  	if len(gidMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 {
   365  		gidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())}
   366  	}
   367  
   368  	if subUIDMap != "" && subGIDMap != "" {
   369  		mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
   370  		if err != nil {
   371  			return nil, err
   372  		}
   373  		options.UIDMap = mappings.UIDs()
   374  		options.GIDMap = mappings.GIDs()
   375  	}
   376  	parsedUIDMap, err := idtools.ParseIDMap(uidMapSlice, "UID")
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  	parsedGIDMap, err := idtools.ParseIDMap(gidMapSlice, "GID")
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  	options.UIDMap = append(options.UIDMap, parsedUIDMap...)
   385  	options.GIDMap = append(options.GIDMap, parsedGIDMap...)
   386  	if len(options.UIDMap) > 0 {
   387  		options.HostUIDMapping = false
   388  	}
   389  	if len(options.GIDMap) > 0 {
   390  		options.HostGIDMapping = false
   391  	}
   392  	return &options, nil
   393  }
   394  
   395  // StartWatcher starts a new SIGHUP go routine for the current config.
   396  func StartWatcher(rt *libpod.Runtime) {
   397  	// Setup the signal notifier
   398  	ch := make(chan os.Signal, 1)
   399  	signal.Notify(ch, syscall.SIGHUP)
   400  
   401  	go func() {
   402  		for {
   403  			// Block until the signal is received
   404  			logrus.Debugf("waiting for SIGHUP to reload configuration")
   405  			<-ch
   406  			if err := rt.Reload(); err != nil {
   407  				logrus.Errorf("Unable to reload configuration: %v", err)
   408  				continue
   409  			}
   410  		}
   411  	}()
   412  
   413  	logrus.Debugf("registered SIGHUP watcher for config")
   414  }