diff options
author | Valentin Rothberg <vrothberg@suse.com> | 2018-07-19 14:41:58 +0200 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-07-19 20:47:52 +0000 |
commit | ba1871dac033783ab0329c9b3c9113a34a90992f (patch) | |
tree | f0be2944ff09d857306ea42864c7b24ae86ae9bd /libpod/container_top.go | |
parent | 98703eb204923f06555605c648fc165a55214520 (diff) | |
download | podman-ba1871dac033783ab0329c9b3c9113a34a90992f.tar.gz podman-ba1871dac033783ab0329c9b3c9113a34a90992f.tar.bz2 podman-ba1871dac033783ab0329c9b3c9113a34a90992f.zip |
podman-top: use containers/psgo
Use github.com/containers/psgo instead of execing `ps (1)`. The psgo
library enables a much more flexible interface with respect to which
data to be printed (e.g., capabilities, seccomp mode, PID, PCPU, etc.)
while the output can be parsed reliably. The library does not use
ps (1) but parses /proc and /dev instead. To list the processes of a
given container, psgo will join the mount namespace of the given
container and extract all data from there.
Notice that this commit breaks compatibility with docker-top.
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>
Closes: #1113
Approved by: rhatdan
Diffstat (limited to 'libpod/container_top.go')
-rw-r--r-- | libpod/container_top.go | 135 |
1 files changed, 0 insertions, 135 deletions
diff --git a/libpod/container_top.go b/libpod/container_top.go deleted file mode 100644 index f5d314d49..000000000 --- a/libpod/container_top.go +++ /dev/null @@ -1,135 +0,0 @@ -package libpod - -import ( - "io/ioutil" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "github.com/projectatomic/libpod/pkg/util" - "github.com/projectatomic/libpod/utils" - "github.com/sirupsen/logrus" -) - -// GetContainerPids reads sysfs to obtain the pids associated with the container's cgroup -// and uses locking -func (c *Container) GetContainerPids() ([]string, error) { - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - if err := c.syncContainer(); err != nil { - return []string{}, errors.Wrapf(err, "error updating container %s state", c.ID()) - } - } - return c.getContainerPids() -} - -// Gets the pids for a container without locking. should only be called from a func where -// locking has already been established. -func (c *Container) getContainerPids() ([]string, error) { - cgroupPath, err := c.CGroupPath() - if err != nil { - return nil, err - } - - taskFile := filepath.Join("/sys/fs/cgroup/pids", cgroupPath, "tasks") - - logrus.Debug("reading pids from ", taskFile) - - content, err := ioutil.ReadFile(taskFile) - if err != nil { - return []string{}, errors.Wrapf(err, "unable to read pids from %s", taskFile) - } - return strings.Fields(string(content)), nil -} - -// GetContainerPidInformation calls ps with the appropriate options and returns -// the results as a string and the container's PIDs as a []string. Note that -// the ps output columns of each string are separated by a '\t\'. Currently, -// the args argument is overwriten with []string{"-ef"} until there is a -// portable library for ps-1 or to parse the procFS to extract all data. -func (c *Container) GetContainerPidInformation(args []string) ([]string, error) { - // XXX(ps-issue): args is overwriten with []{"-ef"} as the ps-1 tool - // doesn't support a generic way of splitting columns, rendering its - // output hard to parse. Docker first deterimes the number of columns - // and merges all exceeding ones into the last one. We believe that - // writing a go library which parses procFS in a ps-compatible way may - // be more beneficial in the long run. Until then, args will be - // ignored. - args = []string{"-ef"} - - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - if err := c.syncContainer(); err != nil { - return []string{}, errors.Wrapf(err, "error updating container %s state", c.ID()) - } - } - pids, err := c.getContainerPids() - if err != nil { - return []string{}, errors.Wrapf(err, "unable to obtain pids for ", c.ID()) - } - args = append(args, "-p", strings.Join(pids, ",")) - logrus.Debug("Executing: ", strings.Join(args, " ")) - results, err := utils.ExecCmd("ps", args...) - if err != nil { - return []string{}, errors.Wrapf(err, "unable to obtain information about pids") - } - - filteredOutput, err := filterPids(results, pids) - if err != nil { - return []string{}, err - } - return filteredOutput, nil -} - -func filterPids(psOutput string, pids []string) ([]string, error) { - var output []string - results := strings.Split(psOutput, "\n") - // The headers are in the first line of the results - headers := fieldsASCII(results[0]) - // We need to make sure PID in headers, so that we can filter - // Pids that don't belong. - - // append the headers back in - output = append(output, strings.Join(headers, "\t")) - - pidIndex := -1 - for i, header := range headers { - if header == "PID" { - pidIndex = i - } - } - if pidIndex == -1 { - return []string{}, errors.Errorf("unable to find PID field in ps output. try a different set of ps arguments") - } - for _, l := range results[1:] { - if l == "" { - continue - } - cols := fieldsASCII(l) - pid := cols[pidIndex] - if util.StringInSlice(pid, pids) { - // XXX(ps-issue): Strip cols down to the header's size - // and merge exceeding fields. This is required to - // "merge" the overhanging CMD entries which can - // contain white spaces. - out := cols[:len(headers)-1] - out = append(out, strings.Join(cols[len(headers)-1:], " ")) - output = append(output, strings.Join(out, "\t")) - } - } - return output, nil -} - -// Detects ascii whitespaces -func fieldsASCII(s string) []string { - fn := func(r rune) bool { - switch r { - case '\t', '\n', '\f', '\r', ' ': - return true - } - return false - } - return strings.FieldsFunc(s, fn) -} |