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 14package procfs 15 16import ( 17 "bytes" 18 "fmt" 19 "io/ioutil" 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. 29type Proc struct { 30 // The process ID. 31 PID int 32 33 fs fs.FS 34} 35 36// Procs represents a list of Proc structs. 37type Procs []Proc 38 39func (p Procs) Len() int { return len(p) } 40func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 41func (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. 44func 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. 53func 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. 62func 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. 71func (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 86func (fs FS) NewProc(pid int) (Proc, error) { 87 return fs.Proc(pid) 88} 89 90// Proc returns a process for the given pid. 91func (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. 99func (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. 124func (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. 138func (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 := ioutil.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. 159func (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. 169func (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. 179func (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) 189func (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. 199func (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. 219func (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. 239func (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. 250func (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. 264func (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 272func (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 287func (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. 293func (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. 313func (p Proc) Schedstat() (ProcSchedstat, error) { 314 contents, err := ioutil.ReadFile(p.path("schedstat")) 315 if err != nil { 316 return ProcSchedstat{}, err 317 } 318 return parseProcSchedstat(string(contents)) 319} 320