github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/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/config" 15 "github.com/containers/image/v5/manifest" 16 "github.com/containers/podman/v2/libpod" 17 "github.com/containers/podman/v2/libpod/define" 18 "github.com/containers/podman/v2/libpod/events" 19 lpfilters "github.com/containers/podman/v2/libpod/filters" 20 "github.com/containers/podman/v2/libpod/image" 21 "github.com/containers/podman/v2/libpod/logs" 22 "github.com/containers/podman/v2/pkg/cgroups" 23 "github.com/containers/podman/v2/pkg/checkpoint" 24 "github.com/containers/podman/v2/pkg/domain/entities" 25 "github.com/containers/podman/v2/pkg/domain/infra/abi/terminal" 26 parallelctr "github.com/containers/podman/v2/pkg/parallel/ctr" 27 "github.com/containers/podman/v2/pkg/ps" 28 "github.com/containers/podman/v2/pkg/rootless" 29 "github.com/containers/podman/v2/pkg/signal" 30 "github.com/containers/podman/v2/pkg/specgen" 31 "github.com/containers/podman/v2/pkg/specgen/generate" 32 "github.com/containers/podman/v2/pkg/util" 33 "github.com/containers/storage" 34 "github.com/pkg/errors" 35 "github.com/sirupsen/logrus" 36 ) 37 38 // getContainersAndInputByContext gets containers whether all, latest, or a slice of names/ids 39 // is specified. It also returns a list of the corresponding input name used to lookup each container. 40 func getContainersAndInputByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) { 41 var ctr *libpod.Container 42 ctrs = []*libpod.Container{} 43 44 switch { 45 case all: 46 ctrs, err = runtime.GetAllContainers() 47 case latest: 48 ctr, err = runtime.GetLatestContainer() 49 if err == nil { 50 rawInput = append(rawInput, ctr.ID()) 51 ctrs = append(ctrs, ctr) 52 } 53 default: 54 for _, n := range names { 55 ctr, e := runtime.LookupContainer(n) 56 if e != nil { 57 // Log all errors here, so callers don't need to. 58 logrus.Debugf("Error looking up container %q: %v", n, e) 59 if err == nil { 60 err = e 61 } 62 } else { 63 rawInput = append(rawInput, n) 64 ctrs = append(ctrs, ctr) 65 } 66 } 67 } 68 return 69 } 70 71 // getContainersByContext gets containers whether all, latest, or a slice of names/ids 72 // is specified. 73 func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) { 74 ctrs, _, err = getContainersAndInputByContext(all, latest, names, runtime) 75 return 76 } 77 78 // ContainerExists returns whether the container exists in container storage 79 func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) { 80 _, err := ic.Libpod.LookupContainer(nameOrID) 81 if err != nil { 82 if errors.Cause(err) != define.ErrNoSuchCtr { 83 return nil, err 84 } 85 if options.External { 86 // Check if container exists in storage 87 if _, storageErr := ic.Libpod.StorageContainer(nameOrID); storageErr == nil { 88 err = nil 89 } 90 } 91 } 92 return &entities.BoolReport{Value: err == nil}, nil 93 } 94 95 func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { 96 ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) 97 if err != nil { 98 return nil, err 99 } 100 responses := make([]entities.WaitReport, 0, len(ctrs)) 101 for _, c := range ctrs { 102 response := entities.WaitReport{Id: c.ID()} 103 exitCode, err := c.WaitForConditionWithInterval(options.Interval, options.Condition) 104 if err != nil { 105 response.Error = err 106 } else { 107 response.ExitCode = exitCode 108 } 109 responses = append(responses, response) 110 } 111 return responses, nil 112 } 113 114 func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { 115 var ( 116 err error 117 ) 118 ctrs := []*libpod.Container{} //nolint 119 if options.All { 120 ctrs, err = ic.Libpod.GetAllContainers() 121 } else { 122 ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) 123 } 124 if err != nil { 125 return nil, err 126 } 127 report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) 128 for _, c := range ctrs { 129 err := c.Pause() 130 report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) 131 } 132 return report, nil 133 } 134 135 func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { 136 var ( 137 err error 138 ) 139 ctrs := []*libpod.Container{} //nolint 140 if options.All { 141 ctrs, err = ic.Libpod.GetAllContainers() 142 } else { 143 ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) 144 } 145 if err != nil { 146 return nil, err 147 } 148 report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) 149 for _, c := range ctrs { 150 err := c.Unpause() 151 report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) 152 } 153 return report, nil 154 } 155 func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { 156 names := namesOrIds 157 for _, cidFile := range options.CIDFiles { 158 content, err := ioutil.ReadFile(cidFile) 159 if err != nil { 160 return nil, errors.Wrap(err, "error reading CIDFile") 161 } 162 id := strings.Split(string(content), "\n")[0] 163 names = append(names, id) 164 } 165 ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) 166 if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { 167 return nil, err 168 } 169 errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { 170 var err error 171 if options.Timeout != nil { 172 err = c.StopWithTimeout(*options.Timeout) 173 } else { 174 err = c.Stop() 175 } 176 if err != nil { 177 switch { 178 case errors.Cause(err) == define.ErrCtrStopped: 179 logrus.Debugf("Container %s is already stopped", c.ID()) 180 case options.All && errors.Cause(err) == define.ErrCtrStateInvalid: 181 logrus.Debugf("Container %s is not running, could not stop", c.ID()) 182 default: 183 return err 184 } 185 } 186 if c.AutoRemove() { 187 // Issue #7384: if the container is configured for 188 // auto-removal, it might already have been removed at 189 // this point. 190 return nil 191 } 192 return c.Cleanup(ctx) 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 report.Err = err 202 reports = append(reports, report) 203 } 204 return reports, nil 205 } 206 207 func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { 208 filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) 209 for k, v := range options.Filters { 210 generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) 211 if err != nil { 212 return nil, err 213 } 214 filterFuncs = append(filterFuncs, generatedFunc) 215 } 216 return ic.pruneContainersHelper(filterFuncs) 217 } 218 219 func (ic *ContainerEngine) pruneContainersHelper(filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) { 220 prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs) 221 if err != nil { 222 return nil, err 223 } 224 report := entities.ContainerPruneReport{ 225 ID: prunedContainers, 226 Err: pruneErrors, 227 } 228 return &report, nil 229 } 230 231 func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { 232 sig, err := signal.ParseSignalNameOrNumber(options.Signal) 233 if err != nil { 234 return nil, err 235 } 236 ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 237 if err != nil { 238 return nil, err 239 } 240 reports := make([]*entities.KillReport, 0, len(ctrs)) 241 for _, con := range ctrs { 242 reports = append(reports, &entities.KillReport{ 243 Id: con.ID(), 244 Err: con.Kill(uint(sig)), 245 }) 246 } 247 return reports, nil 248 } 249 func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { 250 var ( 251 ctrs []*libpod.Container 252 err error 253 ) 254 255 if options.Running { 256 ctrs, err = ic.Libpod.GetRunningContainers() 257 if err != nil { 258 return nil, err 259 } 260 } else { 261 ctrs, err = getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 262 if err != nil { 263 return nil, err 264 } 265 } 266 267 reports := make([]*entities.RestartReport, 0, len(ctrs)) 268 for _, con := range ctrs { 269 timeout := con.StopTimeout() 270 if options.Timeout != nil { 271 timeout = *options.Timeout 272 } 273 reports = append(reports, &entities.RestartReport{ 274 Id: con.ID(), 275 Err: con.RestartWithTimeout(ctx, timeout), 276 }) 277 } 278 return reports, nil 279 } 280 281 func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { 282 reports := []*entities.RmReport{} 283 284 names := namesOrIds 285 for _, cidFile := range options.CIDFiles { 286 content, err := ioutil.ReadFile(cidFile) 287 if err != nil { 288 return nil, errors.Wrap(err, "error reading CIDFile") 289 } 290 id := strings.Split(string(content), "\n")[0] 291 names = append(names, id) 292 } 293 294 // Attempt to remove named containers directly from storage, if container is defined in libpod 295 // this will fail and code will fall through to removing the container from libpod.` 296 tmpNames := []string{} 297 for _, ctr := range names { 298 report := entities.RmReport{Id: ctr} 299 if err := ic.Libpod.RemoveStorageContainer(ctr, options.Force); err != nil { 300 // remove container names that we successfully deleted 301 tmpNames = append(tmpNames, ctr) 302 } else { 303 reports = append(reports, &report) 304 } 305 } 306 if len(tmpNames) < len(names) { 307 names = tmpNames 308 } 309 310 ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) 311 if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { 312 // Failed to get containers. If force is specified, get the containers ID 313 // and evict them 314 if !options.Force { 315 return nil, err 316 } 317 318 for _, ctr := range names { 319 logrus.Debugf("Evicting container %q", ctr) 320 report := entities.RmReport{Id: ctr} 321 id, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes) 322 if err != nil { 323 if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr { 324 logrus.Debugf("Ignoring error (--allow-missing): %v", err) 325 reports = append(reports, &report) 326 continue 327 } 328 report.Err = errors.Wrapf(err, "failed to evict container: %q", id) 329 reports = append(reports, &report) 330 continue 331 } 332 reports = append(reports, &report) 333 } 334 return reports, nil 335 } 336 337 errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { 338 err := ic.Libpod.RemoveContainer(ctx, c, options.Force, options.Volumes) 339 if err != nil { 340 if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr { 341 logrus.Debugf("Ignoring error (--allow-missing): %v", err) 342 return nil 343 } 344 logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error()) 345 } 346 return err 347 }) 348 if err != nil { 349 return nil, err 350 } 351 for ctr, err := range errMap { 352 report := new(entities.RmReport) 353 report.Id = ctr.ID() 354 report.Err = err 355 reports = append(reports, report) 356 } 357 return reports, nil 358 } 359 360 func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { 361 if options.Latest { 362 ctr, err := ic.Libpod.GetLatestContainer() 363 if err != nil { 364 if errors.Cause(err) == define.ErrNoSuchCtr { 365 return nil, []error{errors.Wrapf(err, "no containers to inspect")}, nil 366 } 367 return nil, nil, err 368 } 369 370 inspect, err := ctr.Inspect(options.Size) 371 if err != nil { 372 return nil, nil, err 373 } 374 375 return []*entities.ContainerInspectReport{ 376 { 377 InspectContainerData: inspect, 378 }, 379 }, nil, nil 380 } 381 var ( 382 reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds)) 383 errs = []error{} 384 ) 385 for _, name := range namesOrIds { 386 ctr, err := ic.Libpod.LookupContainer(name) 387 if err != nil { 388 // ErrNoSuchCtr is non-fatal, other errors will be 389 // treated as fatal. 390 if errors.Cause(err) == define.ErrNoSuchCtr { 391 errs = append(errs, errors.Errorf("no such container %s", name)) 392 continue 393 } 394 return nil, nil, err 395 } 396 397 inspect, err := ctr.Inspect(options.Size) 398 if err != nil { 399 return nil, nil, err 400 } 401 402 reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: inspect}) 403 } 404 return reports, errs, nil 405 } 406 407 func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) { 408 var ( 409 container *libpod.Container 410 err error 411 ) 412 413 // Look up the container. 414 if options.Latest { 415 container, err = ic.Libpod.GetLatestContainer() 416 } else { 417 container, err = ic.Libpod.LookupContainer(options.NameOrID) 418 } 419 if err != nil { 420 return nil, errors.Wrap(err, "unable to lookup requested container") 421 } 422 423 // Run Top. 424 report := &entities.StringSliceReport{} 425 report.Value, err = container.Top(options.Descriptors) 426 return report, err 427 } 428 429 func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, options entities.CommitOptions) (*entities.CommitReport, error) { 430 var ( 431 mimeType string 432 ) 433 ctr, err := ic.Libpod.LookupContainer(nameOrID) 434 if err != nil { 435 return nil, err 436 } 437 rtc, err := ic.Libpod.GetConfig() 438 if err != nil { 439 return nil, err 440 } 441 switch options.Format { 442 case "oci": 443 mimeType = buildah.OCIv1ImageManifest 444 if len(options.Message) > 0 { 445 return nil, errors.Errorf("messages are only compatible with the docker image format (-f docker)") 446 } 447 case "docker": 448 mimeType = manifest.DockerV2Schema2MediaType 449 default: 450 return nil, errors.Errorf("unrecognized image format %q", options.Format) 451 } 452 sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) 453 coptions := buildah.CommitOptions{ 454 SignaturePolicyPath: rtc.Engine.SignaturePolicyPath, 455 ReportWriter: options.Writer, 456 SystemContext: sc, 457 PreferredManifestType: mimeType, 458 } 459 opts := libpod.ContainerCommitOptions{ 460 CommitOptions: coptions, 461 Pause: options.Pause, 462 IncludeVolumes: options.IncludeVolumes, 463 Message: options.Message, 464 Changes: options.Changes, 465 Author: options.Author, 466 } 467 newImage, err := ctr.Commit(ctx, options.ImageName, opts) 468 if err != nil { 469 return nil, err 470 } 471 return &entities.CommitReport{Id: newImage.ID()}, nil 472 } 473 474 func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, options entities.ContainerExportOptions) error { 475 ctr, err := ic.Libpod.LookupContainer(nameOrID) 476 if err != nil { 477 return err 478 } 479 return ctr.Export(options.Output) 480 } 481 482 func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { 483 var ( 484 err error 485 cons []*libpod.Container 486 ) 487 checkOpts := libpod.ContainerCheckpointOptions{ 488 Keep: options.Keep, 489 TCPEstablished: options.TCPEstablished, 490 TargetFile: options.Export, 491 IgnoreRootfs: options.IgnoreRootFS, 492 KeepRunning: options.LeaveRunning, 493 } 494 495 if options.All { 496 running := func(c *libpod.Container) bool { 497 state, _ := c.State() 498 return state == define.ContainerStateRunning 499 } 500 cons, err = ic.Libpod.GetContainers(running) 501 } else { 502 cons, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) 503 } 504 if err != nil { 505 return nil, err 506 } 507 reports := make([]*entities.CheckpointReport, 0, len(cons)) 508 for _, con := range cons { 509 err = con.Checkpoint(ctx, checkOpts) 510 reports = append(reports, &entities.CheckpointReport{ 511 Err: err, 512 Id: con.ID(), 513 }) 514 } 515 return reports, nil 516 } 517 518 func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) { 519 var ( 520 cons []*libpod.Container 521 err error 522 ) 523 524 restoreOptions := libpod.ContainerCheckpointOptions{ 525 Keep: options.Keep, 526 TCPEstablished: options.TCPEstablished, 527 TargetFile: options.Import, 528 Name: options.Name, 529 IgnoreRootfs: options.IgnoreRootFS, 530 IgnoreStaticIP: options.IgnoreStaticIP, 531 IgnoreStaticMAC: options.IgnoreStaticMAC, 532 } 533 534 filterFuncs := []libpod.ContainerFilter{ 535 func(c *libpod.Container) bool { 536 state, _ := c.State() 537 return state == define.ContainerStateExited 538 }, 539 } 540 541 switch { 542 case options.Import != "": 543 cons, err = checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options.Import, options.Name) 544 case options.All: 545 cons, err = ic.Libpod.GetContainers(filterFuncs...) 546 default: 547 cons, err = getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod) 548 } 549 if err != nil { 550 return nil, err 551 } 552 reports := make([]*entities.RestoreReport, 0, len(cons)) 553 for _, con := range cons { 554 err := con.Restore(ctx, restoreOptions) 555 reports = append(reports, &entities.RestoreReport{ 556 Err: err, 557 Id: con.ID(), 558 }) 559 } 560 return reports, nil 561 } 562 563 func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) { 564 warn, err := generate.CompleteSpec(ctx, ic.Libpod, s) 565 if err != nil { 566 return nil, err 567 } 568 // Print warnings 569 for _, w := range warn { 570 fmt.Fprintf(os.Stderr, "%s\n", w) 571 } 572 ctr, err := generate.MakeContainer(ctx, ic.Libpod, s) 573 if err != nil { 574 return nil, err 575 } 576 return &entities.ContainerCreateReport{Id: ctr.ID()}, nil 577 } 578 579 func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error { 580 ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) 581 if err != nil { 582 return err 583 } 584 ctr := ctrs[0] 585 conState, err := ctr.State() 586 if err != nil { 587 return errors.Wrapf(err, "unable to determine state of %s", ctr.ID()) 588 } 589 if conState != define.ContainerStateRunning { 590 return errors.Errorf("you can only attach to running containers") 591 } 592 593 // If the container is in a pod, also set to recursively start dependencies 594 err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "") 595 if err != nil && errors.Cause(err) != define.ErrDetach { 596 return errors.Wrapf(err, "error attaching to container %s", ctr.ID()) 597 } 598 os.Stdout.WriteString("\n") 599 return nil 600 } 601 602 func makeExecConfig(options entities.ExecOptions) *libpod.ExecConfig { 603 execConfig := new(libpod.ExecConfig) 604 execConfig.Command = options.Cmd 605 execConfig.Terminal = options.Tty 606 execConfig.Privileged = options.Privileged 607 execConfig.Environment = options.Envs 608 execConfig.User = options.User 609 execConfig.WorkDir = options.WorkDir 610 execConfig.DetachKeys = &options.DetachKeys 611 execConfig.PreserveFDs = options.PreserveFDs 612 execConfig.AttachStdin = options.Interactive 613 614 return execConfig 615 } 616 617 func checkExecPreserveFDs(options entities.ExecOptions) error { 618 if options.PreserveFDs > 0 { 619 entries, err := ioutil.ReadDir("/proc/self/fd") 620 if err != nil { 621 return err 622 } 623 624 m := make(map[int]bool) 625 for _, e := range entries { 626 i, err := strconv.Atoi(e.Name()) 627 if err != nil { 628 return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) 629 } 630 m[i] = true 631 } 632 633 for i := 3; i < 3+int(options.PreserveFDs); i++ { 634 if _, found := m[i]; !found { 635 return errors.New("invalid --preserve-fds=N specified. Not enough FDs available") 636 } 637 } 638 } 639 return nil 640 } 641 642 func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { 643 ec := define.ExecErrorCodeGeneric 644 err := checkExecPreserveFDs(options) 645 if err != nil { 646 return ec, err 647 } 648 ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) 649 if err != nil { 650 return ec, err 651 } 652 ctr := ctrs[0] 653 654 execConfig := makeExecConfig(options) 655 656 ec, err = terminal.ExecAttachCtr(ctx, ctr, execConfig, &streams) 657 return define.TranslateExecErrorToExitCode(ec, err), err 658 } 659 660 func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { 661 err := checkExecPreserveFDs(options) 662 if err != nil { 663 return "", err 664 } 665 ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) 666 if err != nil { 667 return "", err 668 } 669 ctr := ctrs[0] 670 671 execConfig := makeExecConfig(options) 672 673 // Make an exit command 674 storageConfig := ic.Libpod.StorageConfig() 675 runtimeConfig, err := ic.Libpod.GetConfig() 676 if err != nil { 677 return "", errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command") 678 } 679 // TODO: Add some ability to toggle syslog 680 exitCommandArgs, err := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, false, true, true) 681 if err != nil { 682 return "", errors.Wrapf(err, "error constructing exit command for exec session") 683 } 684 execConfig.ExitCommand = exitCommandArgs 685 686 // Create and start the exec session 687 id, err := ctr.ExecCreate(execConfig) 688 if err != nil { 689 return "", err 690 } 691 692 // TODO: we should try and retrieve exit code if this fails. 693 if err := ctr.ExecStart(id); err != nil { 694 return "", err 695 } 696 return id, nil 697 } 698 699 func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { 700 reports := []*entities.ContainerStartReport{} 701 var exitCode = define.ExecErrorCodeGeneric 702 ctrs, rawInputs, err := getContainersAndInputByContext(false, options.Latest, namesOrIds, ic.Libpod) 703 if err != nil { 704 return nil, err 705 } 706 // There can only be one container if attach was used 707 for i := range ctrs { 708 ctr := ctrs[i] 709 rawInput := rawInputs[i] 710 ctrState, err := ctr.State() 711 if err != nil { 712 return nil, err 713 } 714 ctrRunning := ctrState == define.ContainerStateRunning 715 716 if options.Attach { 717 err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning, ctr.PodID() != "") 718 if errors.Cause(err) == define.ErrDetach { 719 // User manually detached 720 // Exit cleanly immediately 721 reports = append(reports, &entities.ContainerStartReport{ 722 Id: ctr.ID(), 723 RawInput: rawInput, 724 Err: nil, 725 ExitCode: 0, 726 }) 727 return reports, nil 728 } 729 730 if errors.Cause(err) == define.ErrWillDeadlock { 731 logrus.Debugf("Deadlock error: %v", err) 732 reports = append(reports, &entities.ContainerStartReport{ 733 Id: ctr.ID(), 734 RawInput: rawInput, 735 Err: err, 736 ExitCode: define.ExitCode(err), 737 }) 738 return reports, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID()) 739 } 740 741 if ctrRunning { 742 reports = append(reports, &entities.ContainerStartReport{ 743 Id: ctr.ID(), 744 RawInput: rawInput, 745 Err: nil, 746 ExitCode: 0, 747 }) 748 return reports, err 749 } 750 751 if err != nil { 752 reports = append(reports, &entities.ContainerStartReport{ 753 Id: ctr.ID(), 754 RawInput: rawInput, 755 Err: err, 756 ExitCode: exitCode, 757 }) 758 return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID()) 759 } 760 761 if ecode, err := ctr.Wait(); err != nil { 762 if errors.Cause(err) == define.ErrNoSuchCtr { 763 // Check events 764 event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) 765 if err != nil { 766 logrus.Errorf("Cannot get exit code: %v", err) 767 exitCode = define.ExecErrorCodeNotFound 768 } else { 769 exitCode = event.ContainerExitCode 770 } 771 } 772 } else { 773 exitCode = int(ecode) 774 } 775 reports = append(reports, &entities.ContainerStartReport{ 776 Id: ctr.ID(), 777 RawInput: rawInput, 778 Err: err, 779 ExitCode: exitCode, 780 }) 781 return reports, nil 782 } // end attach 783 784 // Start the container if it's not running already. 785 if !ctrRunning { 786 // Handle non-attach start 787 // If the container is in a pod, also set to recursively start dependencies 788 report := &entities.ContainerStartReport{ 789 Id: ctr.ID(), 790 RawInput: rawInput, 791 ExitCode: 125, 792 } 793 if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil { 794 // if lastError != nil { 795 // fmt.Fprintln(os.Stderr, lastError) 796 // } 797 report.Err = err 798 if errors.Cause(err) == define.ErrWillDeadlock { 799 report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks") 800 reports = append(reports, report) 801 continue 802 } 803 report.Err = errors.Wrapf(err, "unable to start container %q", ctr.ID()) 804 reports = append(reports, report) 805 continue 806 } 807 report.ExitCode = 0 808 reports = append(reports, report) 809 } 810 } 811 return reports, nil 812 } 813 814 func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { 815 if options.Latest { 816 options.Last = 1 817 } 818 return ps.GetContainerLists(ic.Libpod, options) 819 } 820 821 // ContainerDiff provides changes to given container 822 func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, opts entities.DiffOptions) (*entities.DiffReport, error) { 823 if opts.Latest { 824 ctnr, err := ic.Libpod.GetLatestContainer() 825 if err != nil { 826 return nil, errors.Wrap(err, "unable to get latest container") 827 } 828 nameOrID = ctnr.ID() 829 } 830 changes, err := ic.Libpod.GetDiff("", nameOrID) 831 return &entities.DiffReport{Changes: changes}, err 832 } 833 834 func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { 835 warn, err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec) 836 if err != nil { 837 return nil, err 838 } 839 // Print warnings 840 for _, w := range warn { 841 fmt.Fprintf(os.Stderr, "%s\n", w) 842 } 843 ctr, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec) 844 if err != nil { 845 return nil, err 846 } 847 848 if opts.CIDFile != "" { 849 if err := util.CreateCidFile(opts.CIDFile, ctr.ID()); err != nil { 850 return nil, err 851 } 852 } 853 854 var joinPod bool 855 if len(ctr.PodID()) > 0 { 856 joinPod = true 857 } 858 report := entities.ContainerRunReport{Id: ctr.ID()} 859 860 if logrus.GetLevel() == logrus.DebugLevel { 861 cgroupPath, err := ctr.CGroupPath() 862 if err == nil { 863 logrus.Debugf("container %q has CgroupParent %q", ctr.ID(), cgroupPath) 864 } 865 } 866 if opts.Detach { 867 // if the container was created as part of a pod, also start its dependencies, if any. 868 if err := ctr.Start(ctx, joinPod); err != nil { 869 // This means the command did not exist 870 report.ExitCode = define.ExitCode(err) 871 return &report, err 872 } 873 874 return &report, nil 875 } 876 877 // if the container was created as part of a pod, also start its dependencies, if any. 878 if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true, joinPod); err != nil { 879 // We've manually detached from the container 880 // Do not perform cleanup, or wait for container exit code 881 // Just exit immediately 882 if errors.Cause(err) == define.ErrDetach { 883 report.ExitCode = 0 884 return &report, nil 885 } 886 if opts.Rm { 887 if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false); deleteError != nil { 888 logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) 889 } 890 } 891 if errors.Cause(err) == define.ErrWillDeadlock { 892 logrus.Debugf("Deadlock error on %q: %v", ctr.ID(), err) 893 report.ExitCode = define.ExitCode(err) 894 return &report, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID()) 895 } 896 report.ExitCode = define.ExitCode(err) 897 return &report, err 898 } 899 900 if ecode, err := ctr.Wait(); err != nil { 901 if errors.Cause(err) == define.ErrNoSuchCtr { 902 // Check events 903 event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) 904 if err != nil { 905 logrus.Errorf("Cannot get exit code: %v", err) 906 report.ExitCode = define.ExecErrorCodeNotFound 907 } else { 908 report.ExitCode = event.ContainerExitCode 909 } 910 } 911 } else { 912 report.ExitCode = int(ecode) 913 } 914 if opts.Rm && !ctr.ShouldRestart(ctx) { 915 if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { 916 if errors.Cause(err) == define.ErrNoSuchCtr || 917 errors.Cause(err) == define.ErrCtrRemoved { 918 logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err) 919 } else { 920 logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) 921 } 922 } 923 } 924 return &report, nil 925 } 926 927 func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { 928 if options.Writer == nil { 929 return errors.New("no io.Writer set for container logs") 930 } 931 932 var wg sync.WaitGroup 933 934 ctrs, err := getContainersByContext(false, options.Latest, containers, ic.Libpod) 935 if err != nil { 936 return err 937 } 938 939 logOpts := &logs.LogOptions{ 940 Multi: len(ctrs) > 1, 941 Details: options.Details, 942 Follow: options.Follow, 943 Since: options.Since, 944 Tail: options.Tail, 945 Timestamps: options.Timestamps, 946 UseName: options.Names, 947 WaitGroup: &wg, 948 } 949 950 chSize := len(ctrs) * int(options.Tail) 951 if chSize <= 0 { 952 chSize = 1 953 } 954 logChannel := make(chan *logs.LogLine, chSize) 955 956 if err := ic.Libpod.Log(ctx, ctrs, logOpts, logChannel); err != nil { 957 return err 958 } 959 960 go func() { 961 wg.Wait() 962 close(logChannel) 963 }() 964 965 for line := range logChannel { 966 fmt.Fprintln(options.Writer, line.String(logOpts)) 967 } 968 969 return nil 970 } 971 972 func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { 973 reports := []*entities.ContainerCleanupReport{} 974 ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 975 if err != nil { 976 return nil, err 977 } 978 for _, ctr := range ctrs { 979 var err error 980 report := entities.ContainerCleanupReport{Id: ctr.ID()} 981 982 if options.Exec != "" { 983 if options.Remove { 984 if err := ctr.ExecRemove(options.Exec, false); err != nil { 985 return nil, err 986 } 987 } else { 988 if err := ctr.ExecCleanup(options.Exec); err != nil { 989 return nil, err 990 } 991 } 992 return []*entities.ContainerCleanupReport{}, nil 993 } 994 995 if options.Remove && !ctr.ShouldRestart(ctx) { 996 err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) 997 if err != nil { 998 report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) 999 } 1000 } else { 1001 err := ctr.Cleanup(ctx) 1002 if err != nil { 1003 report.CleanErr = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID()) 1004 } 1005 } 1006 1007 if options.RemoveImage { 1008 _, imageName := ctr.Image() 1009 ctrImage, err := ic.Libpod.ImageRuntime().NewFromLocal(imageName) 1010 if err != nil { 1011 report.RmiErr = err 1012 reports = append(reports, &report) 1013 continue 1014 } 1015 _, err = ic.Libpod.RemoveImage(ctx, ctrImage, false) 1016 report.RmiErr = err 1017 } 1018 1019 reports = append(reports, &report) 1020 } 1021 return reports, nil 1022 } 1023 1024 func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { 1025 ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) 1026 if err != nil { 1027 return nil, err 1028 } 1029 reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) 1030 for _, ctr := range ctrs { 1031 report := entities.ContainerInitReport{Id: ctr.ID()} 1032 err := ctr.Init(ctx, ctr.PodID() != "") 1033 1034 // If we're initializing all containers, ignore invalid state errors 1035 if options.All && errors.Cause(err) == define.ErrCtrStateInvalid { 1036 err = nil 1037 } 1038 report.Err = err 1039 reports = append(reports, &report) 1040 } 1041 return reports, nil 1042 } 1043 1044 func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { 1045 if os.Geteuid() != 0 { 1046 if driver := ic.Libpod.StorageConfig().GraphDriverName; driver != "vfs" { 1047 // Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part 1048 // of the mount command. 1049 return nil, fmt.Errorf("cannot mount using driver %s in rootless mode", driver) 1050 } 1051 1052 became, ret, err := rootless.BecomeRootInUserNS("") 1053 if err != nil { 1054 return nil, err 1055 } 1056 if became { 1057 os.Exit(ret) 1058 } 1059 } 1060 reports := []*entities.ContainerMountReport{} 1061 // Attempt to mount named containers directly from storage, 1062 // this will fail and code will fall through to removing the container from libpod.` 1063 names := []string{} 1064 for _, ctr := range nameOrIDs { 1065 report := entities.ContainerMountReport{Id: ctr} 1066 if report.Path, report.Err = ic.Libpod.MountStorageContainer(ctr); report.Err != nil { 1067 names = append(names, ctr) 1068 } else { 1069 reports = append(reports, &report) 1070 } 1071 } 1072 1073 ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) 1074 if err != nil { 1075 return nil, err 1076 } 1077 for _, ctr := range ctrs { 1078 report := entities.ContainerMountReport{Id: ctr.ID()} 1079 report.Path, report.Err = ctr.Mount() 1080 reports = append(reports, &report) 1081 } 1082 if len(reports) > 0 { 1083 return reports, nil 1084 } 1085 1086 storageCtrs, err := ic.Libpod.StorageContainers() 1087 if err != nil { 1088 return nil, err 1089 } 1090 1091 for _, sctr := range storageCtrs { 1092 mounted, path, err := ic.Libpod.IsStorageContainerMounted(sctr.ID) 1093 if err != nil { 1094 return nil, err 1095 } 1096 1097 var name string 1098 if len(sctr.Names) > 0 { 1099 name = sctr.Names[0] 1100 } 1101 if mounted { 1102 reports = append(reports, &entities.ContainerMountReport{ 1103 Id: sctr.ID, 1104 Name: name, 1105 Path: path, 1106 }) 1107 } 1108 } 1109 1110 // No containers were passed, so we send back what is mounted 1111 ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod) 1112 if err != nil { 1113 return nil, err 1114 } 1115 for _, ctr := range ctrs { 1116 mounted, path, err := ctr.Mounted() 1117 if err != nil { 1118 return nil, err 1119 } 1120 1121 if mounted { 1122 reports = append(reports, &entities.ContainerMountReport{ 1123 Id: ctr.ID(), 1124 Name: ctr.Name(), 1125 Path: path, 1126 }) 1127 } 1128 } 1129 1130 return reports, nil 1131 } 1132 1133 func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { 1134 reports := []*entities.ContainerUnmountReport{} 1135 names := []string{} 1136 if options.All { 1137 storageCtrs, err := ic.Libpod.StorageContainers() 1138 if err != nil { 1139 return nil, err 1140 } 1141 for _, sctr := range storageCtrs { 1142 mounted, _, _ := ic.Libpod.IsStorageContainerMounted(sctr.ID) 1143 if mounted { 1144 report := entities.ContainerUnmountReport{Id: sctr.ID} 1145 if _, report.Err = ic.Libpod.UnmountStorageContainer(sctr.ID, options.Force); report.Err != nil { 1146 if errors.Cause(report.Err) != define.ErrCtrExists { 1147 reports = append(reports, &report) 1148 } 1149 } else { 1150 reports = append(reports, &report) 1151 } 1152 } 1153 } 1154 } 1155 for _, ctr := range nameOrIDs { 1156 report := entities.ContainerUnmountReport{Id: ctr} 1157 if _, report.Err = ic.Libpod.UnmountStorageContainer(ctr, options.Force); report.Err != nil { 1158 names = append(names, ctr) 1159 } else { 1160 reports = append(reports, &report) 1161 } 1162 } 1163 ctrs, err := getContainersByContext(options.All, options.Latest, names, ic.Libpod) 1164 if err != nil { 1165 return nil, err 1166 } 1167 for _, ctr := range ctrs { 1168 state, err := ctr.State() 1169 if err != nil { 1170 logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error()) 1171 continue 1172 } 1173 if state == define.ContainerStateRunning { 1174 logrus.Debugf("Error umounting container %s, is running", ctr.ID()) 1175 continue 1176 } 1177 1178 report := entities.ContainerUnmountReport{Id: ctr.ID()} 1179 if err := ctr.Unmount(options.Force); err != nil { 1180 if options.All && errors.Cause(err) == storage.ErrLayerNotMounted { 1181 logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID()) 1182 continue 1183 } 1184 report.Err = errors.Wrapf(err, "error unmounting container %s", ctr.ID()) 1185 } 1186 reports = append(reports, &report) 1187 } 1188 return reports, nil 1189 } 1190 1191 // GetConfig returns a copy of the configuration used by the runtime 1192 func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { 1193 return ic.Libpod.GetConfig() 1194 } 1195 1196 func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { 1197 ctrs, err := getContainersByContext(options.All, options.Latest, []string{nameOrID}, ic.Libpod) 1198 if err != nil { 1199 return nil, err 1200 } 1201 reports := []*entities.ContainerPortReport{} 1202 for _, con := range ctrs { 1203 state, err := con.State() 1204 if err != nil { 1205 return nil, err 1206 } 1207 if state != define.ContainerStateRunning { 1208 continue 1209 } 1210 portmappings, err := con.PortMappings() 1211 if err != nil { 1212 return nil, err 1213 } 1214 if len(portmappings) > 0 { 1215 reports = append(reports, &entities.ContainerPortReport{ 1216 Id: con.ID(), 1217 Ports: portmappings, 1218 }) 1219 } 1220 } 1221 return reports, nil 1222 } 1223 1224 // Shutdown Libpod engine 1225 func (ic *ContainerEngine) Shutdown(_ context.Context) { 1226 shutdownSync.Do(func() { 1227 _ = ic.Libpod.Shutdown(false) 1228 }) 1229 } 1230 1231 func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) (statsChan chan entities.ContainerStatsReport, err error) { 1232 statsChan = make(chan entities.ContainerStatsReport, 1) 1233 1234 containerFunc := ic.Libpod.GetRunningContainers 1235 queryAll := false 1236 switch { 1237 case options.Latest: 1238 containerFunc = func() ([]*libpod.Container, error) { 1239 lastCtr, err := ic.Libpod.GetLatestContainer() 1240 if err != nil { 1241 return nil, err 1242 } 1243 return []*libpod.Container{lastCtr}, nil 1244 } 1245 case len(namesOrIds) > 0: 1246 containerFunc = func() ([]*libpod.Container, error) { return ic.Libpod.GetContainersByList(namesOrIds) } 1247 default: 1248 // No containers, no latest -> query all! 1249 queryAll = true 1250 containerFunc = ic.Libpod.GetAllContainers 1251 } 1252 1253 go func() { 1254 defer close(statsChan) 1255 var ( 1256 err error 1257 containers []*libpod.Container 1258 containerStats map[string]*define.ContainerStats 1259 ) 1260 containerStats = make(map[string]*define.ContainerStats) 1261 1262 stream: // label to flatten the scope 1263 select { 1264 case <-ctx.Done(): 1265 // client cancelled 1266 logrus.Debugf("Container stats stopped: context cancelled") 1267 return 1268 default: 1269 // just fall through and do work 1270 } 1271 1272 // Anonymous func to easily use the return values for streaming. 1273 computeStats := func() ([]define.ContainerStats, error) { 1274 containers, err = containerFunc() 1275 if err != nil { 1276 return nil, errors.Wrapf(err, "unable to get list of containers") 1277 } 1278 1279 reportStats := []define.ContainerStats{} 1280 for _, ctr := range containers { 1281 prev, ok := containerStats[ctr.ID()] 1282 if !ok { 1283 prev = &define.ContainerStats{} 1284 } 1285 1286 stats, err := ctr.GetContainerStats(prev) 1287 if err != nil { 1288 cause := errors.Cause(err) 1289 if queryAll && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) { 1290 continue 1291 } 1292 if cause == cgroups.ErrCgroupV1Rootless { 1293 err = cause 1294 } 1295 return nil, err 1296 } 1297 1298 containerStats[ctr.ID()] = stats 1299 reportStats = append(reportStats, *stats) 1300 } 1301 return reportStats, nil 1302 } 1303 1304 report := entities.ContainerStatsReport{} 1305 report.Stats, report.Error = computeStats() 1306 statsChan <- report 1307 1308 if report.Error != nil || !options.Stream { 1309 return 1310 } 1311 1312 time.Sleep(time.Second) 1313 goto stream 1314 }() 1315 1316 return statsChan, nil 1317 } 1318 1319 // ShouldRestart returns whether the container should be restarted 1320 func (ic *ContainerEngine) ShouldRestart(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { 1321 ctr, err := ic.Libpod.LookupContainer(nameOrID) 1322 if err != nil { 1323 return nil, err 1324 } 1325 1326 return &entities.BoolReport{Value: ctr.ShouldRestart(ctx)}, nil 1327 }