automation-tests/vendor/github.com/containers/psgo/internal/proc/status.go

353 lines
11 KiB
Go

// Copyright 2018 psgo authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package proc
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
"github.com/pkg/errors"
)
// Status is a direct translation of a `/proc/[pid]/status`, wich provides much
// of the information in /proc/[pid]/stat and /proc/[pid]/statm in a format
// that's easier for humans to parse.
type Status struct {
// Name: Command run by this process.
Name string
// Umask: Process umask, expressed in octal with a leading zero; see
// umask(2). (Since Linux 4.7.)
Umask string
// State: Current state of the process. One of "R (running)", "S
// (sleeping)", "D (disk sleep)", "T (stopped)", "T (tracing stop)", "Z
// (zombie)", or "X (dead)".
State string
// Tgid: Thread group ID (i.e., Process ID).
Tgid string
// Ngid: NUMA group ID (0 if none; since Linux 3.13).
Ngid string
// Pid: Thread ID (see gettid(2)).
Pid string
// PPid: PID of parent process.
PPid string
// TracerPid: PID of process tracing this process (0 if not being traced).
TracerPid string
// Uids: Real, effective, saved set, and filesystem.
Uids []string
// Gids: Real, effective, saved set, and filesystem.
Gids []string
// FDSize: Number of file descriptor slots currently allocated.
FdSize string
// Groups: Supplementary group list.
Groups []string
// NStgid : Thread group ID (i.e., PID) in each of the PID namespaces
// of which [pid] is a member. The leftmost entry shows the value
// with respect to the PID namespace of the reading process, followed
// by the value in successively nested inner namespaces. (Since Linux
// 4.1.)
NStgid string
// NSpid: Thread ID in each of the PID namespaces of which [pid] is a
// member. The fields are ordered as for NStgid. (Since Linux 4.1.)
NSpid []string
// NSpgid: Process group ID in each of the PID namespaces of which
// [pid] is a member. The fields are ordered as for NStgid. (Since
// Linux 4.1.)
NSpgid string
// NSsid: descendant namespace session ID hierarchy Session ID in
// each of the PID names- paces of which [pid] is a member. The fields
// are ordered as for NStgid. (Since Linux 4.1.)
NSsid string
// VMPeak: Peak virtual memory size.
VMPeak string
// VMSize: Virtual memory size.
VMSize string
// VMLck: Locked memory size (see mlock(3)).
VMLCK string
// VMPin: Pinned memory size (since Linux 3.2). These are pages
// that can't be moved because something needs to directly access
// physical memory.
VMPin string
// VMHWM: Peak resident set size ("high water mark").
VMHWM string
// VMRSS: Resident set size. Note that the value here is the sum of
// RssAnon, RssFile, and RssShmem.
VMRSS string
// RssAnon: Size of resident anonymous memory. (since Linux 4.5).
RssAnon string
// RssFile: Size of resident file mappings. (since Linux 4.5).
RssFile string
// RssShmem: Size of resident shared memory (includes System V
// shared memory, mappings from tmpfs(5), and shared anonymous
// mappings). (since Linux 4.5).
RssShmem string
// VMData: Size of data segment.
VMData string
// VMStk: Size of stack segment.
VMStk string
// VMExe: Size of text segment.
VMExe string
// VMLib: Shared library code size.
VMLib string
// VMPTE: Page table entries size (since Linux 2.6.10).
VMPTE string
// VMPMD: Size of second-level page tables (since Linux 4.0).
VMPMD string
// VMSwap: Swapped-out virtual memory size by anonymous private pages;
// shmem swap usage is not included (since Linux 2.6.34).
VMSwap string
// HugetlbPages: Size of hugetlb memory portions. (since Linux 4.4).
HugetlbPages string
// Threads: Number of threads in process containing this thread.
Threads string
// SigQ: This field contains two slash-separated numbers that relate to
// queued signals for the real user ID of this process. The first of
// these is the number of currently queued signals for this real
// user ID, and the second is the resource limit on the number of
// queued signals for this process (see the description of
// RLIMIT_SIGPENDING in getr- limit(2)).
SigQ string
// SigPnd: Number of signals pending for thread and for (see pthreads(7)).
SigPnd string
// ShdPnd: Number of signals pending for process as a whole (see
// signal(7)).
ShdPnd string
// SigBlk: Mask indicating signals being blocked (see signal(7)).
SigBlk string
// SigIgn: Mask indicating signals being ignored (see signal(7)).
SigIgn string
// SigCgt: Mask indicating signals being blocked caught (see signal(7)).
SigCgt string
// CapInh: Mask of capabilities enabled in inheritable sets (see
// capabilities(7)).
CapInh string
// CapPrm: Mask of capabilities enabled in permitted sets (see
// capabilities(7)).
CapPrm string
// CapEff: Mask of capabilities enabled in effective sets (see
// capabilities(7)).
CapEff string
// CapBnd: Capability Bounding set (since Linux 2.6.26, see
// capabilities(7)).
CapBnd string
// CapAmb: Ambient capability set (since Linux 4.3, see capabilities(7)).
CapAmb string
// NoNewPrivs: Value of the no_new_privs bit (since Linux 4.10, see
// prctl(2)).
NoNewPrivs string
// Seccomp: Seccomp mode of the process (since Linux 3.8, see
// seccomp(2)). 0 means SEC- COMP_MODE_DISABLED; 1 means
// SECCOMP_MODE_STRICT; 2 means SECCOMP_MODE_FILTER. This field is
// provided only if the kernel was built with the CONFIG_SECCOMP kernel
// configu- ration option enabled.
Seccomp string
// Cpus_allowed: Mask of CPUs on which this process may run
// (since Linux 2.6.24, see cpuset(7)).
CpusAllowed string
// Cpus_allowed_list: Same as previous, but in "list format" (since
// Linux 2.6.26, see cpuset(7)).
CpusAllowedList string
// Mems_allowed: Mask of memory nodes allowed to this process
// (since Linux 2.6.24, see cpuset(7)).
MemsAllowed string
// Mems_allowed_list: Same as previous, but in "list format" (since
// Linux 2.6.26, see cpuset(7)).
MemsAllowedList string
// voluntaryCtxtSwitches: Number of voluntary context switches
// (since Linux 2.6.23).
VoluntaryCtxtSwitches string
// nonvoluntaryCtxtSwitches: Number of involuntary context switches
// (since Linux 2.6.23).
NonvoluntaryCtxtSwitches string
}
// readStatusUserNS joins the user namespace of pid and returns the content of
// /proc/pid/status as a string slice.
func readStatusUserNS(pid string) ([]string, error) {
path := fmt.Sprintf("/proc/%s/status", pid)
args := []string{"nsenter", "-U", "-t", pid, "cat", path}
c := exec.Command(args[0], args[1:]...)
output, err := c.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("error executing %q: %v", strings.Join(args, " "), err)
}
return strings.Split(string(output), "\n"), nil
}
// readStatusDefault returns the content of /proc/pid/status as a string slice.
func readStatusDefault(pid string) ([]string, error) {
path := fmt.Sprintf("/proc/%s/status", pid)
f, err := os.Open(path)
if err != nil {
return nil, err
}
lines := []string{}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, nil
}
// ParseStatus parses the /proc/$pid/status file and returns a *Status.
func ParseStatus(pid string, joinUserNS bool) (*Status, error) {
var lines []string
var err error
if joinUserNS {
lines, err = readStatusUserNS(pid)
} else {
lines, err = readStatusDefault(pid)
}
if err != nil {
return nil, err
}
return parseStatus(pid, lines)
}
// parseStatus extracts data from lines and returns a *Status.
func parseStatus(pid string, lines []string) (*Status, error) {
s := Status{}
errUnexpectedInput := fmt.Errorf("unexpected input from /proc/%s/status", pid)
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) < 2 {
continue
}
switch fields[0] {
case "Name:":
s.Name = fields[1]
case "Umask:":
s.Umask = fields[1]
case "State:":
s.State = fields[1]
case "Tgid:":
s.Tgid = fields[1]
case "Ngid:":
s.Ngid = fields[1]
case "Pid:":
s.Pid = fields[1]
case "PPid:":
s.PPid = fields[1]
case "TracerPid:":
s.TracerPid = fields[1]
case "Uid:":
if len(fields) != 5 {
return nil, errors.Wrap(errUnexpectedInput, line)
}
s.Uids = []string{fields[1], fields[2], fields[3], fields[4]}
case "Gid:":
if len(fields) != 5 {
return nil, errors.Wrap(errUnexpectedInput, line)
}
s.Gids = []string{fields[1], fields[2], fields[3], fields[4]}
case "FDSize:":
s.FdSize = fields[1]
case "Groups:":
s.Groups = fields[1:]
case "NStgid:":
s.NStgid = fields[1]
case "NSpid:":
s.NSpid = fields[1:]
case "NSpgid:":
s.NSpgid = fields[1]
case "NSsid:":
s.NSsid = fields[1]
case "VmPeak:":
s.VMPeak = fields[1]
case "VmSize:":
s.VMSize = fields[1]
case "VmLck:":
s.VMLCK = fields[1]
case "VmPin:":
s.VMPin = fields[1]
case "VmHWM:":
s.VMHWM = fields[1]
case "VmRSS:":
s.VMRSS = fields[1]
case "RssAnon:":
s.RssAnon = fields[1]
case "RssFile:":
s.RssFile = fields[1]
case "RssShmem:":
s.RssShmem = fields[1]
case "VmData:":
s.VMData = fields[1]
case "VmStk:":
s.VMStk = fields[1]
case "VmExe:":
s.VMExe = fields[1]
case "VmLib:":
s.VMLib = fields[1]
case "VmPTE:":
s.VMPTE = fields[1]
case "VmPMD:":
s.VMPMD = fields[1]
case "VmSwap:":
s.VMSwap = fields[1]
case "HugetlbPages:":
s.HugetlbPages = fields[1]
case "Threads:":
s.Threads = fields[1]
case "SigQ:":
s.SigQ = fields[1]
case "SigPnd:":
s.SigPnd = fields[1]
case "ShdPnd:":
s.ShdPnd = fields[1]
case "SigBlk:":
s.SigBlk = fields[1]
case "SigIgn:":
s.SigIgn = fields[1]
case "SigCgt:":
s.SigCgt = fields[1]
case "CapInh:":
s.CapInh = fields[1]
case "CapPrm:":
s.CapPrm = fields[1]
case "CapEff:":
s.CapEff = fields[1]
case "CapBnd:":
s.CapBnd = fields[1]
case "CapAmb:":
s.CapAmb = fields[1]
case "NoNewPrivs:":
s.NoNewPrivs = fields[1]
case "Seccomp:":
s.Seccomp = fields[1]
case "Cpus_allowed:":
s.CpusAllowed = fields[1]
case "Cpus_allowed_list:":
s.CpusAllowedList = fields[1]
case "Mems_allowed:":
s.MemsAllowed = fields[1]
case "Mems_allowed_list:":
s.MemsAllowedList = fields[1]
case "voluntary_ctxt_switches:":
s.VoluntaryCtxtSwitches = fields[1]
case "nonvoluntary_ctxt_switches:":
s.NonvoluntaryCtxtSwitches = fields[1]
}
}
return &s, nil
}