12 "github.com/docker/docker/api/types/container"
15 func validatePSArgs(psArgs string) error {
16 // NOTE: \\s does not detect unicode whitespaces.
17 // So we use fieldsASCII instead of strings.Fields in parsePSOutput.
18 // See https://github.com/docker/docker/pull/24358
19 re := regexp.MustCompile("\\s+([^\\s]*)=\\s*(PID[^\\s]*)")
20 for _, group := range re.FindAllStringSubmatch(psArgs, -1) {
25 return fmt.Errorf("specifying \"%s=%s\" is not allowed", k, v)
32 // fieldsASCII is similar to strings.Fields but only allows ASCII whitespaces
33 func fieldsASCII(s string) []string {
34 fn := func(r rune) bool {
36 case '\t', '\n', '\f', '\r', ' ':
41 return strings.FieldsFunc(s, fn)
44 func appendProcess2ProcList(procList *container.ContainerTopOKBody, fields []string) {
45 // Make sure number of fields equals number of header titles
46 // merging "overhanging" fields
47 process := fields[:len(procList.Titles)-1]
48 process = append(process, strings.Join(fields[len(procList.Titles)-1:], " "))
49 procList.Processes = append(procList.Processes, process)
52 func hasPid(pids []int, pid int) bool {
53 for _, i := range pids {
61 func parsePSOutput(output []byte, pids []int) (*container.ContainerTopOKBody, error) {
62 procList := &container.ContainerTopOKBody{}
64 lines := strings.Split(string(output), "\n")
65 procList.Titles = fieldsASCII(lines[0])
68 for i, name := range procList.Titles {
74 return nil, fmt.Errorf("Couldn't find PID field in ps output")
77 // loop through the output and extract the PID from each line
78 // fixing #30580, be able to display thread line also when "m" option used
79 // in "docker top" client command
80 preContainedPidFlag := false
81 for _, line := range lines[1:] {
85 fields := fieldsASCII(line)
92 if fields[pidIndex] == "-" {
93 if preContainedPidFlag {
94 appendProcess2ProcList(procList, fields)
98 p, err = strconv.Atoi(fields[pidIndex])
100 return nil, fmt.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
104 preContainedPidFlag = true
105 appendProcess2ProcList(procList, fields)
108 preContainedPidFlag = false
113 // ContainerTop lists the processes running inside of the given
114 // container by calling ps with the given args, or with the flags
115 // "-ef" if no args are given. An error is returned if the container
116 // is not found, or is not running, or if there are any problems
117 // running ps, or parsing the output.
118 func (daemon *Daemon) ContainerTop(name string, psArgs string) (*container.ContainerTopOKBody, error) {
123 if err := validatePSArgs(psArgs); err != nil {
127 container, err := daemon.GetContainer(name)
132 if !container.IsRunning() {
133 return nil, errNotRunning{container.ID}
136 if container.IsRestarting() {
137 return nil, errContainerIsRestarting(container.ID)
140 pids, err := daemon.containerd.GetPidsForContainer(container.ID)
145 output, err := exec.Command("ps", strings.Split(psArgs, " ")...).Output()
147 return nil, fmt.Errorf("Error running ps: %v", err)
149 procList, err := parsePSOutput(output, pids)
153 daemon.LogContainerEvent(container, "top")