blob: 7cf5b8acf95657a854f493a907595cc86fec295b [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// 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
14package procfs
15
16import (
17 "bytes"
18 "fmt"
19 "io/ioutil"
20 "os"
21 "strconv"
22 "strings"
23)
24
25// Proc provides information about a running process.
26type Proc struct {
27 // The process ID.
28 PID int
29
30 fs FS
31}
32
33// Procs represents a list of Proc structs.
34type Procs []Proc
35
36func (p Procs) Len() int { return len(p) }
37func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
38func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
39
40// Self returns a process for the current process read via /proc/self.
41func Self() (Proc, error) {
42 fs, err := NewFS(DefaultMountPoint)
43 if err != nil {
44 return Proc{}, err
45 }
46 return fs.Self()
47}
48
49// NewProc returns a process for the given pid under /proc.
50func NewProc(pid int) (Proc, error) {
51 fs, err := NewFS(DefaultMountPoint)
52 if err != nil {
53 return Proc{}, err
54 }
55 return fs.NewProc(pid)
56}
57
58// AllProcs returns a list of all currently available processes under /proc.
59func AllProcs() (Procs, error) {
60 fs, err := NewFS(DefaultMountPoint)
61 if err != nil {
62 return Procs{}, err
63 }
64 return fs.AllProcs()
65}
66
67// Self returns a process for the current process.
68func (fs FS) Self() (Proc, error) {
69 p, err := os.Readlink(fs.Path("self"))
70 if err != nil {
71 return Proc{}, err
72 }
73 pid, err := strconv.Atoi(strings.Replace(p, string(fs), "", -1))
74 if err != nil {
75 return Proc{}, err
76 }
77 return fs.NewProc(pid)
78}
79
80// NewProc returns a process for the given pid.
81func (fs FS) NewProc(pid int) (Proc, error) {
82 if _, err := os.Stat(fs.Path(strconv.Itoa(pid))); err != nil {
83 return Proc{}, err
84 }
85 return Proc{PID: pid, fs: fs}, nil
86}
87
88// AllProcs returns a list of all currently available processes.
89func (fs FS) AllProcs() (Procs, error) {
90 d, err := os.Open(fs.Path())
91 if err != nil {
92 return Procs{}, err
93 }
94 defer d.Close()
95
96 names, err := d.Readdirnames(-1)
97 if err != nil {
98 return Procs{}, fmt.Errorf("could not read %s: %s", d.Name(), err)
99 }
100
101 p := Procs{}
102 for _, n := range names {
103 pid, err := strconv.ParseInt(n, 10, 64)
104 if err != nil {
105 continue
106 }
107 p = append(p, Proc{PID: int(pid), fs: fs})
108 }
109
110 return p, nil
111}
112
113// CmdLine returns the command line of a process.
114func (p Proc) CmdLine() ([]string, error) {
115 f, err := os.Open(p.path("cmdline"))
116 if err != nil {
117 return nil, err
118 }
119 defer f.Close()
120
121 data, err := ioutil.ReadAll(f)
122 if err != nil {
123 return nil, err
124 }
125
126 if len(data) < 1 {
127 return []string{}, nil
128 }
129
130 return strings.Split(string(bytes.TrimRight(data, string("\x00"))), string(byte(0))), nil
131}
132
133// Comm returns the command name of a process.
134func (p Proc) Comm() (string, error) {
135 f, err := os.Open(p.path("comm"))
136 if err != nil {
137 return "", err
138 }
139 defer f.Close()
140
141 data, err := ioutil.ReadAll(f)
142 if err != nil {
143 return "", err
144 }
145
146 return strings.TrimSpace(string(data)), nil
147}
148
149// Executable returns the absolute path of the executable command of a process.
150func (p Proc) Executable() (string, error) {
151 exe, err := os.Readlink(p.path("exe"))
152 if os.IsNotExist(err) {
153 return "", nil
154 }
155
156 return exe, err
157}
158
159// FileDescriptors returns the currently open file descriptors of a process.
160func (p Proc) FileDescriptors() ([]uintptr, error) {
161 names, err := p.fileDescriptors()
162 if err != nil {
163 return nil, err
164 }
165
166 fds := make([]uintptr, len(names))
167 for i, n := range names {
168 fd, err := strconv.ParseInt(n, 10, 32)
169 if err != nil {
170 return nil, fmt.Errorf("could not parse fd %s: %s", n, err)
171 }
172 fds[i] = uintptr(fd)
173 }
174
175 return fds, nil
176}
177
178// FileDescriptorTargets returns the targets of all file descriptors of a process.
179// If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
180func (p Proc) FileDescriptorTargets() ([]string, error) {
181 names, err := p.fileDescriptors()
182 if err != nil {
183 return nil, err
184 }
185
186 targets := make([]string, len(names))
187
188 for i, name := range names {
189 target, err := os.Readlink(p.path("fd", name))
190 if err == nil {
191 targets[i] = target
192 }
193 }
194
195 return targets, nil
196}
197
198// FileDescriptorsLen returns the number of currently open file descriptors of
199// a process.
200func (p Proc) FileDescriptorsLen() (int, error) {
201 fds, err := p.fileDescriptors()
202 if err != nil {
203 return 0, err
204 }
205
206 return len(fds), nil
207}
208
209// MountStats retrieves statistics and configuration for mount points in a
210// process's namespace.
211func (p Proc) MountStats() ([]*Mount, error) {
212 f, err := os.Open(p.path("mountstats"))
213 if err != nil {
214 return nil, err
215 }
216 defer f.Close()
217
218 return parseMountStats(f)
219}
220
221func (p Proc) fileDescriptors() ([]string, error) {
222 d, err := os.Open(p.path("fd"))
223 if err != nil {
224 return nil, err
225 }
226 defer d.Close()
227
228 names, err := d.Readdirnames(-1)
229 if err != nil {
230 return nil, fmt.Errorf("could not read %s: %s", d.Name(), err)
231 }
232
233 return names, nil
234}
235
236func (p Proc) path(pa ...string) string {
237 return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
238}