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