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  }