utils.go (3483B)
1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cgroups 18 19 import ( 20 "bufio" 21 "fmt" 22 "io" 23 "os" 24 "path/filepath" 25 "strings" 26 "sync" 27 28 "golang.org/x/sys/unix" 29 ) 30 31 var ( 32 nsOnce sync.Once 33 inUserNS bool 34 checkMode sync.Once 35 cgMode CGMode 36 ) 37 38 const unifiedMountpoint = "/sys/fs/cgroup" 39 40 // CGMode is the cgroups mode of the host system 41 type CGMode int 42 43 const ( 44 // Unavailable cgroup mountpoint 45 Unavailable CGMode = iota 46 // Legacy cgroups v1 47 Legacy 48 // Hybrid with cgroups v1 and v2 controllers mounted 49 Hybrid 50 // Unified with only cgroups v2 mounted 51 Unified 52 ) 53 54 // Mode returns the cgroups mode running on the host 55 func Mode() CGMode { 56 checkMode.Do(func() { 57 var st unix.Statfs_t 58 if err := unix.Statfs(unifiedMountpoint, &st); err != nil { 59 cgMode = Unavailable 60 return 61 } 62 switch st.Type { 63 case unix.CGROUP2_SUPER_MAGIC: 64 cgMode = Unified 65 default: 66 cgMode = Legacy 67 if err := unix.Statfs(filepath.Join(unifiedMountpoint, "unified"), &st); err != nil { 68 return 69 } 70 if st.Type == unix.CGROUP2_SUPER_MAGIC { 71 cgMode = Hybrid 72 } 73 } 74 }) 75 return cgMode 76 } 77 78 // RunningInUserNS detects whether we are currently running in a user namespace. 79 // Copied from github.com/lxc/lxd/shared/util.go 80 func RunningInUserNS() bool { 81 nsOnce.Do(func() { 82 file, err := os.Open("/proc/self/uid_map") 83 if err != nil { 84 // This kernel-provided file only exists if user namespaces are supported 85 return 86 } 87 defer file.Close() 88 89 buf := bufio.NewReader(file) 90 l, _, err := buf.ReadLine() 91 if err != nil { 92 return 93 } 94 95 line := string(l) 96 var a, b, c int64 97 fmt.Sscanf(line, "%d %d %d", &a, &b, &c) 98 99 /* 100 * We assume we are in the initial user namespace if we have a full 101 * range - 4294967295 uids starting at uid 0. 102 */ 103 if a == 0 && b == 0 && c == 4294967295 { 104 return 105 } 106 inUserNS = true 107 }) 108 return inUserNS 109 } 110 111 // ParseCgroupFileUnified returns legacy subsystem paths as the first value, 112 // and returns the unified path as the second value. 113 func ParseCgroupFileUnified(path string) (map[string]string, string, error) { 114 f, err := os.Open(path) 115 if err != nil { 116 return nil, "", err 117 } 118 defer f.Close() 119 return ParseCgroupFromReaderUnified(f) 120 } 121 122 // ParseCgroupFromReaderUnified returns legacy subsystem paths as the first value, 123 // and returns the unified path as the second value. 124 func ParseCgroupFromReaderUnified(r io.Reader) (map[string]string, string, error) { 125 var ( 126 cgroups = make(map[string]string) 127 unified = "" 128 s = bufio.NewScanner(r) 129 ) 130 for s.Scan() { 131 var ( 132 text = s.Text() 133 parts = strings.SplitN(text, ":", 3) 134 ) 135 if len(parts) < 3 { 136 return nil, unified, fmt.Errorf("invalid cgroup entry: %q", text) 137 } 138 for _, subs := range strings.Split(parts[1], ",") { 139 if subs == "" { 140 unified = parts[2] 141 } else { 142 cgroups[subs] = parts[2] 143 } 144 } 145 } 146 if err := s.Err(); err != nil { 147 return nil, unified, err 148 } 149 return cgroups, unified, nil 150 }