mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
ebcb7d6b40
Use strongly typed errors to set HTTP status codes. Error interfaces are defined in the api/errors package and errors returned from controllers are checked against these interfaces. Errors can be wraeped in a pkg/errors.Causer, as long as somewhere in the line of causes one of the interfaces is implemented. The special error interfaces take precedence over Causer, meaning if both Causer and one of the new error interfaces are implemented, the Causer is not traversed. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
155 lines
3.7 KiB
Go
155 lines
3.7 KiB
Go
//+build !windows
|
|
|
|
package daemon
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
)
|
|
|
|
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 appendProcess2ProcList(procList *container.ContainerTopOKBody, fields []string) {
|
|
// 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)
|
|
}
|
|
|
|
func hasPid(pids []int, pid int) bool {
|
|
for _, i := range pids {
|
|
if i == pid {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func parsePSOutput(output []byte, pids []int) (*container.ContainerTopOKBody, error) {
|
|
procList := &container.ContainerTopOKBody{}
|
|
|
|
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
|
|
// fixing #30580, be able to display thread line also when "m" option used
|
|
// in "docker top" client command
|
|
preContainedPidFlag := false
|
|
for _, line := range lines[1:] {
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
fields := fieldsASCII(line)
|
|
|
|
var (
|
|
p int
|
|
err error
|
|
)
|
|
|
|
if fields[pidIndex] == "-" {
|
|
if preContainedPidFlag {
|
|
appendProcess2ProcList(procList, fields)
|
|
}
|
|
continue
|
|
}
|
|
p, err = strconv.Atoi(fields[pidIndex])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
|
|
}
|
|
|
|
if hasPid(pids, p) {
|
|
preContainedPidFlag = true
|
|
appendProcess2ProcList(procList, fields)
|
|
continue
|
|
}
|
|
preContainedPidFlag = false
|
|
}
|
|
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) (*container.ContainerTopOKBody, 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
|
|
}
|