github.com/apptainer/singularity@v3.1.1+incompatible/pkg/util/fs/proc/proc.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 proc 7 8 import ( 9 "bufio" 10 "fmt" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "syscall" 16 ) 17 18 // HasFilesystem returns whether kernel support filesystem or not 19 func HasFilesystem(fs string) (bool, error) { 20 p, err := os.Open("/proc/filesystems") 21 if err != nil { 22 return false, fmt.Errorf("can't open /proc/filesystems: %s", err) 23 } 24 defer p.Close() 25 26 suffix := "\t" + fs 27 scanner := bufio.NewScanner(p) 28 for scanner.Scan() { 29 if strings.HasSuffix(scanner.Text(), suffix) { 30 return true, nil 31 } 32 } 33 return false, nil 34 } 35 36 // ParseMountInfo parses mountinfo pointing to path and returns a map 37 // of parent mount points with associated child mount points 38 func ParseMountInfo(path string) (map[string][]string, error) { 39 mp := make(map[string][]string) 40 mountlist := make(map[string][]string) 41 42 p, err := os.Open(path) 43 if err != nil { 44 return mp, fmt.Errorf("can't open %s: %s", path, err) 45 } 46 defer p.Close() 47 48 scanner := bufio.NewScanner(p) 49 for scanner.Scan() { 50 fields := strings.Fields(scanner.Text()) 51 mountlist[fields[0]] = fields 52 } 53 for k := range mountlist { 54 if i, ok := mountlist[mountlist[k][1]]; ok { 55 if mountlist[k][4] != i[4] { 56 mp[i[4]] = append(mp[i[4]], mountlist[k][4]) 57 } 58 } 59 } 60 return mp, nil 61 } 62 63 // ParentMount parses mountinfo and return the path of parent 64 // mount point for which the provided path is mounted in 65 func ParentMount(path string) (string, error) { 66 var mountPoints []string 67 parent := "/" 68 69 resolved, err := filepath.EvalSymlinks(path) 70 if err != nil { 71 return parent, err 72 } 73 74 p, err := os.Open("/proc/self/mountinfo") 75 if err != nil { 76 return parent, fmt.Errorf("can't open /proc/self/mountinfo: %s", err) 77 } 78 defer p.Close() 79 80 scanner := bufio.NewScanner(p) 81 for scanner.Scan() { 82 fields := strings.Fields(scanner.Text()) 83 mountPoints = append(mountPoints, fields[4]) 84 } 85 86 for resolved != "/" { 87 for _, point := range mountPoints { 88 if point == resolved { 89 return point, nil 90 } 91 } 92 resolved = filepath.Dir(resolved) 93 } 94 95 return parent, nil 96 } 97 98 // ExtractPid returns a pid extracted from path of type "/proc/1" 99 func ExtractPid(path string) (pid uint, err error) { 100 n, err := fmt.Sscanf(path, "/proc/%d", &pid) 101 if n != 1 { 102 return 0, fmt.Errorf("can't extract PID from %s: %s", path, err) 103 } 104 return 105 } 106 107 // CountChilds returns the number of child processes for a given process id 108 func CountChilds(pid int) (int, error) { 109 childs := 0 110 111 parentProc := fmt.Sprintf("/proc/%d", pid) 112 if _, err := os.Stat(parentProc); os.IsNotExist(err) { 113 return 0, fmt.Errorf("pid %d doesn't exists", pid) 114 } 115 116 parentLine := fmt.Sprintf("PPid:\t%d", pid) 117 pattern := filepath.Join("/proc", "[0-9]*") 118 119 matches, _ := filepath.Glob(pattern) 120 for _, path := range matches { 121 r, err := os.Open(filepath.Join(path, "status")) 122 if err != nil { 123 continue 124 } 125 scanner := bufio.NewScanner(r) 126 for scanner.Scan() { 127 if scanner.Text() == parentLine { 128 childs++ 129 break 130 } 131 } 132 r.Close() 133 } 134 return childs, nil 135 } 136 137 // ReadIDMap reads uid_map or gid_map and returns both container ID 138 // and host ID 139 func ReadIDMap(path string) (uint32, uint32, error) { 140 r, err := os.Open(path) 141 if err != nil { 142 return 0, 0, err 143 } 144 defer r.Close() 145 146 scanner := bufio.NewScanner(r) 147 scanner.Scan() 148 fields := strings.Fields(scanner.Text()) 149 150 containerID, err := strconv.ParseUint(fields[0], 10, 32) 151 if err != nil { 152 return 0, 0, err 153 } 154 hostID, err := strconv.ParseUint(fields[1], 10, 32) 155 if err != nil { 156 return 0, 0, err 157 } 158 159 return uint32(containerID), uint32(hostID), nil 160 } 161 162 // SetOOMScoreAdj sets OOM score for process with pid 163 func SetOOMScoreAdj(pid int, score *int) error { 164 if score != nil { 165 path := fmt.Sprintf("/proc/%d/oom_score_adj", pid) 166 167 f, err := os.OpenFile(path, os.O_WRONLY, 0) 168 if err != nil { 169 return fmt.Errorf("failed to open oom_score_adj: %s", err) 170 } 171 if _, err := fmt.Fprintf(f, "%d", *score); err != nil { 172 return fmt.Errorf("failed to set oom_score_adj: %s", err) 173 } 174 175 f.Close() 176 } 177 return nil 178 } 179 180 // HasNamespace checks if host namespace and container namespace 181 // are different. 182 func HasNamespace(pid int, nstype string) (bool, error) { 183 var st1 syscall.Stat_t 184 var st2 syscall.Stat_t 185 186 has := false 187 188 processOne := fmt.Sprintf("/proc/%d/ns/%s", pid, nstype) 189 processTwo := fmt.Sprintf("/proc/self/ns/%s", nstype) 190 191 if err := syscall.Stat(processOne, &st1); err != nil { 192 if os.IsNotExist(err) { 193 return has, nil 194 } 195 return has, err 196 } 197 if err := syscall.Stat(processTwo, &st2); err != nil { 198 if os.IsNotExist(err) { 199 return has, nil 200 } 201 return has, err 202 } 203 204 if st1.Ino != st2.Ino { 205 has = true 206 } 207 208 return has, nil 209 }