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  }