From ac7fa8437daa99c951c2cb76e81f7513f1c6f3e7 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Sun, 9 Oct 2022 08:33:47 +0100 Subject: [PATCH] libpod: Add support for 'podman top' on FreeBSD This simply runs ps(1) on the host and filters for processes inside the container. [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/container_top_freebsd.go | 131 ++++++++++++++++++++++++++++ libpod/container_top_unsupported.go | 4 +- 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 libpod/container_top_freebsd.go diff --git a/libpod/container_top_freebsd.go b/libpod/container_top_freebsd.go new file mode 100644 index 0000000000..1a59a5d68f --- /dev/null +++ b/libpod/container_top_freebsd.go @@ -0,0 +1,131 @@ +//go:build freebsd +// +build freebsd + +package libpod + +import ( + "bufio" + "errors" + "fmt" + "os/exec" + "strings" + "sync" + + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/util" + "github.com/google/shlex" + "github.com/sirupsen/logrus" +) + +var isDescriptor = map[string]bool{} + +func init() { + allDescriptors, err := util.GetContainerPidInformationDescriptors() + if err != nil { + // Should never happen + logrus.Debugf("failed call to util.GetContainerPidInformationDescriptors()") + return + } + for _, d := range allDescriptors { + isDescriptor[d] = true + } +} + +// Top gathers statistics about the running processes in a container. It returns a +// []string for output +func (c *Container) Top(descriptors []string) ([]string, error) { + conStat, err := c.State() + if err != nil { + return nil, fmt.Errorf("unable to look up state for %s: %w", c.ID(), err) + } + if conStat != define.ContainerStateRunning { + return nil, errors.New("top can only be used on running containers") + } + + // Default to 'ps -ef' compatible descriptors + if len(descriptors) == 0 { + descriptors = []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "args"} + } + + // If everything in descriptors is a supported AIX format + // descriptor, we use 'ps -ao ', otherwise we pass + // everything straight through to ps. + supportedDescriptors := true + for _, d := range descriptors { + if _, ok := isDescriptor[d]; !ok { + supportedDescriptors = false + break + } + } + if supportedDescriptors { + descriptors = []string{"-ao", strings.Join(descriptors, ",")} + } + + // Note that the descriptors to ps(1) must be shlexed (see #12452). + psDescriptors := []string{} + for _, d := range descriptors { + shSplit, err := shlex.Split(d) + if err != nil { + return nil, fmt.Errorf("parsing ps args: %w", err) + } + for _, s := range shSplit { + if s != "" { + psDescriptors = append(psDescriptors, s) + } + } + } + + args := []string{ + "-J", + c.jailName(), + } + args = append(args, psDescriptors...) + + output, err := c.execPS(args) + if err != nil { + return nil, fmt.Errorf("executing ps(1): %w", err) + } + + return output, nil +} + +func (c *Container) execPS(args []string) ([]string, error) { + cmd := exec.Command("ps", args...) + stdoutPipe, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } + stderrPipe, err := cmd.StderrPipe() + if err != nil { + return nil, err + } + + var wg sync.WaitGroup + wg.Add(2) + stdout := []string{} + go func() { + scanner := bufio.NewScanner(stdoutPipe) + for scanner.Scan() { + stdout = append(stdout, scanner.Text()) + } + wg.Done() + }() + stderr := []string{} + go func() { + scanner := bufio.NewScanner(stderrPipe) + for scanner.Scan() { + stderr = append(stderr, scanner.Text()) + } + wg.Done() + }() + + if err := cmd.Start(); err != nil { + return nil, err + } + wg.Wait() + if err := cmd.Wait(); err != nil { + return nil, fmt.Errorf("ps(1) command failed: %w, output: %s", err, strings.Join(stderr, " ")) + } + + return stdout, nil +} diff --git a/libpod/container_top_unsupported.go b/libpod/container_top_unsupported.go index a8d9b970b7..444f4e9c62 100644 --- a/libpod/container_top_unsupported.go +++ b/libpod/container_top_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod