1 | // Copyright 2018 The Prometheus Authors
|
---|
2 | // Licensed under the Apache License, Version 2.0 (the "License");
|
---|
3 | // you may not use this file except in compliance with the License.
|
---|
4 | // You may obtain a copy of the License at
|
---|
5 | //
|
---|
6 | // http://www.apache.org/licenses/LICENSE-2.0
|
---|
7 | //
|
---|
8 | // Unless required by applicable law or agreed to in writing, software
|
---|
9 | // distributed under the License is distributed on an "AS IS" BASIS,
|
---|
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
11 | // See the License for the specific language governing permissions and
|
---|
12 | // limitations under the License.
|
---|
13 |
|
---|
14 | package procfs
|
---|
15 |
|
---|
16 | import (
|
---|
17 | "bytes"
|
---|
18 | "fmt"
|
---|
19 | "io"
|
---|
20 | "os"
|
---|
21 | "strconv"
|
---|
22 | "strings"
|
---|
23 |
|
---|
24 | "github.com/prometheus/procfs/internal/fs"
|
---|
25 | "github.com/prometheus/procfs/internal/util"
|
---|
26 | )
|
---|
27 |
|
---|
28 | // Proc provides information about a running process.
|
---|
29 | type Proc struct {
|
---|
30 | // The process ID.
|
---|
31 | PID int
|
---|
32 |
|
---|
33 | fs fs.FS
|
---|
34 | }
|
---|
35 |
|
---|
36 | // Procs represents a list of Proc structs.
|
---|
37 | type Procs []Proc
|
---|
38 |
|
---|
39 | func (p Procs) Len() int { return len(p) }
|
---|
40 | func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
---|
41 | func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
|
---|
42 |
|
---|
43 | // Self returns a process for the current process read via /proc/self.
|
---|
44 | func Self() (Proc, error) {
|
---|
45 | fs, err := NewFS(DefaultMountPoint)
|
---|
46 | if err != nil {
|
---|
47 | return Proc{}, err
|
---|
48 | }
|
---|
49 | return fs.Self()
|
---|
50 | }
|
---|
51 |
|
---|
52 | // NewProc returns a process for the given pid under /proc.
|
---|
53 | func NewProc(pid int) (Proc, error) {
|
---|
54 | fs, err := NewFS(DefaultMountPoint)
|
---|
55 | if err != nil {
|
---|
56 | return Proc{}, err
|
---|
57 | }
|
---|
58 | return fs.Proc(pid)
|
---|
59 | }
|
---|
60 |
|
---|
61 | // AllProcs returns a list of all currently available processes under /proc.
|
---|
62 | func AllProcs() (Procs, error) {
|
---|
63 | fs, err := NewFS(DefaultMountPoint)
|
---|
64 | if err != nil {
|
---|
65 | return Procs{}, err
|
---|
66 | }
|
---|
67 | return fs.AllProcs()
|
---|
68 | }
|
---|
69 |
|
---|
70 | // Self returns a process for the current process.
|
---|
71 | func (fs FS) Self() (Proc, error) {
|
---|
72 | p, err := os.Readlink(fs.proc.Path("self"))
|
---|
73 | if err != nil {
|
---|
74 | return Proc{}, err
|
---|
75 | }
|
---|
76 | pid, err := strconv.Atoi(strings.Replace(p, string(fs.proc), "", -1))
|
---|
77 | if err != nil {
|
---|
78 | return Proc{}, err
|
---|
79 | }
|
---|
80 | return fs.Proc(pid)
|
---|
81 | }
|
---|
82 |
|
---|
83 | // NewProc returns a process for the given pid.
|
---|
84 | //
|
---|
85 | // Deprecated: Use fs.Proc() instead.
|
---|
86 | func (fs FS) NewProc(pid int) (Proc, error) {
|
---|
87 | return fs.Proc(pid)
|
---|
88 | }
|
---|
89 |
|
---|
90 | // Proc returns a process for the given pid.
|
---|
91 | func (fs FS) Proc(pid int) (Proc, error) {
|
---|
92 | if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
|
---|
93 | return Proc{}, err
|
---|
94 | }
|
---|
95 | return Proc{PID: pid, fs: fs.proc}, nil
|
---|
96 | }
|
---|
97 |
|
---|
98 | // AllProcs returns a list of all currently available processes.
|
---|
99 | func (fs FS) AllProcs() (Procs, error) {
|
---|
100 | d, err := os.Open(fs.proc.Path())
|
---|
101 | if err != nil {
|
---|
102 | return Procs{}, err
|
---|
103 | }
|
---|
104 | defer d.Close()
|
---|
105 |
|
---|
106 | names, err := d.Readdirnames(-1)
|
---|
107 | if err != nil {
|
---|
108 | return Procs{}, fmt.Errorf("could not read %q: %w", d.Name(), err)
|
---|
109 | }
|
---|
110 |
|
---|
111 | p := Procs{}
|
---|
112 | for _, n := range names {
|
---|
113 | pid, err := strconv.ParseInt(n, 10, 64)
|
---|
114 | if err != nil {
|
---|
115 | continue
|
---|
116 | }
|
---|
117 | p = append(p, Proc{PID: int(pid), fs: fs.proc})
|
---|
118 | }
|
---|
119 |
|
---|
120 | return p, nil
|
---|
121 | }
|
---|
122 |
|
---|
123 | // CmdLine returns the command line of a process.
|
---|
124 | func (p Proc) CmdLine() ([]string, error) {
|
---|
125 | data, err := util.ReadFileNoStat(p.path("cmdline"))
|
---|
126 | if err != nil {
|
---|
127 | return nil, err
|
---|
128 | }
|
---|
129 |
|
---|
130 | if len(data) < 1 {
|
---|
131 | return []string{}, nil
|
---|
132 | }
|
---|
133 |
|
---|
134 | return strings.Split(string(bytes.TrimRight(data, string("\x00"))), string(byte(0))), nil
|
---|
135 | }
|
---|
136 |
|
---|
137 | // Wchan returns the wchan (wait channel) of a process.
|
---|
138 | func (p Proc) Wchan() (string, error) {
|
---|
139 | f, err := os.Open(p.path("wchan"))
|
---|
140 | if err != nil {
|
---|
141 | return "", err
|
---|
142 | }
|
---|
143 | defer f.Close()
|
---|
144 |
|
---|
145 | data, err := io.ReadAll(f)
|
---|
146 | if err != nil {
|
---|
147 | return "", err
|
---|
148 | }
|
---|
149 |
|
---|
150 | wchan := string(data)
|
---|
151 | if wchan == "" || wchan == "0" {
|
---|
152 | return "", nil
|
---|
153 | }
|
---|
154 |
|
---|
155 | return wchan, nil
|
---|
156 | }
|
---|
157 |
|
---|
158 | // Comm returns the command name of a process.
|
---|
159 | func (p Proc) Comm() (string, error) {
|
---|
160 | data, err := util.ReadFileNoStat(p.path("comm"))
|
---|
161 | if err != nil {
|
---|
162 | return "", err
|
---|
163 | }
|
---|
164 |
|
---|
165 | return strings.TrimSpace(string(data)), nil
|
---|
166 | }
|
---|
167 |
|
---|
168 | // Executable returns the absolute path of the executable command of a process.
|
---|
169 | func (p Proc) Executable() (string, error) {
|
---|
170 | exe, err := os.Readlink(p.path("exe"))
|
---|
171 | if os.IsNotExist(err) {
|
---|
172 | return "", nil
|
---|
173 | }
|
---|
174 |
|
---|
175 | return exe, err
|
---|
176 | }
|
---|
177 |
|
---|
178 | // Cwd returns the absolute path to the current working directory of the process.
|
---|
179 | func (p Proc) Cwd() (string, error) {
|
---|
180 | wd, err := os.Readlink(p.path("cwd"))
|
---|
181 | if os.IsNotExist(err) {
|
---|
182 | return "", nil
|
---|
183 | }
|
---|
184 |
|
---|
185 | return wd, err
|
---|
186 | }
|
---|
187 |
|
---|
188 | // RootDir returns the absolute path to the process's root directory (as set by chroot).
|
---|
189 | func (p Proc) RootDir() (string, error) {
|
---|
190 | rdir, err := os.Readlink(p.path("root"))
|
---|
191 | if os.IsNotExist(err) {
|
---|
192 | return "", nil
|
---|
193 | }
|
---|
194 |
|
---|
195 | return rdir, err
|
---|
196 | }
|
---|
197 |
|
---|
198 | // FileDescriptors returns the currently open file descriptors of a process.
|
---|
199 | func (p Proc) FileDescriptors() ([]uintptr, error) {
|
---|
200 | names, err := p.fileDescriptors()
|
---|
201 | if err != nil {
|
---|
202 | return nil, err
|
---|
203 | }
|
---|
204 |
|
---|
205 | fds := make([]uintptr, len(names))
|
---|
206 | for i, n := range names {
|
---|
207 | fd, err := strconv.ParseInt(n, 10, 32)
|
---|
208 | if err != nil {
|
---|
209 | return nil, fmt.Errorf("could not parse fd %q: %w", n, err)
|
---|
210 | }
|
---|
211 | fds[i] = uintptr(fd)
|
---|
212 | }
|
---|
213 |
|
---|
214 | return fds, nil
|
---|
215 | }
|
---|
216 |
|
---|
217 | // FileDescriptorTargets returns the targets of all file descriptors of a process.
|
---|
218 | // If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
|
---|
219 | func (p Proc) FileDescriptorTargets() ([]string, error) {
|
---|
220 | names, err := p.fileDescriptors()
|
---|
221 | if err != nil {
|
---|
222 | return nil, err
|
---|
223 | }
|
---|
224 |
|
---|
225 | targets := make([]string, len(names))
|
---|
226 |
|
---|
227 | for i, name := range names {
|
---|
228 | target, err := os.Readlink(p.path("fd", name))
|
---|
229 | if err == nil {
|
---|
230 | targets[i] = target
|
---|
231 | }
|
---|
232 | }
|
---|
233 |
|
---|
234 | return targets, nil
|
---|
235 | }
|
---|
236 |
|
---|
237 | // FileDescriptorsLen returns the number of currently open file descriptors of
|
---|
238 | // a process.
|
---|
239 | func (p Proc) FileDescriptorsLen() (int, error) {
|
---|
240 | fds, err := p.fileDescriptors()
|
---|
241 | if err != nil {
|
---|
242 | return 0, err
|
---|
243 | }
|
---|
244 |
|
---|
245 | return len(fds), nil
|
---|
246 | }
|
---|
247 |
|
---|
248 | // MountStats retrieves statistics and configuration for mount points in a
|
---|
249 | // process's namespace.
|
---|
250 | func (p Proc) MountStats() ([]*Mount, error) {
|
---|
251 | f, err := os.Open(p.path("mountstats"))
|
---|
252 | if err != nil {
|
---|
253 | return nil, err
|
---|
254 | }
|
---|
255 | defer f.Close()
|
---|
256 |
|
---|
257 | return parseMountStats(f)
|
---|
258 | }
|
---|
259 |
|
---|
260 | // MountInfo retrieves mount information for mount points in a
|
---|
261 | // process's namespace.
|
---|
262 | // It supplies information missing in `/proc/self/mounts` and
|
---|
263 | // fixes various other problems with that file too.
|
---|
264 | func (p Proc) MountInfo() ([]*MountInfo, error) {
|
---|
265 | data, err := util.ReadFileNoStat(p.path("mountinfo"))
|
---|
266 | if err != nil {
|
---|
267 | return nil, err
|
---|
268 | }
|
---|
269 | return parseMountInfo(data)
|
---|
270 | }
|
---|
271 |
|
---|
272 | func (p Proc) fileDescriptors() ([]string, error) {
|
---|
273 | d, err := os.Open(p.path("fd"))
|
---|
274 | if err != nil {
|
---|
275 | return nil, err
|
---|
276 | }
|
---|
277 | defer d.Close()
|
---|
278 |
|
---|
279 | names, err := d.Readdirnames(-1)
|
---|
280 | if err != nil {
|
---|
281 | return nil, fmt.Errorf("could not read %q: %w", d.Name(), err)
|
---|
282 | }
|
---|
283 |
|
---|
284 | return names, nil
|
---|
285 | }
|
---|
286 |
|
---|
287 | func (p Proc) path(pa ...string) string {
|
---|
288 | return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
|
---|
289 | }
|
---|
290 |
|
---|
291 | // FileDescriptorsInfo retrieves information about all file descriptors of
|
---|
292 | // the process.
|
---|
293 | func (p Proc) FileDescriptorsInfo() (ProcFDInfos, error) {
|
---|
294 | names, err := p.fileDescriptors()
|
---|
295 | if err != nil {
|
---|
296 | return nil, err
|
---|
297 | }
|
---|
298 |
|
---|
299 | var fdinfos ProcFDInfos
|
---|
300 |
|
---|
301 | for _, n := range names {
|
---|
302 | fdinfo, err := p.FDInfo(n)
|
---|
303 | if err != nil {
|
---|
304 | continue
|
---|
305 | }
|
---|
306 | fdinfos = append(fdinfos, *fdinfo)
|
---|
307 | }
|
---|
308 |
|
---|
309 | return fdinfos, nil
|
---|
310 | }
|
---|
311 |
|
---|
312 | // Schedstat returns task scheduling information for the process.
|
---|
313 | func (p Proc) Schedstat() (ProcSchedstat, error) {
|
---|
314 | contents, err := os.ReadFile(p.path("schedstat"))
|
---|
315 | if err != nil {
|
---|
316 | return ProcSchedstat{}, err
|
---|
317 | }
|
---|
318 | return parseProcSchedstat(string(contents))
|
---|
319 | }
|
---|