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 = "a 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 }