From e1089e89d7b8f5204ccb226934e46df736af4925 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 6 Oct 2021 09:25:24 -0400 Subject: [PATCH 1/2] Allow `podman stop` to be run on Stopping containers This allows you to stop a container after a `podman stop` process started, but did not finish, stopping the container (probably an ignored stop signal, with no time to SIGKILL?). This is a very narrow case, but once you're in it the only way to recover is a `podman rm -f` of the container or extensive manual remediation (you'd have to kill the container yourself, manually, and then force a `podman ps --all --sync` to update its status from the OCI runtime). [NO NEW TESTS NEEDED] I have no idea how to verify this one - we need to test that it actually started *during* the other stop command, and that's nontrivial. Signed-off-by: Matthew Heon --- libpod/container_api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libpod/container_api.go b/libpod/container_api.go index 50be0eea41..ecb307a5d7 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -184,7 +184,7 @@ func (c *Container) StopWithTimeout(timeout uint) error { return define.ErrCtrStopped } - if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { + if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopping) { return errors.Wrapf(define.ErrCtrStateInvalid, "can only stop created or running containers. %s is in state %s", c.ID(), c.state.State.String()) } From 8bd9f58d1d0db76406dca53907095648f4091f9f Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 6 Oct 2021 11:17:43 -0400 Subject: [PATCH 2/2] Ensure `podman ps --sync` functions The backend for `ps --sync` has been nonfunctional for a long while now - probably since v2.0. It's questionable how useful the flag is in modern Podman (the original case it was intended to catch, Conmon gone via SIGKILL, should be handled now via pinging the process with a signal to ensure it's still alive) but having the ability to force a refresh of container state from the OCI runtime is still useful. Signed-off-by: Matthew Heon --- libpod/container_api.go | 2 +- libpod/oci_conmon_linux.go | 6 ++++++ pkg/ps/ps.go | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libpod/container_api.go b/libpod/container_api.go index ecb307a5d7..38223316e3 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -690,7 +690,7 @@ func (c *Container) Sync() error { // If runtime knows about the container, update its status in runtime // And then save back to disk - if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped) { + if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped, define.ContainerStateStopping) { oldState := c.state.State if err := c.ociRuntime.UpdateContainerStatus(c); err != nil { return err diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 71a7b29fa0..fa31c74612 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -351,6 +351,12 @@ func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container) error { return ctr.handleExitFile(exitFile, fi) } + // Handle ContainerStateStopping - keep it unless the container + // transitioned to no longer running. + if oldState == define.ContainerStateStopping && (ctr.state.State == define.ContainerStatePaused || ctr.state.State == define.ContainerStateRunning) { + ctr.state.State = define.ContainerStateStopping + } + return nil } diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 69ac9c215b..90ad23f491 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -136,6 +136,12 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities ) batchErr := ctr.Batch(func(c *libpod.Container) error { + if opts.Sync { + if err := c.Sync(); err != nil { + return errors.Wrapf(err, "unable to update container state from OCI runtime") + } + } + conConfig = c.Config() conState, err = c.State() if err != nil {