pkg.re/essentialkaos/ek@v12.36.0+incompatible/system/process/processes_linux.go (about) 1 // +build linux 2 3 // Package process provides methods for gathering information about active processes 4 package process 5 6 // ////////////////////////////////////////////////////////////////////////////////// // 7 // // 8 // Copyright (c) 2021 ESSENTIAL KAOS // 9 // Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> // 10 // // 11 // ////////////////////////////////////////////////////////////////////////////////// // 12 13 import ( 14 "fmt" 15 "io/ioutil" 16 "strconv" 17 "strings" 18 19 "pkg.re/essentialkaos/ek.v12/fsutil" 20 "pkg.re/essentialkaos/ek.v12/system" 21 ) 22 23 // ////////////////////////////////////////////////////////////////////////////////// // 24 25 // ProcessInfo contains basic info about process 26 type ProcessInfo struct { 27 Command string // Full command 28 User string // Username 29 PID int // PID 30 Parent int // Parent process PID 31 Childs []*ProcessInfo // Slice with child processes 32 IsThread bool // True if process is thread 33 } 34 35 // ////////////////////////////////////////////////////////////////////////////////// // 36 37 // procFS is path to procfs 38 var procFS = "/proc" 39 40 // ////////////////////////////////////////////////////////////////////////////////// // 41 42 // GetTree returns root process with all subprocesses on the system 43 func GetTree(pid ...int) (*ProcessInfo, error) { 44 root := 1 45 46 if len(pid) != 0 { 47 root = pid[0] 48 } 49 50 if !fsutil.IsExist(procFS + "/" + strconv.Itoa(root)) { 51 return nil, fmt.Errorf("Process with PID %d doesn't exist", pid) 52 } 53 54 list, err := findInfo(procFS, make(map[int]string)) 55 56 if err != nil { 57 return nil, err 58 } 59 60 if len(list) == 0 { 61 return nil, fmt.Errorf("Can't find any processes") 62 } 63 64 return processListToTree(list, root), nil 65 } 66 67 // GetList returns slice with all active processes on the system 68 func GetList() ([]*ProcessInfo, error) { 69 return findInfo(procFS, make(map[int]string)) 70 } 71 72 // ////////////////////////////////////////////////////////////////////////////////// // 73 74 func findInfo(dir string, userMap map[int]string) ([]*ProcessInfo, error) { 75 var result []*ProcessInfo 76 77 dirs := fsutil.List(dir, true, fsutil.ListingFilter{Perms: "DRX"}) 78 79 for _, pid := range dirs { 80 if !isPID(pid) { 81 continue 82 } 83 84 taskDir := dir + "/" + pid + "/task" 85 86 if fsutil.IsExist(taskDir) { 87 threads, err := findInfo(taskDir, userMap) 88 89 if err != nil { 90 return nil, err 91 } 92 93 if len(threads) == 0 { 94 continue 95 } 96 97 result = append(result, threads...) 98 99 continue 100 } 101 102 info, err := readProcessInfo(dir+"/"+pid, pid, userMap) 103 104 if err != nil { 105 return nil, err 106 } 107 108 if info == nil { 109 continue 110 } 111 112 result = append(result, info) 113 } 114 115 return result, nil 116 } 117 118 func readProcessInfo(dir, pid string, userMap map[int]string) (*ProcessInfo, error) { 119 pidInt, err := strconv.Atoi(pid) 120 121 if err != nil { 122 return nil, err 123 } 124 125 // The process had died after the moment when we have created a list of processes 126 if !fsutil.IsExist(dir + "/cmdline") { 127 return nil, nil 128 } 129 130 cmd, err := ioutil.ReadFile(dir + "/cmdline") 131 132 if err != nil { 133 return nil, err 134 } 135 136 if len(cmd) == 0 { 137 return nil, nil 138 } 139 140 uid, _, err := fsutil.GetOwner(dir) 141 142 if err != nil { 143 return nil, err 144 } 145 146 username, err := getProcessUser(uid, userMap) 147 148 if err != nil { 149 return nil, err 150 } 151 152 ppid, isThread := getProcessParent(dir, pidInt) 153 154 return &ProcessInfo{ 155 Command: formatCommand(string(cmd)), 156 User: username, 157 PID: pidInt, 158 Parent: ppid, 159 IsThread: isThread, 160 }, nil 161 } 162 163 func getProcessUser(uid int, userMap map[int]string) (string, error) { 164 if uid == 0 { 165 return "root", nil 166 } 167 168 if userMap[uid] != "" { 169 return userMap[uid], nil 170 } 171 172 user, err := system.LookupUser(strconv.Itoa(uid)) 173 174 if err != nil { 175 return "", err 176 } 177 178 userMap[uid] = user.Name 179 180 return user.Name, nil 181 } 182 183 func getProcessParent(pidDir string, pid int) (int, bool) { 184 tgid, ppid := getParentPIDs(pidDir) 185 186 if tgid != pid { 187 return tgid, true 188 } 189 190 return ppid, false 191 } 192 193 func getParentPIDs(pidDir string) (int, int) { 194 data, err := ioutil.ReadFile(pidDir + "/status") 195 196 if err != nil { 197 return -1, -1 198 } 199 200 var ppid, tgid string 201 202 for _, line := range strings.Split(string(data), "\n") { 203 if strings.HasPrefix(line, "Tgid:") { 204 tgid = strings.TrimSpace(line[5:]) 205 } 206 207 if strings.HasPrefix(line, "PPid:") { 208 ppid = strings.TrimSpace(line[5:]) 209 } 210 211 if ppid != "" && tgid != "" { 212 break 213 } 214 } 215 216 if tgid == "" || ppid == "" { 217 return -1, -1 218 } 219 220 tgidInt, tgidErr := strconv.Atoi(tgid) 221 ppidInt, ppidErr := strconv.Atoi(ppid) 222 223 if tgidErr != nil || ppidErr != nil { 224 return -1, -1 225 } 226 227 return tgidInt, ppidInt 228 } 229 230 func formatCommand(cmd string) string { 231 // Normalize delimiters 232 command := strings.Replace(cmd, "\000", " ", -1) 233 234 // Remove space on the end of command 235 command = strings.TrimSpace(command) 236 237 return command 238 } 239 240 func processListToTree(processes []*ProcessInfo, root int) *ProcessInfo { 241 var result = make(map[int]*ProcessInfo) 242 243 for _, info := range processes { 244 result[info.PID] = info 245 } 246 247 for _, process := range result { 248 if process.Parent < 0 { 249 continue 250 } 251 252 parentProcess := result[process.Parent] 253 254 if parentProcess == nil { 255 continue 256 } 257 258 parentProcess.Childs = append(parentProcess.Childs, process) 259 } 260 261 return result[root] 262 } 263 264 func isPID(pid string) bool { 265 if pid == "" { 266 return false 267 } 268 269 // Pid must start from number 270 switch pid[0] { 271 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 272 return true 273 } 274 275 return false 276 }