From f11020702dbcb458164dbf81e0e41c55fefdd97b Mon Sep 17 00:00:00 2001 From: haircommander Date: Wed, 22 Aug 2018 10:13:13 -0400 Subject: Vendor changes to psgo Signed-off-by: haircommander Closes: #1298 Approved by: mheon --- vendor/github.com/containers/psgo/README.md | 19 ++++++++- vendor/github.com/containers/psgo/psgo.go | 64 +++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 10 deletions(-) (limited to 'vendor') diff --git a/vendor/github.com/containers/psgo/README.md b/vendor/github.com/containers/psgo/README.md index 6b0f1dab3..55cb73a71 100644 --- a/vendor/github.com/containers/psgo/README.md +++ b/vendor/github.com/containers/psgo/README.md @@ -10,9 +10,15 @@ This library aims to make things a bit more comfortable, especially for containe - `psgo.ProcessInfo(descriptors []string) ([][]string, error)` - ProcessInfo returns the process information of all processes in the current mount namespace. The input descriptors must be a slice of supported AIX format descriptors in the normal form or in the code form, if supported. If the input descriptor slice is empty, the `psgo.DefaultDescriptors` are used. The return value contains the string slice of process data, one per process. + - `psgo.ProcessInfoByPids(pids []string, descriptors []string) ([][]string, error)` + - ProcessInfoByPids is similar to `psgo.ProcessInfo`, but limits the return value to a list of specified pids. The pids input must be a slice of PIDs for which process information should be returned. If the input descriptor slice is empty, only the format descriptor headers are returned. + - `psgo.JoinNamespaceAndProcessInfo(pid string, descriptors []string) ([][]string, error)` - JoinNamespaceAndProcessInfo has the same semantics as ProcessInfo but joins the mount namespace of the specified pid before extracting data from /proc. This way, we can extract the `/proc` data from a container without executing any command inside the container. + - `psgo.JoinNamespaceAndProcessInfoByPids(pids []string, descriptors []string) ([][]string, error)` + - JoinNamespaceAndProcessInfoByPids is similar to `psgo.JoinNamespaceAndProcessInfo` but takes a slice of pids as an argument. To avoid duplicate entries (e.g., when two or more containers share the same PID namespace), a given PID namespace will be joined only once. + - `psgo.ListDescriptors() []string` - ListDescriptors returns a sorted string slice of all supported AIX format descriptors in the normal form (e.g., "args,comm,user"). It can be useful in the context of bash-completion, help messages, etc. @@ -28,6 +34,17 @@ root 4 2 0.000 6h3m27.678701852s ? 0s root 6 2 0.000 6h3m27.678999508s ? 0s [mm_percpu_wq] ``` +### Listing processes +You can use the `--pids` flag to restrict `psgo` output to a subset of processes. This option accepts a list of comma separate process IDs and will return exactly the same kind of information per process as the default output. + +``` +$ ./bin/psgo --pids 1,$(pgrep bash | tr "\n" ",") +USER PID PPID %CPU ELAPSED TTY TIME COMMAND +root 1 0 0.009 128h52m44.193475932s ? 40s systemd +root 20830 20827 0.000 105h2m44.19579679s pts/5 0s bash +root 25843 25840 0.000 102h56m4.196072027s pts/6 0s bash +``` + ### Listing processes within a container Let's have a look at how we can use this library in the context of containers. As a simple show case, we'll start a Docker container, extract the process ID via `docker-inspect` and run the `psgo` binary to extract the data of running processes within that container. @@ -38,7 +55,7 @@ $ docker run -d alpine sleep 100 $ docker inspect --format '{{.State.Pid}}' 473c9 5572 -$ sudo ./bin/psgo -pid 5572 +$ sudo ./bin/psgo -pids 5572 -join USER PID PPID %CPU ELAPSED TTY TIME COMMAND root 1 0 0.000 17.249905587s ? 0s sleep ``` diff --git a/vendor/github.com/containers/psgo/psgo.go b/vendor/github.com/containers/psgo/psgo.go index 2ea9a322b..3f3723600 100644 --- a/vendor/github.com/containers/psgo/psgo.go +++ b/vendor/github.com/containers/psgo/psgo.go @@ -80,7 +80,7 @@ func translateDescriptors(descriptors []string) ([]aixFormatDescriptor, error) { var ( // DefaultDescriptors is the `ps -ef` compatible default format. - DefaultDescriptors = []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "comm"} + DefaultDescriptors = []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "args"} // ErrUnkownDescriptor is returned when an unknown descriptor is parsed. ErrUnkownDescriptor = errors.New("unknown descriptor") @@ -334,6 +334,48 @@ func JoinNamespaceAndProcessInfo(pid string, descriptors []string) ([][]string, return data, dataErr } +// JoinNamespaceAndProcessInfoByPids has similar semantics to +// JoinNamespaceAndProcessInfo and avoids duplicate entries by joining a giving +// PID namepsace only once. +func JoinNamespaceAndProcessInfoByPids(pids []string, descriptors []string) ([][]string, error) { + // Extracting data from processes that share the same PID namespace + // would yield duplicate results. Avoid that by extracting data only + // from the first process in `pids` from a given PID namespace. + // `nsMap` is used for quick lookups if a given PID namespace is + // already covered, `pidList` is used to preserve the order which is + // not guaranteed by nondeterministic maps in golang. + nsMap := make(map[string]bool) + pidList := []string{} + for _, pid := range pids { + ns, err := proc.ParsePIDNamespace(pid) + if err != nil { + if os.IsNotExist(err) { + // catch race conditions + continue + } + return nil, errors.Wrapf(err, "error extracing PID namespace") + } + if _, exists := nsMap[ns]; !exists { + nsMap[ns] = true + pidList = append(pidList, pid) + } + } + + data := [][]string{} + for i, pid := range pidList { + pidData, err := JoinNamespaceAndProcessInfo(pid, descriptors) + if err != nil { + return nil, err + } + if i == 0 { + data = append(data, pidData[0]) + } + data = append(data, pidData[1:]...) + } + + return data, nil +} + // ProcessInfo returns the process information of all processes in the current // mount namespace. The input format must be a comma-separated list of // supported AIX format descriptors. If the input string is empty, the @@ -341,12 +383,18 @@ func JoinNamespaceAndProcessInfo(pid string, descriptors []string) ([][]string, // The return value is an array of tab-separated strings, to easily use the // output for column-based formatting (e.g., with the `text/tabwriter` package). func ProcessInfo(descriptors []string) ([][]string, error) { - aixDescriptors, err := translateDescriptors(descriptors) + pids, err := proc.GetPIDs() if err != nil { return nil, err } - pids, err := proc.GetPIDs() + return ProcessInfoByPids(pids, descriptors) +} + +// ProcessInfoByPids is like ProcessInfo, but the process information returned +// is limited to a list of user specified PIDs. +func ProcessInfoByPids(pids []string, descriptors []string) ([][]string, error) { + aixDescriptors, err := translateDescriptors(descriptors) if err != nil { return nil, err } @@ -470,22 +518,20 @@ func processName(p *process.Process) (string, error) { // processARGS returns the command of p with all its arguments. func processARGS(p *process.Process) (string, error) { - args := p.CmdLine // ps (1) returns "[$name]" if command/args are empty - if len(args) == 0 { + if p.CmdLine[0] == "" { return processName(p) } - return strings.Join(args, " "), nil + return strings.Join(p.CmdLine, " "), nil } // processCOMM returns the command name (i.e., executable name) of process p. func processCOMM(p *process.Process) (string, error) { - args := p.CmdLine // ps (1) returns "[$name]" if command/args are empty - if len(args) == 0 { + if p.CmdLine[0] == "" { return processName(p) } - spl := strings.Split(args[0], "/") + spl := strings.Split(p.CmdLine[0], "/") return spl[len(spl)-1], nil } -- cgit v1.2.3-54-g00ecf