diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go index 0f3e92a542..41002eb527 100644 --- a/libpod/container_top_linux.go +++ b/libpod/container_top_linux.go @@ -214,10 +214,11 @@ func (c *Container) Top(descriptors []string) ([]string, error) { return nil, psgoErr } - // Note that the descriptors to ps(1) must be shlexed (see #12452). - psDescriptors := []string{} - for _, d := range descriptors { - shSplit, err := shlex.Split(d) + psDescriptors := descriptors + if len(descriptors) == 1 { + // Note that the descriptors to ps(1) must be shlexed (see #12452). + psDescriptors = make([]string, 0, len(descriptors)) + shSplit, err := shlex.Split(descriptors[0]) if err != nil { return nil, fmt.Errorf("parsing ps args: %w", err) } diff --git a/pkg/api/handlers/compat/containers_top.go b/pkg/api/handlers/compat/containers_top.go index b6edd51b41..8298c46c79 100644 --- a/pkg/api/handlers/compat/containers_top.go +++ b/pkg/api/handlers/compat/containers_top.go @@ -18,14 +18,14 @@ func TopContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := utils.GetDecoder(r) - psArgs := "-ef" - if utils.IsLibpodRequest(r) { - psArgs = "" + var psArgs []string + if !utils.IsLibpodRequest(r) { + psArgs = []string{"-ef"} } query := struct { - Delay int `schema:"delay"` - PsArgs string `schema:"ps_args"` - Stream bool `schema:"stream"` + Delay int `schema:"delay"` + PsArgs []string `schema:"ps_args"` + Stream bool `schema:"stream"` }{ Delay: 5, PsArgs: psArgs, @@ -52,13 +52,22 @@ func TopContainer(w http.ResponseWriter, r *http.Request) { encoder := json.NewEncoder(w) + args := query.PsArgs + if len(args) == 1 && + utils.IsLibpodRequest(r) { + if _, err := utils.SupportedVersion(r, "< 4.8.0"); err == nil { + // Ugly workaround for older clients which used to send arguments comma separated. + args = strings.Split(args[0], ",") + } + } + loop: // break out of for/select infinite` loop for { select { case <-r.Context().Done(): break loop default: - output, err := c.Top(strings.Split(query.PsArgs, ",")) + output, err := c.Top(args) if err != nil { if !statusWritten { utils.InternalServerError(w, err) diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 4cc33682e7..88f9b6cb76 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1181,8 +1181,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // default: 5 // - in: query // name: ps_args - // type: string - // default: + // type: array + // items: + // type: string // description: | // arguments to pass to ps such as aux. // produces: diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index e776a60920..0ca579a6c2 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -290,8 +290,10 @@ func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, e } params := url.Values{} if options.Changed("Descriptors") { - psArgs := strings.Join(options.GetDescriptors(), ",") - params.Add("ps_args", psArgs) + psArgs := options.GetDescriptors() + for _, arg := range psArgs { + params.Add("ps_args", arg) + } } response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/top", params, nil, nameOrID) if err != nil { diff --git a/test/e2e/top_test.go b/test/e2e/top_test.go index f876ded44b..cf8c095a29 100644 --- a/test/e2e/top_test.go +++ b/test/e2e/top_test.go @@ -101,6 +101,10 @@ var _ = Describe("Podman top", func() { result = podmanTest.Podman([]string{"top", session.OutputToString(), "ax -o args"}) result.WaitWithDefaultTimeout() Expect(result).Should(ExitCleanly()) + + result = podmanTest.Podman([]string{"top", session.OutputToString(), "ax", "-o", "args"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(ExitCleanly()) Expect(result.OutputToStringArray()).To(Equal([]string{"COMMAND", "sleep inf"})) // Now make sure we use ps in the container with CAP_SYS_PTRACE diff --git a/test/system/085-top.bats b/test/system/085-top.bats new file mode 100644 index 0000000000..09b61c453e --- /dev/null +++ b/test/system/085-top.bats @@ -0,0 +1,25 @@ +#!/usr/bin/env bats + +load helpers + +@test "podman top - basic tests" { + run_podman run -d $IMAGE top + cid=$output + is "$cid" "[0-9a-f]\{64\}$" + + run_podman top $cid + is "${lines[0]}" "USER[ \t]*PID[ \t]*PPID[ \t]*%CPU[ \t]*ELAPSED[ \t]*TTY[ \t]*TIME[ \t]*COMMAND" "podman top" + is "${lines[1]}" "root[ \t]*1[ \t]*0[ \t]*0.000[ \t]*" "podman top" + + run_podman top $cid -eo pid,comm + is "${lines[0]}" "[ \t]*PID[ \t]*COMMAND" "podman top -eo pid,comm Heading" + is "${lines[1]}" "[ \t]*1[ \t]*top" "podman top -eo pid,comm processes" + + run_podman top $cid -eo "pid comm" + is "${lines[0]}" "[ \t]*PID[ \t]*COMMAND" "podman top -eo "pid comm" Heading" + is "${lines[1]}" "[ \t]*1[ \t]*top" "podman top -eo "pid comm" processes" + + run_podman rm -f $cid +} + +# vim: filetype=sh