github.com/docker/containerd@v0.2.9-0.20170509230648-8ef7df579710/runtime/container_linux.go (about) 1 package runtime 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strings" 12 "syscall" 13 14 "github.com/containerd/containerd/specs" 15 ocs "github.com/opencontainers/runtime-spec/specs-go" 16 ) 17 18 func findCgroupMountpointAndRoot(pid int, subsystem string) (string, string, error) { 19 f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) 20 if err != nil { 21 return "", "", err 22 } 23 defer f.Close() 24 25 scanner := bufio.NewScanner(f) 26 for scanner.Scan() { 27 txt := scanner.Text() 28 fields := strings.Split(txt, " ") 29 for _, opt := range strings.Split(fields[len(fields)-1], ",") { 30 if opt == subsystem { 31 return fields[4], fields[3], nil 32 } 33 } 34 } 35 if err := scanner.Err(); err != nil { 36 return "", "", err 37 } 38 39 return "", "", fmt.Errorf("cgroup path for %s not found", subsystem) 40 } 41 42 func parseCgroupFile(path string) (map[string]string, error) { 43 f, err := os.Open(path) 44 if err != nil { 45 return nil, err 46 } 47 defer f.Close() 48 49 s := bufio.NewScanner(f) 50 cgroups := make(map[string]string) 51 52 for s.Scan() { 53 if err := s.Err(); err != nil { 54 return nil, err 55 } 56 57 text := s.Text() 58 parts := strings.Split(text, ":") 59 60 for _, subs := range strings.Split(parts[1], ",") { 61 cgroups[subs] = parts[2] 62 } 63 } 64 return cgroups, nil 65 } 66 67 func (c *container) OOM() (OOM, error) { 68 p := c.processes[InitProcessID] 69 if p == nil { 70 return nil, fmt.Errorf("no init process found") 71 } 72 73 mountpoint, hostRoot, err := findCgroupMountpointAndRoot(os.Getpid(), "memory") 74 if err != nil { 75 return nil, err 76 } 77 78 cgroups, err := parseCgroupFile(fmt.Sprintf("/proc/%d/cgroup", p.pid)) 79 if err != nil { 80 return nil, err 81 } 82 83 root, ok := cgroups["memory"] 84 if !ok { 85 return nil, fmt.Errorf("no memory cgroup for container %s", c.ID()) 86 } 87 88 // Take care of the case were we're running inside a container 89 // ourself 90 root = strings.TrimPrefix(root, hostRoot) 91 92 return c.getMemoryEventFD(filepath.Join(mountpoint, root)) 93 } 94 95 func (c *container) Pids() ([]int, error) { 96 var pids []int 97 args := c.runtimeArgs 98 args = append(args, "ps", "--format=json", c.id) 99 out, err := exec.Command(c.runtime, args...).CombinedOutput() 100 if err != nil { 101 return nil, fmt.Errorf("%s: %q", err.Error(), out) 102 } 103 if err := json.Unmarshal(out, &pids); err != nil { 104 return nil, err 105 } 106 return pids, nil 107 } 108 109 func u64Ptr(i uint64) *uint64 { return &i } 110 func i64Ptr(i int64) *int64 { return &i } 111 112 func (c *container) UpdateResources(r *Resource) error { 113 sr := ocs.LinuxResources{ 114 Memory: &ocs.LinuxMemory{ 115 Limit: u64Ptr(uint64(r.Memory)), 116 Reservation: u64Ptr(uint64(r.MemoryReservation)), 117 Swap: u64Ptr(uint64(r.MemorySwap)), 118 Kernel: u64Ptr(uint64(r.KernelMemory)), 119 KernelTCP: u64Ptr(uint64(r.KernelTCPMemory)), 120 }, 121 CPU: &ocs.LinuxCPU{ 122 Shares: u64Ptr(uint64(r.CPUShares)), 123 Quota: i64Ptr(int64(r.CPUQuota)), 124 Period: u64Ptr(uint64(r.CPUPeriod)), 125 Cpus: r.CpusetCpus, 126 Mems: r.CpusetMems, 127 }, 128 BlockIO: &ocs.LinuxBlockIO{ 129 Weight: &r.BlkioWeight, 130 }, 131 Pids: &ocs.LinuxPids{ 132 Limit: r.PidsLimit, 133 }, 134 } 135 136 srStr := bytes.NewBuffer(nil) 137 if err := json.NewEncoder(srStr).Encode(&sr); err != nil { 138 return err 139 } 140 141 args := c.runtimeArgs 142 args = append(args, "update", "-r", "-", c.id) 143 cmd := exec.Command(c.runtime, args...) 144 cmd.Stdin = srStr 145 b, err := cmd.CombinedOutput() 146 if err != nil { 147 return fmt.Errorf(string(b)) 148 } 149 return nil 150 } 151 152 func getRootIDs(s *specs.Spec) (int, int, error) { 153 if s == nil { 154 return 0, 0, nil 155 } 156 var hasUserns bool 157 for _, ns := range s.Linux.Namespaces { 158 if ns.Type == ocs.UserNamespace { 159 hasUserns = true 160 break 161 } 162 } 163 if !hasUserns { 164 return 0, 0, nil 165 } 166 uid := hostIDFromMap(0, s.Linux.UIDMappings) 167 gid := hostIDFromMap(0, s.Linux.GIDMappings) 168 return uid, gid, nil 169 } 170 171 func (c *container) getMemoryEventFD(root string) (*oom, error) { 172 f, err := os.Open(filepath.Join(root, "memory.oom_control")) 173 if err != nil { 174 return nil, err 175 } 176 defer f.Close() 177 fd, _, serr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0) 178 if serr != 0 { 179 return nil, serr 180 } 181 if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil { 182 syscall.Close(int(fd)) 183 return nil, err 184 } 185 return &oom{ 186 root: root, 187 id: c.id, 188 eventfd: int(fd), 189 }, nil 190 }