...

Source file src/github.com/prometheus/procfs/proc.go

Documentation: github.com/prometheus/procfs

     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  	"errors"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"strconv"
    23  	"strings"
    24  
    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
    34  }
    35  
    36  // Procs represents a list of Proc structs.
    37  type Procs []Proc
    38  
    39  var (
    40  	ErrFileParse  = errors.New("Error Parsing File")
    41  	ErrFileRead   = errors.New("Error Reading File")
    42  	ErrMountPoint = errors.New("Error Accessing Mount point")
    43  )
    44  
    45  func (p Procs) Len() int           { return len(p) }
    46  func (p Procs) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    47  func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
    48  
    49  // Self returns a process for the current process read via /proc/self.
    50  func Self() (Proc, error) {
    51  	fs, err := NewFS(DefaultMountPoint)
    52  	if err != nil || errors.Unwrap(err) == ErrMountPoint {
    53  		return Proc{}, err
    54  	}
    55  	return fs.Self()
    56  }
    57  
    58  // NewProc returns a process for the given pid under /proc.
    59  func NewProc(pid int) (Proc, error) {
    60  	fs, err := NewFS(DefaultMountPoint)
    61  	if err != nil {
    62  		return Proc{}, err
    63  	}
    64  	return fs.Proc(pid)
    65  }
    66  
    67  // AllProcs returns a list of all currently available processes under /proc.
    68  func AllProcs() (Procs, error) {
    69  	fs, err := NewFS(DefaultMountPoint)
    70  	if err != nil {
    71  		return Procs{}, err
    72  	}
    73  	return fs.AllProcs()
    74  }
    75  
    76  // Self returns a process for the current process.
    77  func (fs FS) Self() (Proc, error) {
    78  	p, err := os.Readlink(fs.proc.Path("self"))
    79  	if err != nil {
    80  		return Proc{}, err
    81  	}
    82  	pid, err := strconv.Atoi(strings.Replace(p, string(fs.proc), "", -1))
    83  	if err != nil {
    84  		return Proc{}, err
    85  	}
    86  	return fs.Proc(pid)
    87  }
    88  
    89  // NewProc returns a process for the given pid.
    90  //
    91  // Deprecated: Use fs.Proc() instead.
    92  func (fs FS) NewProc(pid int) (Proc, error) {
    93  	return fs.Proc(pid)
    94  }
    95  
    96  // Proc returns a process for the given pid.
    97  func (fs FS) Proc(pid int) (Proc, error) {
    98  	if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
    99  		return Proc{}, err
   100  	}
   101  	return Proc{PID: pid, fs: fs}, nil
   102  }
   103  
   104  // AllProcs returns a list of all currently available processes.
   105  func (fs FS) AllProcs() (Procs, error) {
   106  	d, err := os.Open(fs.proc.Path())
   107  	if err != nil {
   108  		return Procs{}, err
   109  	}
   110  	defer d.Close()
   111  
   112  	names, err := d.Readdirnames(-1)
   113  	if err != nil {
   114  		return Procs{}, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err)
   115  	}
   116  
   117  	p := Procs{}
   118  	for _, n := range names {
   119  		pid, err := strconv.ParseInt(n, 10, 64)
   120  		if err != nil {
   121  			continue
   122  		}
   123  		p = append(p, Proc{PID: int(pid), fs: fs})
   124  	}
   125  
   126  	return p, nil
   127  }
   128  
   129  // CmdLine returns the command line of a process.
   130  func (p Proc) CmdLine() ([]string, error) {
   131  	data, err := util.ReadFileNoStat(p.path("cmdline"))
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	if len(data) < 1 {
   137  		return []string{}, nil
   138  	}
   139  
   140  	return strings.Split(string(bytes.TrimRight(data, string("\x00"))), string(byte(0))), nil
   141  }
   142  
   143  // Wchan returns the wchan (wait channel) of a process.
   144  func (p Proc) Wchan() (string, error) {
   145  	f, err := os.Open(p.path("wchan"))
   146  	if err != nil {
   147  		return "", err
   148  	}
   149  	defer f.Close()
   150  
   151  	data, err := io.ReadAll(f)
   152  	if err != nil {
   153  		return "", err
   154  	}
   155  
   156  	wchan := string(data)
   157  	if wchan == "" || wchan == "0" {
   158  		return "", nil
   159  	}
   160  
   161  	return wchan, nil
   162  }
   163  
   164  // Comm returns the command name of a process.
   165  func (p Proc) Comm() (string, error) {
   166  	data, err := util.ReadFileNoStat(p.path("comm"))
   167  	if err != nil {
   168  		return "", err
   169  	}
   170  
   171  	return strings.TrimSpace(string(data)), nil
   172  }
   173  
   174  // Executable returns the absolute path of the executable command of a process.
   175  func (p Proc) Executable() (string, error) {
   176  	exe, err := os.Readlink(p.path("exe"))
   177  	if os.IsNotExist(err) {
   178  		return "", nil
   179  	}
   180  
   181  	return exe, err
   182  }
   183  
   184  // Cwd returns the absolute path to the current working directory of the process.
   185  func (p Proc) Cwd() (string, error) {
   186  	wd, err := os.Readlink(p.path("cwd"))
   187  	if os.IsNotExist(err) {
   188  		return "", nil
   189  	}
   190  
   191  	return wd, err
   192  }
   193  
   194  // RootDir returns the absolute path to the process's root directory (as set by chroot).
   195  func (p Proc) RootDir() (string, error) {
   196  	rdir, err := os.Readlink(p.path("root"))
   197  	if os.IsNotExist(err) {
   198  		return "", nil
   199  	}
   200  
   201  	return rdir, err
   202  }
   203  
   204  // FileDescriptors returns the currently open file descriptors of a process.
   205  func (p Proc) FileDescriptors() ([]uintptr, error) {
   206  	names, err := p.fileDescriptors()
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	fds := make([]uintptr, len(names))
   212  	for i, n := range names {
   213  		fd, err := strconv.ParseInt(n, 10, 32)
   214  		if err != nil {
   215  			return nil, fmt.Errorf("%s: Cannot parse line: %v: %w", ErrFileParse, i, err)
   216  		}
   217  		fds[i] = uintptr(fd)
   218  	}
   219  
   220  	return fds, nil
   221  }
   222  
   223  // FileDescriptorTargets returns the targets of all file descriptors of a process.
   224  // If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
   225  func (p Proc) FileDescriptorTargets() ([]string, error) {
   226  	names, err := p.fileDescriptors()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	targets := make([]string, len(names))
   232  
   233  	for i, name := range names {
   234  		target, err := os.Readlink(p.path("fd", name))
   235  		if err == nil {
   236  			targets[i] = target
   237  		}
   238  	}
   239  
   240  	return targets, nil
   241  }
   242  
   243  // FileDescriptorsLen returns the number of currently open file descriptors of
   244  // a process.
   245  func (p Proc) FileDescriptorsLen() (int, error) {
   246  	// Use fast path if available (Linux v6.2): https://github.com/torvalds/linux/commit/f1f1f2569901
   247  	if p.fs.isReal {
   248  		stat, err := os.Stat(p.path("fd"))
   249  		if err != nil {
   250  			return 0, err
   251  		}
   252  
   253  		size := stat.Size()
   254  		if size > 0 {
   255  			return int(size), nil
   256  		}
   257  	}
   258  
   259  	fds, err := p.fileDescriptors()
   260  	if err != nil {
   261  		return 0, err
   262  	}
   263  
   264  	return len(fds), nil
   265  }
   266  
   267  // MountStats retrieves statistics and configuration for mount points in a
   268  // process's namespace.
   269  func (p Proc) MountStats() ([]*Mount, error) {
   270  	f, err := os.Open(p.path("mountstats"))
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	defer f.Close()
   275  
   276  	return parseMountStats(f)
   277  }
   278  
   279  // MountInfo retrieves mount information for mount points in a
   280  // process's namespace.
   281  // It supplies information missing in `/proc/self/mounts` and
   282  // fixes various other problems with that file too.
   283  func (p Proc) MountInfo() ([]*MountInfo, error) {
   284  	data, err := util.ReadFileNoStat(p.path("mountinfo"))
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	return parseMountInfo(data)
   289  }
   290  
   291  func (p Proc) fileDescriptors() ([]string, error) {
   292  	d, err := os.Open(p.path("fd"))
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  	defer d.Close()
   297  
   298  	names, err := d.Readdirnames(-1)
   299  	if err != nil {
   300  		return nil, fmt.Errorf("%s: Cannot read file: %v: %w", ErrFileRead, names, err)
   301  	}
   302  
   303  	return names, nil
   304  }
   305  
   306  func (p Proc) path(pa ...string) string {
   307  	return p.fs.proc.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
   308  }
   309  
   310  // FileDescriptorsInfo retrieves information about all file descriptors of
   311  // the process.
   312  func (p Proc) FileDescriptorsInfo() (ProcFDInfos, error) {
   313  	names, err := p.fileDescriptors()
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  
   318  	var fdinfos ProcFDInfos
   319  
   320  	for _, n := range names {
   321  		fdinfo, err := p.FDInfo(n)
   322  		if err != nil {
   323  			continue
   324  		}
   325  		fdinfos = append(fdinfos, *fdinfo)
   326  	}
   327  
   328  	return fdinfos, nil
   329  }
   330  
   331  // Schedstat returns task scheduling information for the process.
   332  func (p Proc) Schedstat() (ProcSchedstat, error) {
   333  	contents, err := os.ReadFile(p.path("schedstat"))
   334  	if err != nil {
   335  		return ProcSchedstat{}, err
   336  	}
   337  	return parseProcSchedstat(string(contents))
   338  }
   339  

View as plain text