blob: 06bed0ef4a3ad33b8cf6f928a7115398919a137f [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001// 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// Cwd returns the absolute path to the current working directory of the process.
160func (p Proc) Cwd() (string, error) {
161 wd, err := os.Readlink(p.path("cwd"))
162 if os.IsNotExist(err) {
163 return "", nil
164 }
165
166 return wd, err
167}
168
169// RootDir returns the absolute path to the process's root directory (as set by chroot)
170func (p Proc) RootDir() (string, error) {
171 rdir, err := os.Readlink(p.path("root"))
172 if os.IsNotExist(err) {
173 return "", nil
174 }
175
176 return rdir, err
177}
178
179// FileDescriptors returns the currently open file descriptors of a process.
180func (p Proc) FileDescriptors() ([]uintptr, error) {
181 names, err := p.fileDescriptors()
182 if err != nil {
183 return nil, err
184 }
185
186 fds := make([]uintptr, len(names))
187 for i, n := range names {
188 fd, err := strconv.ParseInt(n, 10, 32)
189 if err != nil {
190 return nil, fmt.Errorf("could not parse fd %s: %s", n, err)
191 }
192 fds[i] = uintptr(fd)
193 }
194
195 return fds, nil
196}
197
198// FileDescriptorTargets returns the targets of all file descriptors of a process.
199// If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
200func (p Proc) FileDescriptorTargets() ([]string, error) {
201 names, err := p.fileDescriptors()
202 if err != nil {
203 return nil, err
204 }
205
206 targets := make([]string, len(names))
207
208 for i, name := range names {
209 target, err := os.Readlink(p.path("fd", name))
210 if err == nil {
211 targets[i] = target
212 }
213 }
214
215 return targets, nil
216}
217
218// FileDescriptorsLen returns the number of currently open file descriptors of
219// a process.
220func (p Proc) FileDescriptorsLen() (int, error) {
221 fds, err := p.fileDescriptors()
222 if err != nil {
223 return 0, err
224 }
225
226 return len(fds), nil
227}
228
229// MountStats retrieves statistics and configuration for mount points in a
230// process's namespace.
231func (p Proc) MountStats() ([]*Mount, error) {
232 f, err := os.Open(p.path("mountstats"))
233 if err != nil {
234 return nil, err
235 }
236 defer f.Close()
237
238 return parseMountStats(f)
239}
240
241func (p Proc) fileDescriptors() ([]string, error) {
242 d, err := os.Open(p.path("fd"))
243 if err != nil {
244 return nil, err
245 }
246 defer d.Close()
247
248 names, err := d.Readdirnames(-1)
249 if err != nil {
250 return nil, fmt.Errorf("could not read %s: %s", d.Name(), err)
251 }
252
253 return names, nil
254}
255
256func (p Proc) path(pa ...string) string {
257 return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
258}