//go:build !remote package compat import ( "encoding/json" "fmt" "net/http" "strings" "time" "github.com/containers/podman/v5/libpod" "github.com/containers/podman/v5/pkg/api/handlers" "github.com/containers/podman/v5/pkg/api/handlers/utils" api "github.com/containers/podman/v5/pkg/api/types" "github.com/sirupsen/logrus" ) func TopContainer(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) decoder := utils.GetDecoder(r) 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: 5, PsArgs: psArgs, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) return } if query.Delay < 1 { utils.Error(w, http.StatusBadRequest, fmt.Errorf("\"delay\" parameter of value %d < 1", query.Delay)) return } name := utils.GetName(r) c, err := runtime.LookupContainer(name) if err != nil { utils.ContainerNotFound(w, name, err) return } statusWritten := false w.Header().Set("Content-Type", "application/json") 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(args) if err != nil { if !statusWritten { utils.InternalServerError(w, err) } else { logrus.Errorf("From %s %q : %v", r.Method, r.URL, err) } break loop } if len(output) > 0 { body := handlers.ContainerTopOKBody{} body.Titles = utils.PSTitles(output[0]) for i := range body.Titles { body.Titles[i] = strings.TrimSpace(body.Titles[i]) } for _, line := range output[1:] { process := strings.FieldsFunc(line, func(r rune) bool { return r == ' ' || r == '\t' }) if len(process) > len(body.Titles) { // Docker assumes the last entry is *always* command // Which can include spaces. // All other descriptors are assumed to NOT include extra spaces. // So combine any extras. cmd := strings.Join(process[len(body.Titles)-1:], " ") var finalProc []string finalProc = append(finalProc, process[:len(body.Titles)-1]...) finalProc = append(finalProc, cmd) body.Processes = append(body.Processes, finalProc) } else { body.Processes = append(body.Processes, process) } } if err := encoder.Encode(body); err != nil { if !statusWritten { utils.InternalServerError(w, err) } else { logrus.Errorf("From %s %q : %v", r.Method, r.URL, err) } break loop } // after the first write we can no longer send a different status code statusWritten = true if f, ok := w.(http.Flusher); ok { f.Flush() } } if query.Stream { time.Sleep(time.Duration(query.Delay) * time.Second) } else { break loop } } } }