...

Source file src/golang.conradwood.net/go-easyops/linux/pidstatus.go

Documentation: golang.conradwood.net/go-easyops/linux

     1  package linux
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"golang.conradwood.net/go-easyops/utils"
     7  	"io/ioutil"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  )
    12  
    13  const (
    14  	INITPID        = 1
    15  	STATUS_RUNNING = 1
    16  	STATUS_STOPPED = 2
    17  )
    18  
    19  type STATUS int
    20  
    21  type ProcessState struct {
    22  	pid             int
    23  	binary          string
    24  	err             error
    25  	parentpid       int
    26  	direct_children []*ProcessState
    27  	stat            string
    28  	cgroup          string
    29  }
    30  
    31  func AllPids() ([]*ProcessState, error) {
    32  	var res []*ProcessState
    33  	files, err := ioutil.ReadDir("/proc")
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	for _, f := range files {
    38  		pid, err := strconv.Atoi(f.Name())
    39  		if err != nil {
    40  			continue
    41  		}
    42  		pf := fmt.Sprintf("/proc/%d/", pid)
    43  		if !utils.FileExists(pf + "exe") {
    44  			continue
    45  		}
    46  		if !utils.FileExists(pf + "stat") {
    47  			continue
    48  		}
    49  		res = append(res, PidStatus(pid))
    50  	}
    51  	/*
    52  		root := PidStatus(INITPID)
    53  		res = append(res, root)
    54  		children, err := root.recursivelyGetChildrenOf()
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  		res = append(res, children...)
    59  	*/
    60  	return res, nil
    61  }
    62  func (ps *ProcessState) getChildrenOf() ([]*ProcessState, error) {
    63  	use_top := true
    64  	var res []*ProcessState
    65  	if use_top {
    66  		aps, err := AllPids()
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  		for _, ap := range aps {
    71  			if ap.ParentPid() == ps.Pid() {
    72  				res = append(res, ap)
    73  			}
    74  		}
    75  		ps.direct_children = res
    76  		return res, nil
    77  	}
    78  	pid := ps.Pid()
    79  	uts, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/task", pid))
    80  	if err != nil {
    81  		return res, nil
    82  	}
    83  	var tids []int
    84  	for _, dir := range uts {
    85  		xpid, err := strconv.Atoi(dir.Name())
    86  		if err != nil {
    87  			continue
    88  		}
    89  		cname := fmt.Sprintf("/proc/%d/task/%d/children", pid, xpid)
    90  		if _, err := os.Stat(cname); errors.Is(err, os.ErrNotExist) {
    91  			continue
    92  		}
    93  		tids = append(tids, xpid)
    94  
    95  	}
    96  	for _, tid := range tids {
    97  		chs, err := readProc(fmt.Sprintf("%d/task/%d/children", pid, tid))
    98  		if err != nil {
    99  			if errors.Is(err, os.ErrNotExist) {
   100  				continue
   101  			}
   102  			return nil, err
   103  		}
   104  		for _, ns := range strings.Split(string(chs), " ") {
   105  			if ns == "" {
   106  				continue
   107  			}
   108  			cp, err := strconv.Atoi(ns)
   109  			if err != nil {
   110  				return nil, err
   111  			}
   112  			if cp == pid {
   113  				continue
   114  			}
   115  			res = append(res, PidStatus(cp))
   116  			//		fmt.Printf("ChildPid: %d\n", cp)
   117  		}
   118  	}
   119  	ps.direct_children = res
   120  	return res, nil
   121  
   122  }
   123  func (ps *ProcessState) recursivelyGetChildrenOf() ([]*ProcessState, error) {
   124  	res, err := ps.getChildrenOf()
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	var childchilds []*ProcessState
   129  	for _, cps := range res {
   130  		chp, err := cps.recursivelyGetChildrenOf()
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  		childchilds = append(childchilds, chp...)
   135  	}
   136  	res = append(res, childchilds...)
   137  	return res, nil
   138  }
   139  
   140  func PidStatus(pid int) *ProcessState {
   141  	ps := &ProcessState{pid: pid}
   142  	b, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
   143  	if err != nil {
   144  		if ps.Status() != STATUS_RUNNING {
   145  			return ps
   146  		}
   147  		ps.fail(err)
   148  		return ps
   149  	}
   150  	ps.binary = b
   151  	st, err := readProc(fmt.Sprintf("%d/stat", pid))
   152  	if err != nil {
   153  		if ps.Status() != STATUS_RUNNING {
   154  			return ps
   155  		}
   156  		ps.fail(err)
   157  		return ps
   158  	}
   159  	ps.stat = string(st)
   160  
   161  	st, err = readProc(fmt.Sprintf("%d/cgroup", pid))
   162  	if err != nil {
   163  		ps.fail(err)
   164  		return ps
   165  	}
   166  	xs := strings.Trim(string(st), "\n")
   167  	lxs := strings.SplitN(xs, ":", 3)
   168  	if len(lxs) == 3 {
   169  		xs = lxs[2]
   170  	}
   171  	ps.cgroup = xs
   172  
   173  	return ps
   174  }
   175  func (ps *ProcessState) fail(err error) {
   176  	fmt.Printf("[go-easyops] linux error: %s\n", err)
   177  	ps.err = err
   178  }
   179  func (ps *ProcessState) Pid() int {
   180  	return ps.pid
   181  }
   182  func (ps *ProcessState) ParentPid() int {
   183  	s := ps.getStatusField(3)
   184  	x, err := strconv.Atoi(s)
   185  	if err != nil {
   186  		return 0
   187  	}
   188  	return x
   189  }
   190  
   191  func (ps *ProcessState) getStatusField(num int) string {
   192  	sx := strings.Split(ps.stat, " ")
   193  	if len(sx) <= num {
   194  		return ""
   195  	}
   196  	return sx[num]
   197  }
   198  
   199  func (ps *ProcessState) Cgroup() string {
   200  	return ps.cgroup
   201  }
   202  func (ps *ProcessState) Binary() string {
   203  	return ps.binary
   204  }
   205  func (ps *ProcessState) Status() STATUS {
   206  	_, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", ps.Pid()))
   207  	if err == nil {
   208  		return STATUS_RUNNING
   209  	}
   210  	return STATUS_STOPPED
   211  }
   212  
   213  func (ps *ProcessState) String() string {
   214  	return fmt.Sprintf("#%d (%s)", ps.Pid(), ps.Binary())
   215  }
   216  func (ps *ProcessState) Children() ([]*ProcessState, error) {
   217  	if ps.direct_children == nil {
   218  		_, err := ps.getChildrenOf()
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  	}
   223  	return ps.direct_children, nil
   224  }
   225  
   226  func (s STATUS) String() string {
   227  	if s == 1 {
   228  		return "RUNNING"
   229  	}
   230  	if s == 2 {
   231  		return "STOPPED"
   232  	}
   233  	return fmt.Sprintf("STATUS=%d", s)
   234  }
   235  

View as plain text