Instead of calling lxc-ps in top endpoint, we reimplement it by
calling ps and filter for pids running in a given container.
| ... | ... |
@@ -1,7 +1,6 @@ |
| 1 | 1 |
package docker |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "bufio" |
|
| 5 | 4 |
"encoding/json" |
| 6 | 5 |
"errors" |
| 7 | 6 |
"fmt" |
| ... | ... |
@@ -690,34 +689,54 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) {
|
| 690 | 690 |
|
| 691 | 691 |
func (srv *Server) ContainerTop(name, psArgs string) (*APITop, error) {
|
| 692 | 692 |
if container := srv.runtime.Get(name); container != nil {
|
| 693 |
- output, err := exec.Command("lxc-ps", "--name", container.ID, "--", psArgs).CombinedOutput()
|
|
| 693 |
+ pids, err := utils.GetPidsForContainer(container.ID) |
|
| 694 | 694 |
if err != nil {
|
| 695 |
- return nil, fmt.Errorf("lxc-ps: %s (%s)", err, output)
|
|
| 695 |
+ return nil, err |
|
| 696 |
+ } |
|
| 697 |
+ if len(psArgs) == 0 {
|
|
| 698 |
+ psArgs = "-ef" |
|
| 699 |
+ } |
|
| 700 |
+ output, err := exec.Command("ps", psArgs).Output()
|
|
| 701 |
+ if err != nil {
|
|
| 702 |
+ return nil, fmt.Errorf("Error running ps: %s", err)
|
|
| 703 |
+ } |
|
| 704 |
+ |
|
| 705 |
+ lines := strings.Split(string(output), "\n") |
|
| 706 |
+ header := strings.Fields(lines[0]) |
|
| 707 |
+ procs := APITop{
|
|
| 708 |
+ Titles: header, |
|
| 709 |
+ } |
|
| 710 |
+ |
|
| 711 |
+ pidIndex := -1 |
|
| 712 |
+ for i, name := range header {
|
|
| 713 |
+ if name == "PID" {
|
|
| 714 |
+ pidIndex = i |
|
| 715 |
+ } |
|
| 696 | 716 |
} |
| 697 |
- procs := APITop{}
|
|
| 698 |
- for i, line := range strings.Split(string(output), "\n") {
|
|
| 717 |
+ if pidIndex == -1 {
|
|
| 718 |
+ return nil, errors.New("Couldn't find PID field in ps output")
|
|
| 719 |
+ } |
|
| 720 |
+ |
|
| 721 |
+ for _, line := range lines[1:] {
|
|
| 699 | 722 |
if len(line) == 0 {
|
| 700 | 723 |
continue |
| 701 | 724 |
} |
| 702 |
- words := []string{}
|
|
| 703 |
- scanner := bufio.NewScanner(strings.NewReader(line)) |
|
| 704 |
- scanner.Split(bufio.ScanWords) |
|
| 705 |
- if !scanner.Scan() {
|
|
| 706 |
- return nil, fmt.Errorf("Wrong output using lxc-ps")
|
|
| 725 |
+ fields := strings.Fields(line) |
|
| 726 |
+ p, err := strconv.Atoi(fields[pidIndex]) |
|
| 727 |
+ if err != nil {
|
|
| 728 |
+ return nil, fmt.Errorf("Unexpected pid '%s': %s", fields[pidIndex], err)
|
|
| 707 | 729 |
} |
| 708 |
- // no scanner.Text because we skip container id |
|
| 709 |
- for scanner.Scan() {
|
|
| 710 |
- if i != 0 && len(words) == len(procs.Titles) {
|
|
| 711 |
- words[len(words)-1] = fmt.Sprintf("%s %s", words[len(words)-1], scanner.Text())
|
|
| 712 |
- } else {
|
|
| 713 |
- words = append(words, scanner.Text()) |
|
| 730 |
+ |
|
| 731 |
+ for _, pid := range pids {
|
|
| 732 |
+ if pid == p {
|
|
| 733 |
+ // Make sure number of fields equals number of header titles |
|
| 734 |
+ // merging "overhanging" fields |
|
| 735 |
+ processes := fields[:len(procs.Titles)-1] |
|
| 736 |
+ processes = append(processes, strings.Join(fields[len(procs.Titles)-1:], " ")) |
|
| 737 |
+ |
|
| 738 |
+ procs.Processes = append(procs.Processes, processes) |
|
| 714 | 739 |
} |
| 715 | 740 |
} |
| 716 |
- if i == 0 {
|
|
| 717 |
- procs.Titles = words |
|
| 718 |
- } else {
|
|
| 719 |
- procs.Processes = append(procs.Processes, words) |
|
| 720 |
- } |
|
| 721 | 741 |
} |
| 722 | 742 |
return &procs, nil |
| 723 | 743 |
|
| ... | ... |
@@ -1117,3 +1117,59 @@ func CopyFile(src, dst string) (int64, error) {
|
| 1117 | 1117 |
defer df.Close() |
| 1118 | 1118 |
return io.Copy(df, sf) |
| 1119 | 1119 |
} |
| 1120 |
+ |
|
| 1121 |
+// Returns the relative path to the cgroup docker is running in. |
|
| 1122 |
+func GetThisCgroup(cgroupType string) (string, error) {
|
|
| 1123 |
+ output, err := ioutil.ReadFile("/proc/self/cgroup")
|
|
| 1124 |
+ if err != nil {
|
|
| 1125 |
+ return "", err |
|
| 1126 |
+ } |
|
| 1127 |
+ for _, line := range strings.Split(string(output), "\n") {
|
|
| 1128 |
+ parts := strings.Split(line, ":") |
|
| 1129 |
+ // any type used by docker should work |
|
| 1130 |
+ if parts[1] == cgroupType {
|
|
| 1131 |
+ return parts[2], nil |
|
| 1132 |
+ } |
|
| 1133 |
+ } |
|
| 1134 |
+ return "", fmt.Errorf("cgroup '%s' not found in /proc/self/cgroup", cgroupType)
|
|
| 1135 |
+} |
|
| 1136 |
+ |
|
| 1137 |
+// Returns a list of pids for the given container. |
|
| 1138 |
+func GetPidsForContainer(id string) ([]int, error) {
|
|
| 1139 |
+ pids := []int{}
|
|
| 1140 |
+ |
|
| 1141 |
+ // memory is chosen randomly, any cgroup used by docker works |
|
| 1142 |
+ cgroupType := "memory" |
|
| 1143 |
+ |
|
| 1144 |
+ cgroupRoot, err := FindCgroupMountpoint(cgroupType) |
|
| 1145 |
+ if err != nil {
|
|
| 1146 |
+ return pids, err |
|
| 1147 |
+ } |
|
| 1148 |
+ |
|
| 1149 |
+ cgroupThis, err := GetThisCgroup(cgroupType) |
|
| 1150 |
+ if err != nil {
|
|
| 1151 |
+ return pids, err |
|
| 1152 |
+ } |
|
| 1153 |
+ |
|
| 1154 |
+ filename := filepath.Join(cgroupRoot, cgroupThis, id, "tasks") |
|
| 1155 |
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
| 1156 |
+ // With more recent lxc versions use, cgroup will be in lxc/ |
|
| 1157 |
+ filename = filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks") |
|
| 1158 |
+ } |
|
| 1159 |
+ |
|
| 1160 |
+ output, err := ioutil.ReadFile(filename) |
|
| 1161 |
+ if err != nil {
|
|
| 1162 |
+ return pids, err |
|
| 1163 |
+ } |
|
| 1164 |
+ for _, p := range strings.Split(string(output), "\n") {
|
|
| 1165 |
+ if len(p) == 0 {
|
|
| 1166 |
+ continue |
|
| 1167 |
+ } |
|
| 1168 |
+ pid, err := strconv.Atoi(p) |
|
| 1169 |
+ if err != nil {
|
|
| 1170 |
+ return pids, fmt.Errorf("Invalid pid '%s': %s", p, err)
|
|
| 1171 |
+ } |
|
| 1172 |
+ pids = append(pids, pid) |
|
| 1173 |
+ } |
|
| 1174 |
+ return pids, nil |
|
| 1175 |
+} |