github.com/hinshun/containerd@v0.2.7/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  }