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 }