123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- //+build !windows
- package daemon
- import (
- "fmt"
- "os/exec"
- "regexp"
- "strconv"
- "strings"
- "github.com/docker/engine-api/types"
- )
- func validatePSArgs(psArgs string) error {
- // NOTE: \\s does not detect unicode whitespaces.
- // So we use fieldsASCII instead of strings.Fields in parsePSOutput.
- // See https://github.com/docker/docker/pull/24358
- re := regexp.MustCompile("\\s+([^\\s]*)=\\s*(PID[^\\s]*)")
- for _, group := range re.FindAllStringSubmatch(psArgs, -1) {
- if len(group) >= 3 {
- k := group[1]
- v := group[2]
- if k != "pid" {
- return fmt.Errorf("specifying \"%s=%s\" is not allowed", k, v)
- }
- }
- }
- return nil
- }
- // fieldsASCII is similar to strings.Fields but only allows 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)
- }
- func parsePSOutput(output []byte, pids []int) (*types.ContainerProcessList, error) {
- procList := &types.ContainerProcessList{}
- lines := strings.Split(string(output), "\n")
- procList.Titles = fieldsASCII(lines[0])
- pidIndex := -1
- for i, name := range procList.Titles {
- if name == "PID" {
- pidIndex = i
- }
- }
- if pidIndex == -1 {
- return nil, fmt.Errorf("Couldn't find PID field in ps output")
- }
- // loop through the output and extract the PID from each line
- for _, line := range lines[1:] {
- if len(line) == 0 {
- continue
- }
- fields := fieldsASCII(line)
- p, err := strconv.Atoi(fields[pidIndex])
- if err != nil {
- return nil, fmt.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
- }
- for _, pid := range pids {
- if pid == p {
- // Make sure number of fields equals number of header titles
- // merging "overhanging" fields
- process := fields[:len(procList.Titles)-1]
- process = append(process, strings.Join(fields[len(procList.Titles)-1:], " "))
- procList.Processes = append(procList.Processes, process)
- }
- }
- }
- return procList, nil
- }
- // ContainerTop lists the processes running inside of the given
- // container by calling ps with the given args, or with the flags
- // "-ef" if no args are given. An error is returned if the container
- // is not found, or is not running, or if there are any problems
- // running ps, or parsing the output.
- func (daemon *Daemon) ContainerTop(name string, psArgs string) (*types.ContainerProcessList, error) {
- if psArgs == "" {
- psArgs = "-ef"
- }
- if err := validatePSArgs(psArgs); err != nil {
- return nil, err
- }
- container, err := daemon.GetContainer(name)
- if err != nil {
- return nil, err
- }
- if !container.IsRunning() {
- return nil, errNotRunning{container.ID}
- }
- if container.IsRestarting() {
- return nil, errContainerIsRestarting(container.ID)
- }
- pids, err := daemon.containerd.GetPidsForContainer(container.ID)
- if err != nil {
- return nil, err
- }
- output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
- if err != nil {
- return nil, fmt.Errorf("Error running ps: %v", err)
- }
- procList, err := parsePSOutput(output, pids)
- if err != nil {
- return nil, err
- }
- daemon.LogContainerEvent(container, "top")
- return procList, nil
- }
|