gitee.com/h79/goutils@v1.22.10/common/system/process_linux.go (about) 1 //go:build linux 2 // +build linux 3 4 package system 5 6 import ( 7 "bufio" 8 "fmt" 9 "os" 10 "path/filepath" 11 "strconv" 12 "strings" 13 ) 14 15 // unixProcess is an implementation of Process that contains Unix-specific 16 // fields and information. 17 type unixProcess struct { 18 pid int32 19 ppid int32 20 binary string 21 path string 22 } 23 24 func (p *unixProcess) Pid() int32 { 25 return p.pid 26 } 27 28 func (p *unixProcess) PPid() int32 { 29 return p.ppid 30 } 31 32 func (p *unixProcess) Name() string { 33 return p.binary 34 } 35 36 func (p *unixProcess) Path() string { 37 return p.path 38 } 39 40 type processList struct { 41 processes []Process 42 } 43 44 func (p *processList) fetchPID(path string) (int, error) { 45 index := strings.LastIndex(path, "/") 46 if index < 0 || len(path) < 7 { 47 return -1, fmt.Errorf("fetch pid error, path: %v", path) 48 } 49 return strconv.Atoi(path[6:index]) 50 } 51 52 func (p *processList) read(path string) (string, int, int, error) { 53 // The status file contains the name of the process in its first line. 54 // The line looks like "Name: theProcess". 55 f, err := os.Open(path) 56 if err != nil { 57 return "", 0, 0, err 58 } 59 defer f.Close() 60 61 var pidStr string 62 var ppidStr string 63 var name string 64 scanner := bufio.NewScanner(f) 65 for scanner.Scan() { 66 // read line by line 67 line := string(scanner.Bytes()) 68 if strings.HasPrefix(line, "Name:") { 69 name = strings.Split(line, ":")[1] 70 } else if strings.HasPrefix(line, "PPid:") { 71 ppidStr = strings.Split(line, ":")[1] 72 } else if strings.HasPrefix(line, "Pid:") { 73 pidStr = strings.Split(line, ":")[1] 74 } 75 if name != "" && ppidStr != "" && pidStr != "" { 76 break 77 } 78 } 79 pid, _ := strconv.Atoi(strings.Trim(pidStr, " ")) 80 ppid, _ := strconv.Atoi(strings.Trim(ppidStr, " ")) 81 return strings.Trim(name, " "), pid, ppid, nil 82 } 83 84 func (p *processList) walk(path string, info os.FileInfo, err error) error { 85 // We just return in case of errors, as they are likely due to insufficient 86 // privileges. We shouldn't get any errors for accessing the information we 87 // are interested in. Run as root (sudo) and log the error, in case you want 88 // this information. 89 if err != nil { 90 return nil 91 } 92 93 // We are only interested in files with a path looking like /proc/<pid>/status. 94 if strings.Count(path, "/") != 3 { 95 return nil 96 } 97 // Let's extract the middle part of the path with the <pid> and 98 // convert the <pid> into an integer. Log an error if it fails. 99 pid, err := p.fetchPID(path) 100 if err != nil { 101 return err 102 } 103 if strings.HasSuffix(path, "/status") { 104 // Extract the process name from within the first line in the buffer 105 name, pid_, ppid, err := p.read(path) 106 if err != nil { 107 return err 108 } 109 if pid_ != pid { 110 return fmt.Errorf("pid not match, pid: %v, name: %v, ppid: %v", pid_, name, ppid) 111 } 112 pa, err := os.Readlink("/proc/" + strconv.Itoa(pid) + "/exe") 113 if err != nil { 114 return err 115 } 116 p.processes = append(p.processes, &unixProcess{pid: int32(pid), binary: name, ppid: int32(ppid), path: pa}) 117 } 118 return nil 119 } 120 121 func getProcesses() ([]Process, error) { 122 p := processList{} 123 err := filepath.Walk("/proc", p.walk) 124 return p.processes, err 125 }