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
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
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