github.com/docker-archive/containerd@v0.2.8/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/docker/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 111 func (c *container) UpdateResources(r *Resource) error { 112 sr := ocs.Resources{ 113 Memory: &ocs.Memory{ 114 Limit: u64Ptr(uint64(r.Memory)), 115 Reservation: u64Ptr(uint64(r.MemoryReservation)), 116 Swap: u64Ptr(uint64(r.MemorySwap)), 117 Kernel: u64Ptr(uint64(r.KernelMemory)), 118 KernelTCP: u64Ptr(uint64(r.KernelTCPMemory)), 119 }, 120 CPU: &ocs.CPU{ 121 Shares: u64Ptr(uint64(r.CPUShares)), 122 Quota: u64Ptr(uint64(r.CPUQuota)), 123 Period: u64Ptr(uint64(r.CPUPeriod)), 124 Cpus: &r.CpusetCpus, 125 Mems: &r.CpusetMems, 126 }, 127 BlockIO: &ocs.BlockIO{ 128 Weight: &r.BlkioWeight, 129 }, 130 } 131 132 srStr := bytes.NewBuffer(nil) 133 if err := json.NewEncoder(srStr).Encode(&sr); err != nil { 134 return err 135 } 136 137 args := c.runtimeArgs 138 args = append(args, "update", "-r", "-", c.id) 139 cmd := exec.Command(c.runtime, args...) 140 cmd.Stdin = srStr 141 b, err := cmd.CombinedOutput() 142 if err != nil { 143 return fmt.Errorf(string(b)) 144 } 145 return nil 146 } 147 148 func getRootIDs(s *specs.Spec) (int, int, error) { 149 if s == nil { 150 return 0, 0, nil 151 } 152 var hasUserns bool 153 for _, ns := range s.Linux.Namespaces { 154 if ns.Type == ocs.UserNamespace { 155 hasUserns = true 156 break 157 } 158 } 159 if !hasUserns { 160 return 0, 0, nil 161 } 162 uid := hostIDFromMap(0, s.Linux.UIDMappings) 163 gid := hostIDFromMap(0, s.Linux.GIDMappings) 164 return uid, gid, nil 165 } 166 167 func (c *container) getMemoryEventFD(root string) (*oom, error) { 168 f, err := os.Open(filepath.Join(root, "memory.oom_control")) 169 if err != nil { 170 return nil, err 171 } 172 defer f.Close() 173 fd, _, serr := syscall.RawSyscall(syscall.SYS_EVENTFD2, 0, syscall.FD_CLOEXEC, 0) 174 if serr != 0 { 175 return nil, serr 176 } 177 if err := c.writeEventFD(root, int(f.Fd()), int(fd)); err != nil { 178 syscall.Close(int(fd)) 179 return nil, err 180 } 181 return &oom{ 182 root: root, 183 id: c.id, 184 eventfd: int(fd), 185 }, nil 186 }