Skip to content

Commit

Permalink
libpod: Add support for 'podman top' on FreeBSD
Browse files Browse the repository at this point in the history
This simply runs ps(1) on the host and filters for processes inside the
container.

[NO NEW TESTS NEEDED]

Signed-off-by: Doug Rabson <[email protected]>
  • Loading branch information
dfr authored and mheon committed Oct 18, 2022
1 parent f8a7940 commit ac7fa84
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 2 deletions.
131 changes: 131 additions & 0 deletions libpod/container_top_freebsd.go
Original file line number Diff line number Diff line change
@@ -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 <descriptors>', 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
}
4 changes: 2 additions & 2 deletions libpod/container_top_unsupported.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build !linux
// +build !linux
//go:build !linux && !freebsd
// +build !linux,!freebsd

package libpod

Expand Down

0 comments on commit ac7fa84

Please sign in to comment.