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 %s: %s", 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// Comm returns the command name of a process. 138func (p Proc) Comm() (string, error) { 139 data, err := util.ReadFileNoStat(p.path("comm")) 140 if err != nil { 141 return "", err 142 } 143 144 return strings.TrimSpace(string(data)), nil 145} 146 147// Executable returns the absolute path of the executable command of a process. 148func (p Proc) Executable() (string, error) { 149 exe, err := os.Readlink(p.path("exe")) 150 if os.IsNotExist(err) { 151 return "", nil 152 } 153 154 return exe, err 155} 156 157// Cwd returns the absolute path to the current working directory of the process. 158func (p Proc) Cwd() (string, error) { 159 wd, err := os.Readlink(p.path("cwd")) 160 if os.IsNotExist(err) { 161 return "", nil 162 } 163 164 return wd, err 165} 166 167// RootDir returns the absolute path to the process's root directory (as set by chroot) 168func (p Proc) RootDir() (string, error) { 169 rdir, err := os.Readlink(p.path("root")) 170 if os.IsNotExist(err) { 171 return "", nil 172 } 173 174 return rdir, err 175} 176 177// FileDescriptors returns the currently open file descriptors of a process. 178func (p Proc) FileDescriptors() ([]uintptr, error) { 179 names, err := p.fileDescriptors() 180 if err != nil { 181 return nil, err 182 } 183 184 fds := make([]uintptr, len(names)) 185 for i, n := range names { 186 fd, err := strconv.ParseInt(n, 10, 32) 187 if err != nil { 188 return nil, fmt.Errorf("could not parse fd %s: %s", n, err) 189 } 190 fds[i] = uintptr(fd) 191 } 192 193 return fds, nil 194} 195 196// FileDescriptorTargets returns the targets of all file descriptors of a process. 197// If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string. 198func (p Proc) FileDescriptorTargets() ([]string, error) { 199 names, err := p.fileDescriptors() 200 if err != nil { 201 return nil, err 202 } 203 204 targets := make([]string, len(names)) 205 206 for i, name := range names { 207 target, err := os.Readlink(p.path("fd", name)) 208 if err == nil { 209 targets[i] = target 210 } 211 } 212 213 return targets, nil 214} 215 216// FileDescriptorsLen returns the number of currently open file descriptors of 217// a process. 218func (p Proc) FileDescriptorsLen() (int, error) { 219 fds, err := p.fileDescriptors() 220 if err != nil { 221 return 0, err 222 } 223 224 return len(fds), nil 225} 226 227// MountStats retrieves statistics and configuration for mount points in a 228// process's namespace. 229func (p Proc) MountStats() ([]*Mount, error) { 230 f, err := os.Open(p.path("mountstats")) 231 if err != nil { 232 return nil, err 233 } 234 defer f.Close() 235 236 return parseMountStats(f) 237} 238 239// MountInfo retrieves mount information for mount points in a 240// process's namespace. 241// It supplies information missing in `/proc/self/mounts` and 242// fixes various other problems with that file too. 243func (p Proc) MountInfo() ([]*MountInfo, error) { 244 data, err := util.ReadFileNoStat(p.path("mountinfo")) 245 if err != nil { 246 return nil, err 247 } 248 return parseMountInfo(data) 249} 250 251func (p Proc) fileDescriptors() ([]string, error) { 252 d, err := os.Open(p.path("fd")) 253 if err != nil { 254 return nil, err 255 } 256 defer d.Close() 257 258 names, err := d.Readdirnames(-1) 259 if err != nil { 260 return nil, fmt.Errorf("could not read %s: %s", d.Name(), err) 261 } 262 263 return names, nil 264} 265 266func (p Proc) path(pa ...string) string { 267 return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...) 268} 269 270// FileDescriptorsInfo retrieves information about all file descriptors of 271// the process. 272func (p Proc) FileDescriptorsInfo() (ProcFDInfos, error) { 273 names, err := p.fileDescriptors() 274 if err != nil { 275 return nil, err 276 } 277 278 var fdinfos ProcFDInfos 279 280 for _, n := range names { 281 fdinfo, err := p.FDInfo(n) 282 if err != nil { 283 continue 284 } 285 fdinfos = append(fdinfos, *fdinfo) 286 } 287 288 return fdinfos, nil 289} 290 291// Schedstat returns task scheduling information for the process. 292func (p Proc) Schedstat() (ProcSchedstat, error) { 293 contents, err := ioutil.ReadFile(p.path("schedstat")) 294 if err != nil { 295 return ProcSchedstat{}, err 296 } 297 return parseProcSchedstat(string(contents)) 298} 299