github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/cgroups/cgroups.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package cgroups 7 8 import ( 9 "encoding/json" 10 "fmt" 11 "path/filepath" 12 "strings" 13 14 "github.com/containerd/cgroups" 15 specs "github.com/opencontainers/runtime-spec/specs-go" 16 ) 17 18 // Manager manage container cgroup resources restriction 19 type Manager struct { 20 Path string 21 Pid int 22 cgroup cgroups.Cgroup 23 } 24 25 func readSpecFromFile(path string) (spec specs.LinuxResources, err error) { 26 conf, err := LoadConfig(path) 27 if err != nil { 28 return 29 } 30 31 // convert TOML structures to OCI JSON structures 32 data, err := json.Marshal(conf) 33 if err != nil { 34 return 35 } 36 37 if err = json.Unmarshal(data, &spec); err != nil { 38 return 39 } 40 41 return 42 } 43 44 // GetCgroupRootPath returns cgroup root path 45 func (m *Manager) GetCgroupRootPath() string { 46 if m.cgroup == nil { 47 return "" 48 } 49 50 for _, sub := range m.cgroup.Subsystems() { 51 processes, err := m.cgroup.Processes(sub.Name(), false) 52 if len(processes) == 0 || err != nil { 53 continue 54 } 55 process := processes[0] 56 cgroupPath := strings.Split(process.Path, string(sub.Name()))[0] 57 return filepath.Clean(cgroupPath) 58 } 59 60 return "" 61 } 62 63 // ApplyFromSpec applies cgroups ressources restriction from OCI specification 64 func (m *Manager) ApplyFromSpec(spec *specs.LinuxResources) (err error) { 65 var path cgroups.Path 66 67 if !filepath.IsAbs(m.Path) { 68 return fmt.Errorf("cgroup path must be an absolute path") 69 } 70 71 path = cgroups.StaticPath(m.Path) 72 73 s := spec 74 if s == nil { 75 s = &specs.LinuxResources{} 76 } 77 78 // creates cgroup 79 m.cgroup, err = cgroups.New(cgroups.V1, path, s) 80 if err != nil { 81 return err 82 } 83 84 if err := m.cgroup.Add(cgroups.Process{Pid: m.Pid}); err != nil { 85 return err 86 } 87 88 return 89 } 90 91 // ApplyFromFile applies cgroups resources restriction from TOML configuration 92 // file 93 func (m *Manager) ApplyFromFile(path string) error { 94 spec, err := readSpecFromFile(path) 95 if err != nil { 96 return err 97 } 98 return m.ApplyFromSpec(&spec) 99 } 100 101 func (m *Manager) loadFromPid() (err error) { 102 if m.Pid == 0 { 103 return fmt.Errorf("no process ID specified") 104 } 105 path := cgroups.PidPath(m.Pid) 106 m.cgroup, err = cgroups.Load(cgroups.V1, path) 107 return 108 } 109 110 // UpdateFromSpec updates cgroups resources restriction from OCI specification 111 func (m *Manager) UpdateFromSpec(spec *specs.LinuxResources) (err error) { 112 if m.cgroup == nil { 113 if err = m.loadFromPid(); err != nil { 114 return 115 } 116 } 117 err = m.cgroup.Update(spec) 118 return 119 } 120 121 // UpdateFromFile updates cgroups resources restriction from TOML configuration 122 func (m *Manager) UpdateFromFile(path string) error { 123 spec, err := readSpecFromFile(path) 124 if err != nil { 125 return err 126 } 127 return m.UpdateFromSpec(&spec) 128 } 129 130 // Remove removes ressources restriction for current managed process 131 func (m *Manager) Remove() error { 132 // deletes subgroup 133 return m.cgroup.Delete() 134 } 135 136 // Pause suspends all processes inside the container 137 func (m *Manager) Pause() error { 138 if m.cgroup == nil { 139 if err := m.loadFromPid(); err != nil { 140 return err 141 } 142 } 143 return m.cgroup.Freeze() 144 } 145 146 // Resume resumes all processes that have been previously paused 147 func (m *Manager) Resume() error { 148 if m.cgroup == nil { 149 if err := m.loadFromPid(); err != nil { 150 return err 151 } 152 } 153 return m.cgroup.Thaw() 154 }