agent/cgroup/cgroup.go (156 lines of code) (raw):
//+build linux
package cgroup
import (
"bufio"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
type Cgroup interface {
Set(*Config) error
Get(*Config) error
GetPath() string
}
func NewGroup(subpath string, subsystem string, pid int) (Cgroup, error) {
subsystemPath, err := GetSubsystemMountpoint(subsystem)
if err != nil {
return nil, err
}
path := filepath.Join(subsystemPath, subpath)
if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
return nil, err
}
if err := writeValue(path, "cgroup.procs", strconv.Itoa(pid)); err != nil {
return nil, err
}
if subsystem == "cpu" {
return Cgroup(&CpuGroup{path}), nil
} else if subsystem == "memory" {
return Cgroup(&MemoryGroup{path}), nil
} else {
return nil, errors.New("Invalid subsystem")
}
}
func LookupCgroupByPid(pid int, subsystem string) (Cgroup, error) {
subsystemPath, err := GetSubsystemMountpoint(subsystem)
if err != nil {
return nil, err
}
subpath, err := GetCgroupPathByPid(pid, subsystem)
if err != nil {
return nil, err
}
path := filepath.Join(subsystemPath, subpath)
var g Cgroup
switch subsystem {
case "cpu":
g = Cgroup(&CpuGroup{path})
case "memory":
g = Cgroup(&MemoryGroup{path})
default:
return nil, NewUnsupportedError(subsystem)
}
return g, nil
}
func DestroyCgroup(path string) error {
os.RemoveAll(path)
if _, err := os.Stat(path); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
func GetEnabledSubsystems() (map[string]int, error) {
cgroupsFile, err := os.Open("/proc/cgroups")
if err != nil {
return nil, err
}
defer cgroupsFile.Close()
scanner := bufio.NewScanner(cgroupsFile)
// Skip the first line. It's a comment
scanner.Scan()
cgroups := make(map[string]int)
for scanner.Scan() {
var subsystem string
var hierarchy int
var num int
var enabled int
fmt.Sscanf(scanner.Text(), "%s %d %d %d", &subsystem, &hierarchy, &num, &enabled)
if enabled == 1 {
cgroups[subsystem] = hierarchy
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("Cannot parsing /proc/cgroups: %s", err)
}
return cgroups, nil
}
func GetSubsystemMountpoint(subsystem string) (string, error) {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return "", err
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
txt := scanner.Text()
fields := strings.Split(txt, " ")
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
if opt == subsystem {
return fields[4], nil
}
}
}
if err := scanner.Err(); err != nil {
return "", err
}
return "", fmt.Errorf("Mountpoint not found: %s", subsystem)
}
func GetCgroupPathByPid(pid int, subsystem string) (string, error) {
cgroups, err := GetProcessCgroups(pid)
if err != nil {
return "", err
}
for s, p := range cgroups {
if s == subsystem {
return p, nil
}
}
return "", fmt.Errorf("Not in subsystem %s: %d", subsystem, pid)
}
func GetProcessCgroups(pid int) (map[string]string, error) {
fname := fmt.Sprintf("/proc/%d/cgroup", pid)
cgroups := make(map[string]string)
f, err := os.Open(fname)
if err != nil {
return nil, fmt.Errorf("Cannot open %s: %s", fname, err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
parts := strings.SplitN(scanner.Text(), ":", 3)
if len(parts) < 3 {
return nil, fmt.Errorf("Cannot parsing %s: unknown format", fname)
}
subsystemsParts := strings.Split(parts[1], ",")
for _, s := range subsystemsParts {
cgroups[s] = parts[2]
}
}
return cgroups, nil
}
type UnsupportedError struct {
Subsystem string
}
func (e *UnsupportedError) Error() string {
return fmt.Sprintf("Unsupported subsystem: %s", e.Subsystem)
}
func NewUnsupportedError(subsystem string) error {
return &UnsupportedError{subsystem}
}
func IsUnsupportedError(err error) bool {
if err == nil {
return false
}
_, ok := err.(*UnsupportedError)
return ok
}