github.com/opencontainers/runtime-tools@v0.9.0/cgroups/cgroups_v1.go (about)

     1  package cgroups
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  
    12  	rspec "github.com/opencontainers/runtime-spec/specs-go"
    13  	"github.com/opencontainers/runtime-tools/specerror"
    14  )
    15  
    16  // CgroupV1 used for cgroupv1 validation
    17  type CgroupV1 struct {
    18  	MountPath string
    19  }
    20  
    21  func getDeviceID(id string) (int64, int64, error) {
    22  	elem := strings.Split(id, ":")
    23  	major, err := strconv.ParseInt(elem[0], 10, 64)
    24  	if err != nil {
    25  		return 0, 0, err
    26  	}
    27  	minor, err := strconv.ParseInt(elem[1], 10, 64)
    28  	if err != nil {
    29  		return 0, 0, err
    30  	}
    31  	return major, minor, nil
    32  }
    33  
    34  // GetBlockIOData gets cgroup blockio data
    35  func (cg *CgroupV1) GetBlockIOData(pid int, cgPath string) (*rspec.LinuxBlockIO, error) {
    36  	if filepath.IsAbs(cgPath) {
    37  		path := filepath.Join(cg.MountPath, "blkio", cgPath)
    38  		if _, err := os.Stat(path); err != nil {
    39  			if os.IsNotExist(err) {
    40  				return nil, specerror.NewError(specerror.CgroupsAbsPathRelToMount, fmt.Errorf("In the case of an absolute path, the runtime MUST take the path to be relative to the cgroups mount point"), rspec.Version)
    41  			}
    42  			return nil, err
    43  		}
    44  	}
    45  	lb := &rspec.LinuxBlockIO{}
    46  	names := []string{"weight", "leaf_weight", "weight_device", "leaf_weight_device", "throttle.read_bps_device", "throttle.write_bps_device", "throttle.read_iops_device", "throttle.write_iops_device"}
    47  	for i, name := range names {
    48  		fileName := strings.Join([]string{"blkio", name}, ".")
    49  		filePath := filepath.Join(cg.MountPath, "blkio", cgPath, fileName)
    50  		if !filepath.IsAbs(cgPath) {
    51  			subPath, err := GetSubsystemPath(pid, "blkio")
    52  			if err != nil {
    53  				return nil, err
    54  			}
    55  			if !strings.Contains(subPath, cgPath) {
    56  				return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "blkio")
    57  			}
    58  			filePath = filepath.Join(cg.MountPath, "blkio", subPath, fileName)
    59  		}
    60  		contents, err := ioutil.ReadFile(filePath)
    61  		if err != nil {
    62  			if os.IsNotExist(err) {
    63  				return nil, specerror.NewError(specerror.CgroupsPathAttach, fmt.Errorf("The runtime MUST consistently attach to the same place in the cgroups hierarchy given the same value of `cgroupsPath`"), rspec.Version)
    64  			}
    65  
    66  			return nil, err
    67  		}
    68  		switch i {
    69  		case 0:
    70  			res, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 16)
    71  			if err != nil {
    72  				return nil, err
    73  			}
    74  			weight := uint16(res)
    75  			lb.Weight = &weight
    76  		case 1:
    77  			res, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 16)
    78  			if err != nil {
    79  				return nil, err
    80  			}
    81  			leafWeight := uint16(res)
    82  			lb.LeafWeight = &leafWeight
    83  		case 2:
    84  			parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
    85  			for _, part := range parts {
    86  				elem := strings.Split(part, " ")
    87  				major, minor, err := getDeviceID(elem[0])
    88  				if err != nil {
    89  					return nil, err
    90  				}
    91  				res, err := strconv.ParseUint(elem[1], 10, 16)
    92  				if err != nil {
    93  					return nil, err
    94  				}
    95  				weight := uint16(res)
    96  				lwd := rspec.LinuxWeightDevice{}
    97  				lwd.Major = major
    98  				lwd.Minor = minor
    99  				lwd.Weight = &weight
   100  				lb.WeightDevice = append(lb.WeightDevice, lwd)
   101  			}
   102  		case 3:
   103  			parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
   104  			for _, part := range parts {
   105  				elem := strings.Split(part, " ")
   106  				major, minor, err := getDeviceID(elem[0])
   107  				if err != nil {
   108  					return nil, err
   109  				}
   110  				res, err := strconv.ParseUint(elem[1], 10, 16)
   111  				if err != nil {
   112  					return nil, err
   113  				}
   114  				leafWeight := uint16(res)
   115  				exist := false
   116  				for i, wd := range lb.WeightDevice {
   117  					if wd.Major == major && wd.Minor == minor {
   118  						exist = true
   119  						lb.WeightDevice[i].LeafWeight = &leafWeight
   120  						break
   121  					}
   122  				}
   123  				if !exist {
   124  					lwd := rspec.LinuxWeightDevice{}
   125  					lwd.Major = major
   126  					lwd.Minor = minor
   127  					lwd.LeafWeight = &leafWeight
   128  					lb.WeightDevice = append(lb.WeightDevice, lwd)
   129  				}
   130  			}
   131  		case 4:
   132  			parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
   133  			for _, part := range parts {
   134  				elem := strings.Split(part, " ")
   135  				major, minor, err := getDeviceID(elem[0])
   136  				if err != nil {
   137  					return nil, err
   138  				}
   139  				rate, err := strconv.ParseUint(elem[1], 10, 64)
   140  				if err != nil {
   141  					return nil, err
   142  				}
   143  				ltd := rspec.LinuxThrottleDevice{}
   144  				ltd.Major = major
   145  				ltd.Minor = minor
   146  				ltd.Rate = rate
   147  				lb.ThrottleReadBpsDevice = append(lb.ThrottleReadBpsDevice, ltd)
   148  			}
   149  		case 5:
   150  			parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
   151  			for _, part := range parts {
   152  				elem := strings.Split(part, " ")
   153  				major, minor, err := getDeviceID(elem[0])
   154  				if err != nil {
   155  					return nil, err
   156  				}
   157  				rate, err := strconv.ParseUint(elem[1], 10, 64)
   158  				if err != nil {
   159  					return nil, err
   160  				}
   161  				ltd := rspec.LinuxThrottleDevice{}
   162  				ltd.Major = major
   163  				ltd.Minor = minor
   164  				ltd.Rate = rate
   165  				lb.ThrottleWriteBpsDevice = append(lb.ThrottleWriteBpsDevice, ltd)
   166  			}
   167  		case 6:
   168  			parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
   169  			for _, part := range parts {
   170  				elem := strings.Split(part, " ")
   171  				major, minor, err := getDeviceID(elem[0])
   172  				if err != nil {
   173  					return nil, err
   174  				}
   175  				rate, err := strconv.ParseUint(elem[1], 10, 64)
   176  				if err != nil {
   177  					return nil, err
   178  				}
   179  				ltd := rspec.LinuxThrottleDevice{}
   180  				ltd.Major = major
   181  				ltd.Minor = minor
   182  				ltd.Rate = rate
   183  				lb.ThrottleReadIOPSDevice = append(lb.ThrottleReadIOPSDevice, ltd)
   184  			}
   185  		case 7:
   186  			parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
   187  			for _, part := range parts {
   188  				elem := strings.Split(part, " ")
   189  				major, minor, err := getDeviceID(elem[0])
   190  				if err != nil {
   191  					return nil, err
   192  				}
   193  				rate, err := strconv.ParseUint(elem[1], 10, 64)
   194  				if err != nil {
   195  					return nil, err
   196  				}
   197  				ltd := rspec.LinuxThrottleDevice{}
   198  				ltd.Major = major
   199  				ltd.Minor = minor
   200  				ltd.Rate = rate
   201  				lb.ThrottleWriteIOPSDevice = append(lb.ThrottleWriteIOPSDevice, ltd)
   202  			}
   203  		}
   204  	}
   205  
   206  	return lb, nil
   207  }
   208  
   209  // GetCPUData gets cgroup cpus data
   210  func (cg *CgroupV1) GetCPUData(pid int, cgPath string) (*rspec.LinuxCPU, error) {
   211  	if filepath.IsAbs(cgPath) {
   212  		path := filepath.Join(cg.MountPath, "cpu", cgPath)
   213  		if _, err := os.Stat(path); err != nil {
   214  			if os.IsNotExist(err) {
   215  				return nil, specerror.NewError(specerror.CgroupsAbsPathRelToMount, fmt.Errorf("In the case of an absolute path, the runtime MUST take the path to be relative to the cgroups mount point"), rspec.Version)
   216  			}
   217  			return nil, err
   218  		}
   219  	}
   220  	lc := &rspec.LinuxCPU{}
   221  	names := []string{"shares", "cfs_quota_us", "cfs_period_us"}
   222  	for i, name := range names {
   223  		fileName := strings.Join([]string{"cpu", name}, ".")
   224  		filePath := filepath.Join(cg.MountPath, "cpu", cgPath, fileName)
   225  		if !filepath.IsAbs(cgPath) {
   226  			subPath, err := GetSubsystemPath(pid, "cpu")
   227  			if err != nil {
   228  				return nil, err
   229  			}
   230  			if !strings.Contains(subPath, cgPath) {
   231  				return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "cpu")
   232  			}
   233  			filePath = filepath.Join(cg.MountPath, "cpu", subPath, fileName)
   234  		}
   235  		contents, err := ioutil.ReadFile(filePath)
   236  		if err != nil {
   237  			if os.IsNotExist(err) {
   238  				return nil, specerror.NewError(specerror.CgroupsPathAttach, fmt.Errorf("The runtime MUST consistently attach to the same place in the cgroups hierarchy given the same value of `cgroupsPath`"), rspec.Version)
   239  			}
   240  
   241  			return nil, err
   242  		}
   243  		switch i {
   244  		case 0:
   245  			res, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64)
   246  			if err != nil {
   247  				return nil, err
   248  			}
   249  			shares := res
   250  			lc.Shares = &shares
   251  		case 1:
   252  			res, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   253  			if err != nil {
   254  				return nil, err
   255  			}
   256  			quota := res
   257  			lc.Quota = &quota
   258  		case 2:
   259  			res, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64)
   260  			if err != nil {
   261  				return nil, err
   262  			}
   263  			period := res
   264  			lc.Period = &period
   265  		}
   266  	}
   267  	// CONFIG_RT_GROUP_SCHED may be not set
   268  	// Can always get rt data from /proc
   269  	contents, err := ioutil.ReadFile("/proc/sys/kernel/sched_rt_period_us")
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	rtPeriod, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	lc.RealtimePeriod = &rtPeriod
   278  	contents, err = ioutil.ReadFile("/proc/sys/kernel/sched_rt_runtime_us")
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	rtQuota, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	lc.RealtimeRuntime = &rtQuota
   287  
   288  	names = []string{"cpus", "mems"}
   289  	for i, name := range names {
   290  		fileName := strings.Join([]string{"cpuset", name}, ".")
   291  		filePath := filepath.Join(cg.MountPath, "cpuset", cgPath, fileName)
   292  		if !filepath.IsAbs(cgPath) {
   293  			subPath, err := GetSubsystemPath(pid, "cpuset")
   294  			if err != nil {
   295  				return nil, err
   296  			}
   297  			if !strings.Contains(subPath, cgPath) {
   298  				return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "cpuset")
   299  			}
   300  			filePath = filepath.Join(cg.MountPath, "cpuset", subPath, fileName)
   301  		}
   302  		contents, err := ioutil.ReadFile(filePath)
   303  		if err != nil {
   304  			return nil, err
   305  		}
   306  		switch i {
   307  		case 0:
   308  			lc.Cpus = strings.TrimSpace(string(contents))
   309  		case 1:
   310  			lc.Mems = strings.TrimSpace(string(contents))
   311  		}
   312  	}
   313  
   314  	return lc, nil
   315  }
   316  
   317  // GetDevicesData gets cgroup devices data
   318  func (cg *CgroupV1) GetDevicesData(pid int, cgPath string) ([]rspec.LinuxDeviceCgroup, error) {
   319  	ld := []rspec.LinuxDeviceCgroup{}
   320  	fileName := strings.Join([]string{"devices", "list"}, ".")
   321  	filePath := filepath.Join(cg.MountPath, "devices", cgPath, fileName)
   322  	if !filepath.IsAbs(cgPath) {
   323  		subPath, err := GetSubsystemPath(pid, "devices")
   324  		if err != nil {
   325  			return nil, err
   326  		}
   327  		if !strings.Contains(subPath, cgPath) {
   328  			return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "devices")
   329  		}
   330  		filePath = filepath.Join(cg.MountPath, "devices", subPath, fileName)
   331  	}
   332  	contents, err := ioutil.ReadFile(filePath)
   333  	if err != nil {
   334  		return nil, err
   335  	}
   336  	parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
   337  	for _, part := range parts {
   338  		elem := strings.Split(part, " ")
   339  		ele := strings.Split(elem[1], ":")
   340  		var major, minor int64
   341  		if ele[0] == "*" {
   342  			major = 0
   343  		} else {
   344  			major, err = strconv.ParseInt(ele[0], 10, 64)
   345  			if err != nil {
   346  				return nil, err
   347  			}
   348  		}
   349  		if ele[1] == "*" {
   350  			minor = 0
   351  		} else {
   352  			minor, err = strconv.ParseInt(ele[1], 10, 64)
   353  			if err != nil {
   354  				return nil, err
   355  			}
   356  		}
   357  
   358  		device := rspec.LinuxDeviceCgroup{}
   359  		device.Allow = true
   360  		device.Type = elem[0]
   361  		device.Major = &major
   362  		device.Minor = &minor
   363  		device.Access = elem[2]
   364  		ld = append(ld, device)
   365  	}
   366  
   367  	return ld, nil
   368  }
   369  
   370  func inBytes(size string) (int64, error) {
   371  	KiB := 1024
   372  	MiB := 1024 * KiB
   373  	GiB := 1024 * MiB
   374  	TiB := 1024 * GiB
   375  	PiB := 1024 * TiB
   376  	binaryMap := map[string]int64{"k": int64(KiB), "m": int64(MiB), "g": int64(GiB), "t": int64(TiB), "p": int64(PiB)}
   377  	sizeRegex := regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
   378  	matches := sizeRegex.FindStringSubmatch(size)
   379  	if len(matches) != 4 {
   380  		return -1, fmt.Errorf("invalid size: '%s'", size)
   381  	}
   382  
   383  	byteSize, err := strconv.ParseFloat(matches[1], 64)
   384  	if err != nil {
   385  		return -1, err
   386  	}
   387  
   388  	unitPrefix := strings.ToLower(matches[3])
   389  	if mul, ok := binaryMap[unitPrefix]; ok {
   390  		byteSize *= float64(mul)
   391  	}
   392  
   393  	return int64(byteSize), nil
   394  }
   395  
   396  func getHugePageSize() ([]string, error) {
   397  	var pageSizes []string
   398  	sizeList := []string{"B", "kB", "MB", "GB", "TB", "PB"}
   399  	files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages")
   400  	if err != nil {
   401  		return pageSizes, err
   402  	}
   403  	for _, st := range files {
   404  		nameArray := strings.Split(st.Name(), "-")
   405  		pageSize, err := inBytes(nameArray[1])
   406  		if err != nil {
   407  			return []string{}, err
   408  		}
   409  		size := float64(pageSize)
   410  		base := float64(1024.0)
   411  		i := 0
   412  		unitsLimit := len(sizeList) - 1
   413  		for size >= base && i < unitsLimit {
   414  			size = size / base
   415  			i++
   416  		}
   417  		sizeString := fmt.Sprintf("%g%s", size, sizeList[i])
   418  		pageSizes = append(pageSizes, sizeString)
   419  	}
   420  
   421  	return pageSizes, nil
   422  }
   423  
   424  // GetHugepageLimitData gets cgroup hugetlb data
   425  func (cg *CgroupV1) GetHugepageLimitData(pid int, cgPath string) ([]rspec.LinuxHugepageLimit, error) {
   426  	if filepath.IsAbs(cgPath) {
   427  		path := filepath.Join(cg.MountPath, "hugetlb", cgPath)
   428  		if _, err := os.Stat(path); err != nil {
   429  			if os.IsNotExist(err) {
   430  				return nil, specerror.NewError(specerror.CgroupsAbsPathRelToMount, fmt.Errorf("In the case of an absolute path, the runtime MUST take the path to be relative to the cgroups mount point"), rspec.Version)
   431  			}
   432  			return nil, err
   433  		}
   434  	}
   435  	lh := []rspec.LinuxHugepageLimit{}
   436  	pageSizes, err := getHugePageSize()
   437  	if err != nil {
   438  		return lh, err
   439  	}
   440  	for _, pageSize := range pageSizes {
   441  		maxUsage := strings.Join([]string{"hugetlb", pageSize, "limit_in_bytes"}, ".")
   442  		filePath := filepath.Join(cg.MountPath, "hugetlb", cgPath, maxUsage)
   443  		if !filepath.IsAbs(cgPath) {
   444  			subPath, err := GetSubsystemPath(pid, "hugetlb")
   445  			if err != nil {
   446  				return lh, err
   447  			}
   448  			if !strings.Contains(subPath, cgPath) {
   449  				return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "hugetlb")
   450  			}
   451  			filePath = filepath.Join(cg.MountPath, "hugetlb", subPath, maxUsage)
   452  		}
   453  		contents, err := ioutil.ReadFile(filePath)
   454  		if err != nil {
   455  			if os.IsNotExist(err) {
   456  				return nil, specerror.NewError(specerror.CgroupsPathAttach, fmt.Errorf("The runtime MUST consistently attach to the same place in the cgroups hierarchy given the same value of `cgroupsPath`"), rspec.Version)
   457  			}
   458  
   459  			return lh, err
   460  		}
   461  		res, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64)
   462  		if err != nil {
   463  			return nil, err
   464  		}
   465  		pageLimit := rspec.LinuxHugepageLimit{}
   466  		pageLimit.Pagesize = pageSize
   467  		pageLimit.Limit = res
   468  		lh = append(lh, pageLimit)
   469  	}
   470  
   471  	return lh, nil
   472  }
   473  
   474  // GetMemoryData gets cgroup memory data
   475  func (cg *CgroupV1) GetMemoryData(pid int, cgPath string) (*rspec.LinuxMemory, error) {
   476  	if filepath.IsAbs(cgPath) {
   477  		path := filepath.Join(cg.MountPath, "memory", cgPath)
   478  		if _, err := os.Stat(path); err != nil {
   479  			if os.IsNotExist(err) {
   480  				return nil, specerror.NewError(specerror.CgroupsAbsPathRelToMount, fmt.Errorf("In the case of an absolute path, the runtime MUST take the path to be relative to the cgroups mount point"), rspec.Version)
   481  			}
   482  			return nil, err
   483  		}
   484  	}
   485  	lm := &rspec.LinuxMemory{}
   486  	names := []string{"limit_in_bytes", "soft_limit_in_bytes", "memsw.limit_in_bytes", "kmem.limit_in_bytes", "kmem.tcp.limit_in_bytes", "swappiness", "oom_control"}
   487  	for i, name := range names {
   488  		fileName := strings.Join([]string{"memory", name}, ".")
   489  		filePath := filepath.Join(cg.MountPath, "memory", cgPath, fileName)
   490  		if !filepath.IsAbs(cgPath) {
   491  			subPath, err := GetSubsystemPath(pid, "memory")
   492  			if err != nil {
   493  				return nil, err
   494  			}
   495  			if !strings.Contains(subPath, cgPath) {
   496  				return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "memory")
   497  			}
   498  			filePath = filepath.Join(cg.MountPath, "memory", subPath, fileName)
   499  		}
   500  		contents, err := ioutil.ReadFile(filePath)
   501  		if err != nil {
   502  			if os.IsNotExist(err) {
   503  				return nil, specerror.NewError(specerror.CgroupsPathAttach, fmt.Errorf("The runtime MUST consistently attach to the same place in the cgroups hierarchy given the same value of `cgroupsPath`"), rspec.Version)
   504  			}
   505  
   506  			return nil, err
   507  		}
   508  		switch i {
   509  		case 0:
   510  			res, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   511  			if err != nil {
   512  				return nil, err
   513  			}
   514  			limit := res
   515  			lm.Limit = &limit
   516  		case 1:
   517  			res, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   518  			if err != nil {
   519  				return nil, err
   520  			}
   521  			sLimit := res
   522  			lm.Reservation = &sLimit
   523  		case 2:
   524  			res, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   525  			if err != nil {
   526  				return nil, err
   527  			}
   528  			swLimit := res
   529  			lm.Swap = &swLimit
   530  		case 3:
   531  			res, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   532  			if err != nil {
   533  				return nil, err
   534  			}
   535  			kernelLimit := res
   536  			lm.Kernel = &kernelLimit
   537  		case 4:
   538  			res, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   539  			if err != nil {
   540  				return nil, err
   541  			}
   542  			tcpLimit := res
   543  			lm.KernelTCP = &tcpLimit
   544  		case 5:
   545  			res, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64)
   546  			if err != nil {
   547  				return nil, err
   548  			}
   549  			swappiness := res
   550  			lm.Swappiness = &swappiness
   551  		case 6:
   552  			parts := strings.Split(string(contents), "\n")
   553  			part := strings.Split(parts[0], " ")
   554  			res, err := strconv.ParseInt(part[1], 10, 64)
   555  			if err != nil {
   556  				return nil, err
   557  			}
   558  			oom := false
   559  			if res == 1 {
   560  				oom = true
   561  			}
   562  			lm.DisableOOMKiller = &oom
   563  		}
   564  	}
   565  
   566  	return lm, nil
   567  }
   568  
   569  // GetNetworkData gets cgroup network data
   570  func (cg *CgroupV1) GetNetworkData(pid int, cgPath string) (*rspec.LinuxNetwork, error) {
   571  	if filepath.IsAbs(cgPath) {
   572  		path := filepath.Join(cg.MountPath, "net_cls", cgPath)
   573  		if _, err := os.Stat(path); err != nil {
   574  			if os.IsNotExist(err) {
   575  				return nil, specerror.NewError(specerror.CgroupsAbsPathRelToMount, fmt.Errorf("In the case of an absolute path, the runtime MUST take the path to be relative to the cgroups mount point"), rspec.Version)
   576  			}
   577  			return nil, err
   578  		}
   579  	}
   580  	ln := &rspec.LinuxNetwork{}
   581  	fileName := strings.Join([]string{"net_cls", "classid"}, ".")
   582  	filePath := filepath.Join(cg.MountPath, "net_cls", cgPath, fileName)
   583  	if !filepath.IsAbs(cgPath) {
   584  		subPath, err := GetSubsystemPath(pid, "net_cls")
   585  		if err != nil {
   586  			return nil, err
   587  		}
   588  		if !strings.Contains(subPath, cgPath) {
   589  			return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "net_cls")
   590  		}
   591  		filePath = filepath.Join(cg.MountPath, "net_cls", subPath, fileName)
   592  	}
   593  	contents, err := ioutil.ReadFile(filePath)
   594  	if err != nil {
   595  		if os.IsNotExist(err) {
   596  			return nil, specerror.NewError(specerror.CgroupsPathAttach, fmt.Errorf("The runtime MUST consistently attach to the same place in the cgroups hierarchy given the same value of `cgroupsPath`"), rspec.Version)
   597  		}
   598  
   599  		return nil, err
   600  	}
   601  	res, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 10, 64)
   602  	if err != nil {
   603  		return nil, err
   604  	}
   605  	classid := uint32(res)
   606  	ln.ClassID = &classid
   607  
   608  	fileName = strings.Join([]string{"net_prio", "ifpriomap"}, ".")
   609  	filePath = filepath.Join(cg.MountPath, "net_prio", cgPath, fileName)
   610  	if !filepath.IsAbs(cgPath) {
   611  		subPath, err := GetSubsystemPath(pid, "net_prio")
   612  		if err != nil {
   613  			return nil, err
   614  		}
   615  		if !strings.Contains(subPath, cgPath) {
   616  			return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "net_prio")
   617  		}
   618  		filePath = filepath.Join(cg.MountPath, "net_prio", subPath, fileName)
   619  	}
   620  	contents, err = ioutil.ReadFile(filePath)
   621  	if err != nil {
   622  		return nil, err
   623  	}
   624  	parts := strings.Split(strings.TrimSpace(string(contents)), "\n")
   625  	for _, part := range parts {
   626  		elem := strings.Split(part, " ")
   627  		res, err := strconv.ParseUint(elem[1], 10, 64)
   628  		if err != nil {
   629  			return nil, err
   630  		}
   631  		lip := rspec.LinuxInterfacePriority{}
   632  		lip.Name = elem[0]
   633  		lip.Priority = uint32(res)
   634  		ln.Priorities = append(ln.Priorities, lip)
   635  	}
   636  
   637  	return ln, nil
   638  }
   639  
   640  // GetPidsData gets cgroup pids data
   641  func (cg *CgroupV1) GetPidsData(pid int, cgPath string) (*rspec.LinuxPids, error) {
   642  	if filepath.IsAbs(cgPath) {
   643  		path := filepath.Join(cg.MountPath, "pids", cgPath)
   644  		if _, err := os.Stat(path); err != nil {
   645  			if os.IsNotExist(err) {
   646  				return nil, specerror.NewError(specerror.CgroupsAbsPathRelToMount, fmt.Errorf("In the case of an absolute path, the runtime MUST take the path to be relative to the cgroups mount point"), rspec.Version)
   647  			}
   648  			return nil, err
   649  		}
   650  	}
   651  	lp := &rspec.LinuxPids{}
   652  	fileName := strings.Join([]string{"pids", "max"}, ".")
   653  	filePath := filepath.Join(cg.MountPath, "pids", cgPath, fileName)
   654  	if !filepath.IsAbs(cgPath) {
   655  		subPath, err := GetSubsystemPath(pid, "pids")
   656  		if err != nil {
   657  			return nil, err
   658  		}
   659  		if !strings.Contains(subPath, cgPath) {
   660  			return nil, fmt.Errorf("cgroup subsystem %s is not mounted as expected", "pids")
   661  		}
   662  		filePath = filepath.Join(cg.MountPath, "pids", subPath, fileName)
   663  	}
   664  	contents, err := ioutil.ReadFile(filePath)
   665  	if err != nil {
   666  		return nil, err
   667  	}
   668  	res, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64)
   669  	if err != nil {
   670  		if os.IsNotExist(err) {
   671  			return nil, specerror.NewError(specerror.CgroupsPathAttach, fmt.Errorf("The runtime MUST consistently attach to the same place in the cgroups hierarchy given the same value of `cgroupsPath`"), rspec.Version)
   672  		}
   673  
   674  		return nil, err
   675  	}
   676  	lp.Limit = res
   677  
   678  	return lp, nil
   679  }