github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/domain/infra/abi/containers.go (about) 1 package abi 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/containers/buildah" 14 "github.com/containers/common/pkg/cgroups" 15 "github.com/containers/common/pkg/config" 16 "github.com/containers/image/v5/manifest" 17 "github.com/hanks177/podman/v4/libpod" 18 "github.com/hanks177/podman/v4/libpod/define" 19 "github.com/hanks177/podman/v4/libpod/events" 20 "github.com/hanks177/podman/v4/libpod/logs" 21 "github.com/hanks177/podman/v4/pkg/checkpoint" 22 "github.com/hanks177/podman/v4/pkg/domain/entities" 23 "github.com/hanks177/podman/v4/pkg/domain/entities/reports" 24 dfilters "github.com/hanks177/podman/v4/pkg/domain/filters" 25 "github.com/hanks177/podman/v4/pkg/domain/infra/abi/terminal" 26 "github.com/hanks177/podman/v4/pkg/errorhandling" 27 parallelctr "github.com/hanks177/podman/v4/pkg/parallel/ctr" 28 "github.com/hanks177/podman/v4/pkg/ps" 29 "github.com/hanks177/podman/v4/pkg/rootless" 30 "github.com/hanks177/podman/v4/pkg/signal" 31 "github.com/hanks177/podman/v4/pkg/specgen" 32 "github.com/hanks177/podman/v4/pkg/specgen/generate" 33 "github.com/hanks177/podman/v4/pkg/specgenutil" 34 "github.com/hanks177/podman/v4/pkg/util" 35 "github.com/containers/storage" 36 "github.com/pkg/errors" 37 "github.com/sirupsen/logrus" 38 ) 39 40 // getContainersAndInputByContext gets containers whether all, latest, or a slice of names/ids 41 // is specified. It also returns a list of the corresponding input name used to lookup each container. 42 func getContainersAndInputByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) { 43 var ctr *libpod.Container 44 ctrs = []*libpod.Container{} 45 46 switch { 47 case all: 48 ctrs, err = runtime.GetAllContainers() 49 case latest: 50 ctr, err = runtime.GetLatestContainer() 51 if err == nil { 52 rawInput = append(rawInput, ctr.ID()) 53 ctrs = append(ctrs, ctr) 54 } 55 default: 56 for _, n := range names { 57 ctr, e := runtime.LookupContainer(n) 58 if e != nil { 59 // Log all errors here, so callers don't need to. 60 logrus.Debugf("Error looking up container %q: %v", n, e) 61 if err == nil { 62 err = e 63 } 64 } else { 65 rawInput = append(rawInput, n) 66 ctrs = append(ctrs, ctr) 67 } 68 } 69 } 70 return 71 } 72 73 // getContainersByContext gets containers whether all, latest, or a slice of names/ids 74 // is specified. 75 func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) { 76 ctrs, _, err = getContainersAndInputByContext(all, latest, names, runtime) 77 return 78 } 79 80 // ContainerExists returns whether the container exists in container storage 81 func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) { 82 _, err := ic.Libpod.LookupContainer(nameOrID) 83 if err != nil { 84 if errors.Cause(err) != define.ErrNoSuchCtr { 85 return nil, err 86 } 87 if options.External { 88 // Check if container exists in storage 89 if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil { 90 err = nil 91 } 92 } 93 } 94 return &entities.BoolReport{Value: err == nil}, nil 95 } 96 97 func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { 98 ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) 99 if err != nil { 100 return nil, err 101 } 102 responses := make([]entities.WaitReport, 0, len(ctrs)) 103 for _, c := range ctrs { 104 response := entities.WaitReport{Id: c.ID()} 105 exitCode, err := c.WaitForConditionWithInterval(ctx, options.Interval, options.Condition...) 106 if err != nil { 107 response.Error = err 108 } else { 109 response.ExitCode = exitCode 110 } 111 responses = append(responses, response) 112 } 113 return responses, nil 114 } 115 116 func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { 117 ctrs, err := getContainersByContext(options.All, false, namesOrIds, ic.Libpod) 118 if err != nil { 119 return nil, err 120 } 121 report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) 122 for _, c := range ctrs { 123 err := c.Pause() 124 if err != nil && options.All && errors.Cause(err) == define.ErrCtrStateInvalid { 125 logrus.Debugf("Container %s is not running", c.ID()) 126 continue 127 } 128 report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) 129 } 130 return report, nil 131 } 132 133 func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { 134 ctrs, err := getContainersByContext(options.All, false, namesOrIds, ic.Libpod) 135 if err != nil { 136 return nil, err 137 } 138 report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) 139 for _, c := range ctrs { 140 err := c.Unpause() 141 if err != nil && options.All && errors.Cause(err) == define.ErrCtrStateInvalid { 142 logrus.Debugf("Container %s is not paused", c.ID()) 143 continue 144 } 145 report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) 146 } 147 return report, nil 148 } 149 func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { 150 names := namesOrIds 151 ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, names, ic.Libpod) 152 if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { 153 return nil, err 154 } 155 ctrMap := map[string]string{} 156 if len(rawInputs) == len(ctrs) { 157 for i := range ctrs { 158 ctrMap[ctrs[i].ID()] = rawInputs[i] 159 } 160 } 161 errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { 162 var err error 163 if options.Timeout != nil { 164 err = c.StopWithTimeout(*options.Timeout) 165 } else { 166 err = c.Stop() 167 } 168 if err != nil { 169 switch { 170 case errors.Cause(err) == define.ErrCtrStopped: 171 logrus.Debugf("Container %s is already stopped", c.ID()) 172 case options.All && errors.Cause(err) == define.ErrCtrStateInvalid: 173 logrus.Debugf("Container %s is not running, could not stop", c.ID()) 174 // container never created in OCI runtime 175 // docker parity: do nothing just return container id 176 case errors.Cause(err) == define.ErrCtrStateInvalid: 177 logrus.Debugf("Container %s is either not created on runtime or is in a invalid state", c.ID()) 178 default: 179 return err 180 } 181 } 182 err = c.Cleanup(ctx) 183 if err != nil { 184 // Issue #7384 and #11384: If the container is configured for 185 // auto-removal, it might already have been removed at this point. 186 // We still need to to cleanup since we do not know if the other cleanup process is successful 187 if c.AutoRemove() && (errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved)) { 188 return nil 189 } 190 return err 191 } 192 return nil 193 }) 194 if err != nil { 195 return nil, err 196 } 197 reports := make([]*entities.StopReport, 0, len(errMap)) 198 for ctr, err := range errMap { 199 report := new(entities.StopReport) 200 report.Id = ctr.ID() 201 if options.All { 202 report.RawInput = ctr.ID() 203 } else { 204 report.RawInput = ctrMap[ctr.ID()] 205 } 206 report.Err = err 207 reports = append(reports, report) 208 } 209 return reports, nil 210 } 211 212 func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { 213 filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) 214 for k, v := range options.Filters { 215 generatedFunc, err := dfilters.GeneratePruneContainerFilterFuncs(k, v, ic.Libpod) 216 if err != nil { 217 return nil, err 218 } 219 filterFuncs = append(filterFuncs, generatedFunc) 220 } 221 return ic.Libpod.PruneContainers(filterFuncs) 222 } 223 224 func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { 225 sig, err := signal.ParseSignalNameOrNumber(options.Signal) 226 if err != nil { 227 return nil, err 228 } 229 ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 230 if err != nil { 231 return nil, err 232 } 233 ctrMap := map[string]string{} 234 if len(rawInputs) == len(ctrs) { 235 for i := range ctrs { 236 ctrMap[ctrs[i].ID()] = rawInputs[i] 237 } 238 } 239 reports := make([]*entities.KillReport, 0, len(ctrs)) 240 for _, con := range ctrs { 241 err := con.Kill(uint(sig)) 242 if options.All && errors.Cause(err) == define.ErrCtrStateInvalid { 243 logrus.Debugf("Container %s is not running", con.ID()) 244 continue 245 } 246 reports = append(reports, &entities.KillReport{ 247 Id: con.ID(), 248 Err: err, 249 RawInput: ctrMap[con.ID()], 250 }) 251 } 252 return reports, nil 253 } 254 255 func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { 256 var ( 257 ctrs []*libpod.Container 258 err error 259 ) 260 261 if options.Running { 262 ctrs, err = ic.Libpod.GetRunningContainers() 263 if err != nil { 264 return nil, err 265 } 266 } else { 267 ctrs, err = getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 268 if err != nil { 269 return nil, err 270 } 271 } 272 273 reports := make([]*entities.RestartReport, 0, len(ctrs)) 274 for _, con := range ctrs { 275 timeout := con.StopTimeout() 276 if options.Timeout != nil { 277 timeout = *options.Timeout 278 } 279 reports = append(reports, &entities.RestartReport{ 280 Id: con.ID(), 281 Err: con.RestartWithTimeout(ctx, timeout), 282 }) 283 } 284 return reports, nil 285 } 286 287 func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Container, options entities.RmOptions) error { 288 err := ic.Libpod.RemoveContainer(ctx, ctr, options.Force, options.Volumes, options.Timeout) 289 if err == nil { 290 return nil 291 } 292 logrus.Debugf("Failed to remove container %s: %s", ctr.ID(), err.Error()) 293 switch errors.Cause(err) { 294 case define.ErrNoSuchCtr: 295 // Ignore if the container does not exist (anymore) when either 296 // it has been requested by the user of if the container is a 297 // service one. Service containers are removed along with its 298 // pods which in turn are removed along with their infra 299 // container. Hence, there is an inherent race when removing 300 // infra containers with service containers in parallel. 301 if options.Ignore || ctr.IsService() { 302 logrus.Debugf("Ignoring error (--allow-missing): %v", err) 303 return nil 304 } 305 case define.ErrCtrRemoved: 306 return nil 307 } 308 return err 309 } 310 311 func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*reports.RmReport, error) { 312 rmReports := []*reports.RmReport{} 313 314 names := namesOrIds 315 // Attempt to remove named containers directly from storage, if container is defined in libpod 316 // this will fail and code will fall through to removing the container from libpod.` 317 tmpNames := []string{} 318 for _, ctr := range names { 319 report := reports.RmReport{Id: ctr} 320 report.Err = ic.Libpod.RemoveStorageContainer(ctr, options.Force) 321 switch errors.Cause(report.Err) { 322 case nil: 323 // remove container names that we successfully deleted 324 rmReports = append(rmReports, &report) 325 case define.ErrNoSuchCtr, define.ErrCtrExists: 326 // There is still a potential this is a libpod container 327 tmpNames = append(tmpNames, ctr) 328 default: 329 if _, err := ic.Libpod.LookupContainer(ctr); errors.Cause(err) == define.ErrNoSuchCtr { 330 // remove container failed, but not a libpod container 331 rmReports = append(rmReports, &report) 332 continue 333 } 334 // attempt to remove as a libpod container 335 tmpNames = append(tmpNames, ctr) 336 } 337 } 338 names = tmpNames 339 340 ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) 341 if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { 342 // Failed to get containers. If force is specified, get the containers ID 343 // and evict them 344 if !options.Force { 345 return nil, err 346 } 347 348 for _, ctr := range names { 349 logrus.Debugf("Evicting container %q", ctr) 350 report := reports.RmReport{Id: ctr} 351 _, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes) 352 if err != nil { 353 if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr { 354 logrus.Debugf("Ignoring error (--allow-missing): %v", err) 355 rmReports = append(rmReports, &report) 356 continue 357 } 358 report.Err = err 359 rmReports = append(rmReports, &report) 360 continue 361 } 362 rmReports = append(rmReports, &report) 363 } 364 return rmReports, nil 365 } 366 367 alreadyRemoved := make(map[string]bool) 368 addReports := func(newReports []*reports.RmReport) { 369 for i := range newReports { 370 alreadyRemoved[newReports[i].Id] = true 371 rmReports = append(rmReports, newReports[i]) 372 } 373 } 374 if !options.All && options.Depend { 375 // Add additional containers based on dependencies to container map 376 for _, ctr := range ctrs { 377 if alreadyRemoved[ctr.ID()] { 378 continue 379 } 380 reports, err := ic.Libpod.RemoveDepend(ctx, ctr, options.Force, options.Volumes, options.Timeout) 381 if err != nil { 382 return rmReports, err 383 } 384 addReports(reports) 385 } 386 return rmReports, nil 387 } 388 389 // If --all is set, make sure to remove the infra containers' 390 // dependencies before doing the parallel removal below. 391 if options.All { 392 for _, ctr := range ctrs { 393 if alreadyRemoved[ctr.ID()] || !ctr.IsInfra() { 394 continue 395 } 396 reports, err := ic.Libpod.RemoveDepend(ctx, ctr, options.Force, options.Volumes, options.Timeout) 397 if err != nil { 398 return rmReports, err 399 } 400 addReports(reports) 401 } 402 } 403 404 errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { 405 if alreadyRemoved[c.ID()] { 406 return nil 407 } 408 return ic.removeContainer(ctx, c, options) 409 }) 410 if err != nil { 411 return nil, err 412 } 413 for ctr, err := range errMap { 414 if alreadyRemoved[ctr.ID()] { 415 continue 416 } 417 report := new(reports.RmReport) 418 report.Id = ctr.ID() 419 report.Err = err 420 rmReports = append(rmReports, report) 421 } 422 return rmReports, nil 423 } 424 425 func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { 426 if options.Latest { 427 ctr, err := ic.Libpod.GetLatestContainer() 428 if err != nil { 429 if errors.Is(err, define.ErrNoSuchCtr) { 430 return nil, []error{errors.Wrapf(err, "no containers to inspect")}, nil 431 } 432 return nil, nil, err 433 } 434 435 inspect, err := ctr.Inspect(options.Size) 436 if err != nil { 437 return nil, nil, err 438 } 439 440 return []*entities.ContainerInspectReport{ 441 { 442 InspectContainerData: inspect, 443 }, 444 }, nil, nil 445 } 446 var ( 447 reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds)) 448 errs = []error{} 449 ) 450 for _, name := range namesOrIds { 451 ctr, err := ic.Libpod.LookupContainer(name) 452 if err != nil { 453 // ErrNoSuchCtr is non-fatal, other errors will be 454 // treated as fatal. 455 if errors.Is(err, define.ErrNoSuchCtr) { 456 errs = append(errs, errors.Errorf("no such container %s", name)) 457 continue 458 } 459 return nil, nil, err 460 } 461 462 inspect, err := ctr.Inspect(options.Size) 463 if err != nil { 464 // ErrNoSuchCtr is non-fatal, other errors will be 465 // treated as fatal. 466 if errors.Is(err, define.ErrNoSuchCtr) { 467 errs = append(errs, errors.Errorf("no such container %s", name)) 468 continue 469 } 470 return nil, nil, err 471 } 472 473 reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: inspect}) 474 } 475 return reports, errs, nil 476 } 477 478 func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) { 479 var ( 480 container *libpod.Container 481 err error 482 ) 483 484 // Look up the container. 485 if options.Latest { 486 container, err = ic.Libpod.GetLatestContainer() 487 } else { 488 container, err = ic.Libpod.LookupContainer(options.NameOrID) 489 } 490 if err != nil { 491 return nil, errors.Wrap(err, "unable to lookup requested container") 492 } 493 494 // Run Top. 495 report := &entities.StringSliceReport{} 496 report.Value, err = container.Top(options.Descriptors) 497 return report, err 498 } 499 500 func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, options entities.CommitOptions) (*entities.CommitReport, error) { 501 var ( 502 mimeType string 503 ) 504 ctr, err := ic.Libpod.LookupContainer(nameOrID) 505 if err != nil { 506 return nil, err 507 } 508 rtc, err := ic.Libpod.GetConfig() 509 if err != nil { 510 return nil, err 511 } 512 switch options.Format { 513 case "oci": 514 mimeType = buildah.OCIv1ImageManifest 515 if len(options.Message) > 0 { 516 return nil, errors.Errorf("messages are only compatible with the docker image format (-f docker)") 517 } 518 case "docker": 519 mimeType = manifest.DockerV2Schema2MediaType 520 default: 521 return nil, errors.Errorf("unrecognized image format %q", options.Format) 522 } 523 524 sc := ic.Libpod.SystemContext() 525 coptions := buildah.CommitOptions{ 526 SignaturePolicyPath: rtc.Engine.SignaturePolicyPath, 527 ReportWriter: options.Writer, 528 SystemContext: sc, 529 PreferredManifestType: mimeType, 530 } 531 opts := libpod.ContainerCommitOptions{ 532 CommitOptions: coptions, 533 Pause: options.Pause, 534 IncludeVolumes: options.IncludeVolumes, 535 Message: options.Message, 536 Changes: options.Changes, 537 Author: options.Author, 538 Squash: options.Squash, 539 } 540 newImage, err := ctr.Commit(ctx, options.ImageName, opts) 541 if err != nil { 542 return nil, err 543 } 544 return &entities.CommitReport{Id: newImage.ID()}, nil 545 } 546 547 func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, options entities.ContainerExportOptions) error { 548 ctr, err := ic.Libpod.LookupContainer(nameOrID) 549 if err != nil { 550 return err 551 } 552 return ctr.Export(options.Output) 553 } 554 555 func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { 556 var ( 557 err error 558 cons []*libpod.Container 559 ) 560 checkOpts := libpod.ContainerCheckpointOptions{ 561 Keep: options.Keep, 562 TCPEstablished: options.TCPEstablished, 563 TargetFile: options.Export, 564 IgnoreRootfs: options.IgnoreRootFS, 565 IgnoreVolumes: options.IgnoreVolumes, 566 KeepRunning: options.LeaveRunning, 567 PreCheckPoint: options.PreCheckPoint, 568 WithPrevious: options.WithPrevious, 569 Compression: options.Compression, 570 PrintStats: options.PrintStats, 571 FileLocks: options.FileLocks, 572 CreateImage: options.CreateImage, 573 } 574 575 if options.All { 576 running := func(c *libpod.Container) bool { 577 state, _ := c.State() 578 return state == define.ContainerStateRunning 579 } 580 cons, err = ic.Libpod.GetContainers(running) 581 } else { 582 cons, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) 583 } 584 if err != nil { 585 return nil, err 586 } 587 reports := make([]*entities.CheckpointReport, 0, len(cons)) 588 for _, con := range cons { 589 criuStatistics, runtimeCheckpointDuration, err := con.Checkpoint(ctx, checkOpts) 590 reports = append(reports, &entities.CheckpointReport{ 591 Err: err, 592 Id: con.ID(), 593 RuntimeDuration: runtimeCheckpointDuration, 594 CRIUStatistics: criuStatistics, 595 }) 596 } 597 return reports, nil 598 } 599 600 func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) { 601 var ( 602 containers []*libpod.Container 603 checkpointImageImportErrors []error 604 err error 605 ) 606 607 restoreOptions := libpod.ContainerCheckpointOptions{ 608 Keep: options.Keep, 609 TCPEstablished: options.TCPEstablished, 610 TargetFile: options.Import, 611 Name: options.Name, 612 IgnoreRootfs: options.IgnoreRootFS, 613 IgnoreVolumes: options.IgnoreVolumes, 614 IgnoreStaticIP: options.IgnoreStaticIP, 615 IgnoreStaticMAC: options.IgnoreStaticMAC, 616 ImportPrevious: options.ImportPrevious, 617 Pod: options.Pod, 618 PrintStats: options.PrintStats, 619 } 620 621 filterFuncs := []libpod.ContainerFilter{ 622 func(c *libpod.Container) bool { 623 state, _ := c.State() 624 return state == define.ContainerStateExited 625 }, 626 } 627 628 switch { 629 case options.Import != "": 630 containers, err = checkpoint.CRImportCheckpointTar(ctx, ic.Libpod, options) 631 case options.All: 632 containers, err = ic.Libpod.GetContainers(filterFuncs...) 633 case options.Latest: 634 containers, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) 635 default: 636 for _, nameOrID := range namesOrIds { 637 logrus.Debugf("lookup container: %q", nameOrID) 638 ctr, err := ic.Libpod.LookupContainer(nameOrID) 639 if err == nil { 640 containers = append(containers, ctr) 641 } else { 642 // If container was not found, check if this is a checkpoint image 643 logrus.Debugf("lookup image: %q", nameOrID) 644 img, _, err := ic.Libpod.LibimageRuntime().LookupImage(nameOrID, nil) 645 if err != nil { 646 return nil, fmt.Errorf("no such container or image: %s", nameOrID) 647 } 648 restoreOptions.CheckpointImageID = img.ID() 649 mountPoint, err := img.Mount(ctx, nil, "") 650 defer func() { 651 if err := img.Unmount(true); err != nil { 652 logrus.Errorf("Failed to unmount image: %v", err) 653 } 654 }() 655 if err != nil { 656 return nil, err 657 } 658 importedContainers, err := checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options, mountPoint) 659 if err != nil { 660 // CRImportCheckpoint is expected to import exactly one container from checkpoint image 661 checkpointImageImportErrors = append( 662 checkpointImageImportErrors, 663 errors.Errorf("unable to import checkpoint from image: %q: %v", nameOrID, err), 664 ) 665 } else { 666 containers = append(containers, importedContainers[0]) 667 } 668 } 669 } 670 } 671 if err != nil { 672 return nil, err 673 } 674 675 reports := make([]*entities.RestoreReport, 0, len(containers)) 676 for _, con := range containers { 677 criuStatistics, runtimeRestoreDuration, err := con.Restore(ctx, restoreOptions) 678 reports = append(reports, &entities.RestoreReport{ 679 Err: err, 680 Id: con.ID(), 681 RuntimeDuration: runtimeRestoreDuration, 682 CRIUStatistics: criuStatistics, 683 }) 684 } 685 686 for _, importErr := range checkpointImageImportErrors { 687 reports = append(reports, &entities.RestoreReport{ 688 Err: importErr, 689 }) 690 } 691 692 return reports, nil 693 } 694 695 func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) { 696 warn, err := generate.CompleteSpec(ctx, ic.Libpod, s) 697 if err != nil { 698 return nil, err 699 } 700 // Print warnings 701 for _, w := range warn { 702 fmt.Fprintf(os.Stderr, "%s\n", w) 703 } 704 rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), ic.Libpod, s, false, nil) 705 if err != nil { 706 return nil, err 707 } 708 ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) 709 if err != nil { 710 return nil, err 711 } 712 return &entities.ContainerCreateReport{Id: ctr.ID()}, nil 713 } 714 715 func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error { 716 ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) 717 if err != nil { 718 return err 719 } 720 ctr := ctrs[0] 721 conState, err := ctr.State() 722 if err != nil { 723 return errors.Wrapf(err, "unable to determine state of %s", ctr.ID()) 724 } 725 if conState != define.ContainerStateRunning { 726 return errors.Errorf("you can only attach to running containers") 727 } 728 729 // If the container is in a pod, also set to recursively start dependencies 730 err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false) 731 if err != nil && errors.Cause(err) != define.ErrDetach { 732 return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) 733 } 734 os.Stdout.WriteString("\n") 735 return nil 736 } 737 738 func makeExecConfig(options entities.ExecOptions, rt *libpod.Runtime) (*libpod.ExecConfig, error) { 739 execConfig := new(libpod.ExecConfig) 740 execConfig.Command = options.Cmd 741 execConfig.Terminal = options.Tty 742 execConfig.Privileged = options.Privileged 743 execConfig.Environment = options.Envs 744 execConfig.User = options.User 745 execConfig.WorkDir = options.WorkDir 746 execConfig.DetachKeys = &options.DetachKeys 747 execConfig.PreserveFDs = options.PreserveFDs 748 execConfig.AttachStdin = options.Interactive 749 750 // Make an exit command 751 storageConfig := rt.StorageConfig() 752 runtimeConfig, err := rt.GetConfig() 753 if err != nil { 754 return nil, errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command") 755 } 756 // TODO: Add some ability to toggle syslog 757 exitCommandArgs, err := specgenutil.CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), false, true) 758 if err != nil { 759 return nil, errors.Wrapf(err, "error constructing exit command for exec session") 760 } 761 execConfig.ExitCommand = exitCommandArgs 762 763 return execConfig, nil 764 } 765 766 func checkExecPreserveFDs(options entities.ExecOptions) error { 767 if options.PreserveFDs > 0 { 768 entries, err := ioutil.ReadDir("/proc/self/fd") 769 if err != nil { 770 return err 771 } 772 773 m := make(map[int]bool) 774 for _, e := range entries { 775 i, err := strconv.Atoi(e.Name()) 776 if err != nil { 777 return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) 778 } 779 m[i] = true 780 } 781 782 for i := 3; i < 3+int(options.PreserveFDs); i++ { 783 if _, found := m[i]; !found { 784 return errors.New("invalid --preserve-fds=N specified. Not enough FDs available") 785 } 786 } 787 } 788 return nil 789 } 790 791 func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { 792 ec := define.ExecErrorCodeGeneric 793 err := checkExecPreserveFDs(options) 794 if err != nil { 795 return ec, err 796 } 797 ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) 798 if err != nil { 799 return ec, err 800 } 801 ctr := ctrs[0] 802 803 execConfig, err := makeExecConfig(options, ic.Libpod) 804 if err != nil { 805 return ec, err 806 } 807 808 ec, err = terminal.ExecAttachCtr(ctx, ctr, execConfig, &streams) 809 return define.TranslateExecErrorToExitCode(ec, err), err 810 } 811 812 func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { 813 err := checkExecPreserveFDs(options) 814 if err != nil { 815 return "", err 816 } 817 ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) 818 if err != nil { 819 return "", err 820 } 821 ctr := ctrs[0] 822 823 execConfig, err := makeExecConfig(options, ic.Libpod) 824 if err != nil { 825 return "", err 826 } 827 828 // Create and start the exec session 829 id, err := ctr.ExecCreate(execConfig) 830 if err != nil { 831 return "", err 832 } 833 834 // TODO: we should try and retrieve exit code if this fails. 835 if err := ctr.ExecStart(id); err != nil { 836 return "", err 837 } 838 return id, nil 839 } 840 841 func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { 842 reports := []*entities.ContainerStartReport{} 843 var exitCode = define.ExecErrorCodeGeneric 844 containersNamesOrIds := namesOrIds 845 all := options.All 846 if len(options.Filters) > 0 { 847 all = false 848 filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) 849 if len(options.Filters) > 0 { 850 for k, v := range options.Filters { 851 generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) 852 if err != nil { 853 return nil, err 854 } 855 filterFuncs = append(filterFuncs, generatedFunc) 856 } 857 } 858 candidates, err := ic.Libpod.GetContainers(filterFuncs...) 859 if err != nil { 860 return nil, err 861 } 862 containersNamesOrIds = []string{} 863 for _, candidate := range candidates { 864 if options.All { 865 containersNamesOrIds = append(containersNamesOrIds, candidate.ID()) 866 continue 867 } 868 for _, nameOrID := range namesOrIds { 869 if nameOrID == candidate.ID() || nameOrID == candidate.Name() { 870 containersNamesOrIds = append(containersNamesOrIds, nameOrID) 871 } 872 } 873 } 874 } 875 ctrs, rawInputs, err := getContainersAndInputByContext(all, options.Latest, containersNamesOrIds, ic.Libpod) 876 if err != nil { 877 return nil, err 878 } 879 // There can only be one container if attach was used 880 for i := range ctrs { 881 ctr := ctrs[i] 882 rawInput := ctr.ID() 883 if !options.All { 884 rawInput = rawInputs[i] 885 } 886 ctrState, err := ctr.State() 887 if err != nil { 888 return nil, err 889 } 890 ctrRunning := ctrState == define.ContainerStateRunning 891 892 if options.Attach { 893 err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning) 894 if errors.Cause(err) == define.ErrDetach { 895 // User manually detached 896 // Exit cleanly immediately 897 reports = append(reports, &entities.ContainerStartReport{ 898 Id: ctr.ID(), 899 RawInput: rawInput, 900 Err: nil, 901 ExitCode: 0, 902 }) 903 return reports, nil 904 } 905 906 if errors.Cause(err) == define.ErrWillDeadlock { 907 logrus.Debugf("Deadlock error: %v", err) 908 reports = append(reports, &entities.ContainerStartReport{ 909 Id: ctr.ID(), 910 RawInput: rawInput, 911 Err: err, 912 ExitCode: define.ExitCode(err), 913 }) 914 return reports, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID()) 915 } 916 917 if ctrRunning { 918 reports = append(reports, &entities.ContainerStartReport{ 919 Id: ctr.ID(), 920 RawInput: rawInput, 921 Err: nil, 922 ExitCode: 0, 923 }) 924 return reports, err 925 } 926 927 if err != nil { 928 reports = append(reports, &entities.ContainerStartReport{ 929 Id: ctr.ID(), 930 RawInput: rawInput, 931 Err: err, 932 ExitCode: exitCode, 933 }) 934 if ctr.AutoRemove() { 935 if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { 936 logrus.Errorf("Removing container %s: %v", ctr.ID(), err) 937 } 938 } 939 return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID()) 940 } 941 exitCode = ic.GetContainerExitCode(ctx, ctr) 942 reports = append(reports, &entities.ContainerStartReport{ 943 Id: ctr.ID(), 944 RawInput: rawInput, 945 Err: err, 946 ExitCode: exitCode, 947 }) 948 return reports, nil 949 } // end attach 950 951 // Start the container if it's not running already. 952 if !ctrRunning { 953 // Handle non-attach start 954 // If the container is in a pod, also set to recursively start dependencies 955 report := &entities.ContainerStartReport{ 956 Id: ctr.ID(), 957 RawInput: rawInput, 958 ExitCode: 125, 959 } 960 if err := ctr.Start(ctx, true); err != nil { 961 report.Err = err 962 if errors.Cause(err) == define.ErrWillDeadlock { 963 report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks") 964 reports = append(reports, report) 965 continue 966 } 967 report.Err = errors.Wrapf(err, "unable to start container %q", ctr.ID()) 968 reports = append(reports, report) 969 if ctr.AutoRemove() { 970 if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { 971 logrus.Errorf("Removing container %s: %v", ctr.ID(), err) 972 } 973 } 974 continue 975 } 976 report.ExitCode = 0 977 reports = append(reports, report) 978 } 979 } 980 return reports, nil 981 } 982 983 func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { 984 if options.Latest { 985 options.Last = 1 986 } 987 return ps.GetContainerLists(ic.Libpod, options) 988 } 989 990 func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entities.ListContainer, error) { 991 return ps.GetExternalContainerLists(ic.Libpod) 992 } 993 994 // Diff provides changes to given container 995 func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts entities.DiffOptions) (*entities.DiffReport, error) { 996 var ( 997 base string 998 parent string 999 ) 1000 if opts.Latest { 1001 ctnr, err := ic.Libpod.GetLatestContainer() 1002 if err != nil { 1003 return nil, errors.Wrap(err, "unable to get latest container") 1004 } 1005 base = ctnr.ID() 1006 } 1007 if len(namesOrIDs) > 0 { 1008 base = namesOrIDs[0] 1009 if len(namesOrIDs) > 1 { 1010 parent = namesOrIDs[1] 1011 } 1012 } 1013 changes, err := ic.Libpod.GetDiff(parent, base, opts.Type) 1014 return &entities.DiffReport{Changes: changes}, err 1015 } 1016 1017 func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { 1018 warn, err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec) 1019 if err != nil { 1020 return nil, err 1021 } 1022 // Print warnings 1023 for _, w := range warn { 1024 fmt.Fprintf(os.Stderr, "%s\n", w) 1025 } 1026 1027 rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec, false, nil) 1028 if err != nil { 1029 return nil, err 1030 } 1031 ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, optsN...) 1032 if err != nil { 1033 return nil, err 1034 } 1035 1036 if opts.CIDFile != "" { 1037 if err := util.CreateCidFile(opts.CIDFile, ctr.ID()); err != nil { 1038 return nil, err 1039 } 1040 } 1041 1042 report := entities.ContainerRunReport{Id: ctr.ID()} 1043 1044 if logrus.GetLevel() == logrus.DebugLevel { 1045 cgroupPath, err := ctr.CgroupPath() 1046 if err == nil { 1047 logrus.Debugf("container %q has CgroupParent %q", ctr.ID(), cgroupPath) 1048 } 1049 } 1050 if opts.Detach { 1051 // if the container was created as part of a pod, also start its dependencies, if any. 1052 if err := ctr.Start(ctx, true); err != nil { 1053 // This means the command did not exist 1054 report.ExitCode = define.ExitCode(err) 1055 return &report, err 1056 } 1057 1058 return &report, nil 1059 } 1060 1061 // if the container was created as part of a pod, also start its dependencies, if any. 1062 if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true); err != nil { 1063 // We've manually detached from the container 1064 // Do not perform cleanup, or wait for container exit code 1065 // Just exit immediately 1066 if errors.Cause(err) == define.ErrDetach { 1067 report.ExitCode = 0 1068 return &report, nil 1069 } 1070 if opts.Rm { 1071 var timeout *uint 1072 if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false, timeout); deleteError != nil { 1073 logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) 1074 } 1075 } 1076 if errors.Cause(err) == define.ErrWillDeadlock { 1077 logrus.Debugf("Deadlock error on %q: %v", ctr.ID(), err) 1078 report.ExitCode = define.ExitCode(err) 1079 return &report, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID()) 1080 } 1081 report.ExitCode = define.ExitCode(err) 1082 return &report, err 1083 } 1084 report.ExitCode = ic.GetContainerExitCode(ctx, ctr) 1085 if opts.Rm && !ctr.ShouldRestart(ctx) { 1086 var timeout *uint 1087 if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout); err != nil { 1088 if errors.Cause(err) == define.ErrNoSuchCtr || 1089 errors.Cause(err) == define.ErrCtrRemoved { 1090 logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID()) 1091 } else { 1092 logrus.Errorf("Removing container %s: %v", ctr.ID(), err) 1093 } 1094 } 1095 } 1096 return &report, nil 1097 } 1098 1099 func (ic *ContainerEngine) GetContainerExitCode(ctx context.Context, ctr *libpod.Container) int { 1100 exitCode, err := ctr.Wait(ctx) 1101 if err == nil { 1102 return int(exitCode) 1103 } 1104 if errors.Cause(err) != define.ErrNoSuchCtr { 1105 logrus.Errorf("Could not retrieve exit code: %v", err) 1106 return define.ExecErrorCodeNotFound 1107 } 1108 // Make 4 attempt with 0.25s backoff between each for 1 second total 1109 var event *events.Event 1110 for i := 0; i < 4; i++ { 1111 event, err = ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) 1112 if err != nil { 1113 time.Sleep(250 * time.Millisecond) 1114 continue 1115 } 1116 return event.ContainerExitCode 1117 } 1118 logrus.Errorf("Could not retrieve exit code from event: %v", err) 1119 return define.ExecErrorCodeNotFound 1120 } 1121 1122 func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { 1123 if options.StdoutWriter == nil && options.StderrWriter == nil { 1124 return errors.New("no io.Writer set for container logs") 1125 } 1126 1127 var wg sync.WaitGroup 1128 1129 ctrs, err := getContainersByContext(false, options.Latest, containers, ic.Libpod) 1130 if err != nil { 1131 return err 1132 } 1133 1134 logOpts := &logs.LogOptions{ 1135 Multi: len(ctrs) > 1, 1136 Details: options.Details, 1137 Follow: options.Follow, 1138 Since: options.Since, 1139 Until: options.Until, 1140 Tail: options.Tail, 1141 Timestamps: options.Timestamps, 1142 Colors: options.Colors, 1143 UseName: options.Names, 1144 WaitGroup: &wg, 1145 } 1146 1147 chSize := len(ctrs) * int(options.Tail) 1148 if chSize <= 0 { 1149 chSize = 1 1150 } 1151 logChannel := make(chan *logs.LogLine, chSize) 1152 1153 if err := ic.Libpod.Log(ctx, ctrs, logOpts, logChannel); err != nil { 1154 return err 1155 } 1156 1157 go func() { 1158 wg.Wait() 1159 close(logChannel) 1160 }() 1161 1162 for line := range logChannel { 1163 line.Write(options.StdoutWriter, options.StderrWriter, logOpts) 1164 } 1165 1166 return nil 1167 } 1168 1169 func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { 1170 reports := []*entities.ContainerCleanupReport{} 1171 ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 1172 if err != nil { 1173 return nil, err 1174 } 1175 for _, ctr := range ctrs { 1176 var err error 1177 report := entities.ContainerCleanupReport{Id: ctr.ID()} 1178 1179 if options.Exec != "" { 1180 if options.Remove { 1181 if err := ctr.ExecRemove(options.Exec, false); err != nil { 1182 return nil, err 1183 } 1184 } else { 1185 if err := ctr.ExecCleanup(options.Exec); err != nil { 1186 return nil, err 1187 } 1188 } 1189 return []*entities.ContainerCleanupReport{}, nil 1190 } 1191 1192 if options.Remove && !ctr.ShouldRestart(ctx) { 1193 var timeout *uint 1194 err = ic.Libpod.RemoveContainer(ctx, ctr, false, true, timeout) 1195 if err != nil { 1196 report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) 1197 } 1198 } else { 1199 err := ctr.Cleanup(ctx) 1200 if err != nil { 1201 report.CleanErr = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID()) 1202 } 1203 } 1204 1205 if options.RemoveImage { 1206 _, imageName := ctr.Image() 1207 imageEngine := ImageEngine{Libpod: ic.Libpod} 1208 _, rmErrors := imageEngine.Remove(ctx, []string{imageName}, entities.ImageRemoveOptions{}) 1209 report.RmiErr = errorhandling.JoinErrors(rmErrors) 1210 } 1211 1212 reports = append(reports, &report) 1213 } 1214 return reports, nil 1215 } 1216 1217 func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { 1218 ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 1219 if err != nil { 1220 return nil, err 1221 } 1222 reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) 1223 for _, ctr := range ctrs { 1224 report := entities.ContainerInitReport{Id: ctr.ID()} 1225 err := ctr.Init(ctx, ctr.PodID() != "") 1226 1227 // If we're initializing all containers, ignore invalid state errors 1228 if options.All && errors.Cause(err) == define.ErrCtrStateInvalid { 1229 err = nil 1230 } 1231 report.Err = err 1232 reports = append(reports, &report) 1233 } 1234 return reports, nil 1235 } 1236 1237 func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { 1238 if os.Geteuid() != 0 { 1239 if driver := ic.Libpod.StorageConfig().GraphDriverName; driver != "vfs" { 1240 // Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part 1241 // of the mount command. 1242 return nil, fmt.Errorf("cannot mount using driver %s in rootless mode", driver) 1243 } 1244 1245 became, ret, err := rootless.BecomeRootInUserNS("") 1246 if err != nil { 1247 return nil, err 1248 } 1249 if became { 1250 os.Exit(ret) 1251 } 1252 } 1253 reports := []*entities.ContainerMountReport{} 1254 // Attempt to mount named containers directly from storage, 1255 // this will fail and code will fall through to removing the container from libpod.` 1256 names := []string{} 1257 for _, ctr := range nameOrIDs { 1258 report := entities.ContainerMountReport{Id: ctr} 1259 if report.Path, report.Err = ic.Libpod.MountStorageContainer(ctr); report.Err != nil { 1260 names = append(names, ctr) 1261 } else { 1262 reports = append(reports, &report) 1263 } 1264 } 1265 1266 ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) 1267 if err != nil { 1268 return nil, err 1269 } 1270 for _, ctr := range ctrs { 1271 report := entities.ContainerMountReport{Id: ctr.ID()} 1272 report.Path, report.Err = ctr.Mount() 1273 reports = append(reports, &report) 1274 } 1275 if len(reports) > 0 { 1276 return reports, nil 1277 } 1278 1279 storageCtrs, err := ic.Libpod.StorageContainers() 1280 if err != nil { 1281 return nil, err 1282 } 1283 1284 for _, sctr := range storageCtrs { 1285 mounted, path, err := ic.Libpod.IsStorageContainerMounted(sctr.ID) 1286 if err != nil { 1287 return nil, err 1288 } 1289 1290 var name string 1291 if len(sctr.Names) > 0 { 1292 name = sctr.Names[0] 1293 } 1294 if mounted { 1295 reports = append(reports, &entities.ContainerMountReport{ 1296 Id: sctr.ID, 1297 Name: name, 1298 Path: path, 1299 }) 1300 } 1301 } 1302 1303 // No containers were passed, so we send back what is mounted 1304 ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod) 1305 if err != nil { 1306 return nil, err 1307 } 1308 for _, ctr := range ctrs { 1309 mounted, path, err := ctr.Mounted() 1310 if err != nil { 1311 return nil, err 1312 } 1313 1314 if mounted { 1315 reports = append(reports, &entities.ContainerMountReport{ 1316 Id: ctr.ID(), 1317 Name: ctr.Name(), 1318 Path: path, 1319 }) 1320 } 1321 } 1322 1323 return reports, nil 1324 } 1325 1326 func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { 1327 reports := []*entities.ContainerUnmountReport{} 1328 names := []string{} 1329 if options.All { 1330 storageCtrs, err := ic.Libpod.StorageContainers() 1331 if err != nil { 1332 return nil, err 1333 } 1334 for _, sctr := range storageCtrs { 1335 mounted, _, _ := ic.Libpod.IsStorageContainerMounted(sctr.ID) 1336 if mounted { 1337 report := entities.ContainerUnmountReport{Id: sctr.ID} 1338 if _, report.Err = ic.Libpod.UnmountStorageContainer(sctr.ID, options.Force); report.Err != nil { 1339 if errors.Cause(report.Err) != define.ErrCtrExists { 1340 reports = append(reports, &report) 1341 } 1342 } else { 1343 reports = append(reports, &report) 1344 } 1345 } 1346 } 1347 } 1348 for _, ctr := range nameOrIDs { 1349 report := entities.ContainerUnmountReport{Id: ctr} 1350 if _, report.Err = ic.Libpod.UnmountStorageContainer(ctr, options.Force); report.Err != nil { 1351 names = append(names, ctr) 1352 } else { 1353 reports = append(reports, &report) 1354 } 1355 } 1356 ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) 1357 if err != nil { 1358 return nil, err 1359 } 1360 for _, ctr := range ctrs { 1361 state, err := ctr.State() 1362 if err != nil { 1363 logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error()) 1364 continue 1365 } 1366 if state == define.ContainerStateRunning { 1367 logrus.Debugf("Error umounting container %s, is running", ctr.ID()) 1368 continue 1369 } 1370 1371 report := entities.ContainerUnmountReport{Id: ctr.ID()} 1372 if err := ctr.Unmount(options.Force); err != nil { 1373 if options.All && errors.Cause(err) == storage.ErrLayerNotMounted { 1374 logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID()) 1375 continue 1376 } 1377 report.Err = errors.Wrapf(err, "error unmounting container %s", ctr.ID()) 1378 } 1379 reports = append(reports, &report) 1380 } 1381 return reports, nil 1382 } 1383 1384 // GetConfig returns a copy of the configuration used by the runtime 1385 func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { 1386 return ic.Libpod.GetConfig() 1387 } 1388 1389 func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { 1390 ctrs, err := getContainersByContext(options.All, options.Latest, []string{nameOrID}, ic.Libpod) 1391 if err != nil { 1392 return nil, err 1393 } 1394 reports := []*entities.ContainerPortReport{} 1395 for _, con := range ctrs { 1396 state, err := con.State() 1397 if err != nil { 1398 return nil, err 1399 } 1400 if state != define.ContainerStateRunning { 1401 continue 1402 } 1403 portmappings, err := con.PortMappings() 1404 if err != nil { 1405 return nil, err 1406 } 1407 if len(portmappings) > 0 { 1408 reports = append(reports, &entities.ContainerPortReport{ 1409 Id: con.ID(), 1410 Ports: portmappings, 1411 }) 1412 } 1413 } 1414 return reports, nil 1415 } 1416 1417 // Shutdown Libpod engine 1418 func (ic *ContainerEngine) Shutdown(_ context.Context) { 1419 shutdownSync.Do(func() { 1420 _ = ic.Libpod.Shutdown(false) 1421 }) 1422 } 1423 1424 func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) (statsChan chan entities.ContainerStatsReport, err error) { 1425 if options.Interval < 1 { 1426 return nil, errors.New("Invalid interval, must be a positive number greater zero") 1427 } 1428 if rootless.IsRootless() { 1429 unified, err := cgroups.IsCgroup2UnifiedMode() 1430 if err != nil { 1431 return nil, err 1432 } 1433 if !unified { 1434 return nil, errors.New("stats is not supported in rootless mode without cgroups v2") 1435 } 1436 } 1437 statsChan = make(chan entities.ContainerStatsReport, 1) 1438 1439 containerFunc := ic.Libpod.GetRunningContainers 1440 queryAll := false 1441 switch { 1442 case options.Latest: 1443 containerFunc = func() ([]*libpod.Container, error) { 1444 lastCtr, err := ic.Libpod.GetLatestContainer() 1445 if err != nil { 1446 return nil, err 1447 } 1448 return []*libpod.Container{lastCtr}, nil 1449 } 1450 case len(namesOrIds) > 0: 1451 containerFunc = func() ([]*libpod.Container, error) { return ic.Libpod.GetContainersByList(namesOrIds) } 1452 default: 1453 // No containers, no latest -> query all! 1454 queryAll = true 1455 containerFunc = ic.Libpod.GetAllContainers 1456 } 1457 1458 go func() { 1459 defer close(statsChan) 1460 var ( 1461 err error 1462 containers []*libpod.Container 1463 containerStats map[string]*define.ContainerStats 1464 ) 1465 containerStats = make(map[string]*define.ContainerStats) 1466 1467 stream: // label to flatten the scope 1468 select { 1469 case <-ctx.Done(): 1470 // client cancelled 1471 logrus.Debugf("Container stats stopped: context cancelled") 1472 return 1473 default: 1474 // just fall through and do work 1475 } 1476 1477 // Anonymous func to easily use the return values for streaming. 1478 computeStats := func() ([]define.ContainerStats, error) { 1479 containers, err = containerFunc() 1480 if err != nil { 1481 return nil, errors.Wrapf(err, "unable to get list of containers") 1482 } 1483 1484 reportStats := []define.ContainerStats{} 1485 for _, ctr := range containers { 1486 stats, err := ctr.GetContainerStats(containerStats[ctr.ID()]) 1487 if err != nil { 1488 cause := errors.Cause(err) 1489 if queryAll && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) { 1490 continue 1491 } 1492 if cause == cgroups.ErrCgroupV1Rootless { 1493 err = cause 1494 } 1495 return nil, err 1496 } 1497 1498 containerStats[ctr.ID()] = stats 1499 reportStats = append(reportStats, *stats) 1500 } 1501 return reportStats, nil 1502 } 1503 1504 report := entities.ContainerStatsReport{} 1505 report.Stats, report.Error = computeStats() 1506 statsChan <- report 1507 1508 if report.Error != nil || !options.Stream { 1509 return 1510 } 1511 1512 time.Sleep(time.Second * time.Duration(options.Interval)) 1513 goto stream 1514 }() 1515 1516 return statsChan, nil 1517 } 1518 1519 // ShouldRestart returns whether the container should be restarted 1520 func (ic *ContainerEngine) ShouldRestart(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { 1521 ctr, err := ic.Libpod.LookupContainer(nameOrID) 1522 if err != nil { 1523 return nil, err 1524 } 1525 1526 return &entities.BoolReport{Value: ctr.ShouldRestart(ctx)}, nil 1527 } 1528 1529 // ContainerRename renames the given container. 1530 func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error { 1531 ctr, err := ic.Libpod.LookupContainer(nameOrID) 1532 if err != nil { 1533 return err 1534 } 1535 1536 if _, err := ic.Libpod.RenameContainer(ctx, ctr, opts.NewName); err != nil { 1537 return err 1538 } 1539 1540 return nil 1541 } 1542 1543 func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts entities.ContainerCloneOptions) (*entities.ContainerCreateReport, error) { 1544 spec := specgen.NewSpecGenerator(ctrCloneOpts.Image, ctrCloneOpts.CreateOpts.RootFS) 1545 var c *libpod.Container 1546 c, _, err := generate.ConfigToSpec(ic.Libpod, spec, ctrCloneOpts.ID) 1547 if err != nil { 1548 return nil, err 1549 } 1550 1551 if ctrCloneOpts.CreateOpts.Pod != "" { 1552 pod, err := ic.Libpod.LookupPod(ctrCloneOpts.CreateOpts.Pod) 1553 if err != nil { 1554 return nil, err 1555 } 1556 1557 if len(spec.Networks) > 0 && pod.SharesNet() { 1558 logrus.Warning("resetting network config, cannot specify a network other than the pod's when sharing the net namespace") 1559 spec.Networks = nil 1560 spec.NetworkOptions = nil 1561 } 1562 1563 allNamespaces := []struct { 1564 isShared bool 1565 value *specgen.Namespace 1566 }{ 1567 {pod.SharesPID(), &spec.PidNS}, 1568 {pod.SharesNet(), &spec.NetNS}, 1569 {pod.SharesCgroup(), &spec.CgroupNS}, 1570 {pod.SharesIPC(), &spec.IpcNS}, 1571 {pod.SharesUTS(), &spec.UtsNS}, 1572 } 1573 1574 printWarning := false 1575 for _, n := range allNamespaces { 1576 if n.isShared && !n.value.IsDefault() { 1577 *n.value = specgen.Namespace{NSMode: specgen.Default} 1578 printWarning = true 1579 } 1580 } 1581 if printWarning { 1582 logrus.Warning("At least one namespace was reset to the default configuration") 1583 } 1584 } 1585 1586 err = specgenutil.FillOutSpecGen(spec, &ctrCloneOpts.CreateOpts, []string{}) 1587 if err != nil { 1588 return nil, err 1589 } 1590 out, err := generate.CompleteSpec(ctx, ic.Libpod, spec) 1591 if err != nil { 1592 return nil, err 1593 } 1594 1595 // Print warnings 1596 if len(out) > 0 { 1597 for _, w := range out { 1598 fmt.Println("Could not properly complete the spec as expected:") 1599 fmt.Fprintf(os.Stderr, "%s\n", w) 1600 } 1601 } 1602 1603 if len(ctrCloneOpts.CreateOpts.Name) > 0 { 1604 spec.Name = ctrCloneOpts.CreateOpts.Name 1605 } else { 1606 n := c.Name() 1607 _, err := ic.Libpod.LookupContainer(c.Name() + "-clone") 1608 if err == nil { 1609 n += "-clone" 1610 } 1611 switch { 1612 case strings.Contains(n, "-clone"): 1613 ind := strings.Index(n, "-clone") + 6 1614 num, _ := strconv.Atoi(n[ind:]) 1615 if num == 0 { // clone1 is hard to get with this logic, just check for it here. 1616 _, err = ic.Libpod.LookupContainer(n + "1") 1617 if err != nil { 1618 spec.Name = n + "1" 1619 break 1620 } 1621 } else { 1622 n = n[0:ind] 1623 } 1624 err = nil 1625 count := num 1626 for err == nil { 1627 count++ 1628 tempN := n + strconv.Itoa(count) 1629 _, err = ic.Libpod.LookupContainer(tempN) 1630 } 1631 n += strconv.Itoa(count) 1632 spec.Name = n 1633 default: 1634 spec.Name = c.Name() + "-clone" 1635 } 1636 } 1637 1638 rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), ic.Libpod, spec, true, c) 1639 if err != nil { 1640 return nil, err 1641 } 1642 ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) 1643 if err != nil { 1644 return nil, err 1645 } 1646 1647 if ctrCloneOpts.Destroy { 1648 var time *uint 1649 err = ic.Libpod.RemoveContainer(context.Background(), c, ctrCloneOpts.Force, false, time) 1650 if err != nil { 1651 return nil, err 1652 } 1653 } 1654 1655 if ctrCloneOpts.Run { 1656 if err := ctr.Start(ctx, true); err != nil { 1657 return nil, err 1658 } 1659 } 1660 1661 return &entities.ContainerCreateReport{Id: ctr.ID()}, nil 1662 }