From 159f7f179bf63dee2710680a12164d7487e7e696 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Thu, 26 Jul 2018 15:26:52 +0200 Subject: vendor latest containers/psgo Signed-off-by: Valentin Rothberg Closes: #1162 Approved by: rhatdan --- vendor/github.com/containers/psgo/README.md | 40 +- .../psgo/internal/capabilities/capabilities.go | 75 +++ .../github.com/containers/psgo/internal/dev/tty.go | 113 ++++ .../containers/psgo/internal/host/host.go | 67 +++ .../containers/psgo/internal/proc/attr.go | 24 + .../containers/psgo/internal/proc/cmdline.go | 22 + .../github.com/containers/psgo/internal/proc/ns.go | 15 + .../containers/psgo/internal/proc/pids.go | 79 +++ .../containers/psgo/internal/proc/stat.go | 145 +++++ .../containers/psgo/internal/proc/status.go | 310 ++++++++++ .../containers/psgo/internal/process/process.go | 205 +++++++ .../github.com/containers/psgo/ps/capabilities.go | 71 --- vendor/github.com/containers/psgo/ps/cmdline.go | 34 -- vendor/github.com/containers/psgo/ps/ps.go | 662 --------------------- vendor/github.com/containers/psgo/ps/stat.go | 193 ------ vendor/github.com/containers/psgo/ps/status.go | 314 ---------- vendor/github.com/containers/psgo/ps/tty.go | 113 ---- vendor/github.com/containers/psgo/psgo.go | 640 ++++++++++++++++++++ 18 files changed, 1719 insertions(+), 1403 deletions(-) create mode 100644 vendor/github.com/containers/psgo/internal/capabilities/capabilities.go create mode 100644 vendor/github.com/containers/psgo/internal/dev/tty.go create mode 100644 vendor/github.com/containers/psgo/internal/host/host.go create mode 100644 vendor/github.com/containers/psgo/internal/proc/attr.go create mode 100644 vendor/github.com/containers/psgo/internal/proc/cmdline.go create mode 100644 vendor/github.com/containers/psgo/internal/proc/ns.go create mode 100644 vendor/github.com/containers/psgo/internal/proc/pids.go create mode 100644 vendor/github.com/containers/psgo/internal/proc/stat.go create mode 100644 vendor/github.com/containers/psgo/internal/proc/status.go create mode 100644 vendor/github.com/containers/psgo/internal/process/process.go delete mode 100644 vendor/github.com/containers/psgo/ps/capabilities.go delete mode 100644 vendor/github.com/containers/psgo/ps/cmdline.go delete mode 100644 vendor/github.com/containers/psgo/ps/ps.go delete mode 100644 vendor/github.com/containers/psgo/ps/stat.go delete mode 100644 vendor/github.com/containers/psgo/ps/status.go delete mode 100644 vendor/github.com/containers/psgo/ps/tty.go create mode 100644 vendor/github.com/containers/psgo/psgo.go (limited to 'vendor/github.com') diff --git a/vendor/github.com/containers/psgo/README.md b/vendor/github.com/containers/psgo/README.md index 5c4f6e150..7b779b539 100644 --- a/vendor/github.com/containers/psgo/README.md +++ b/vendor/github.com/containers/psgo/README.md @@ -1,23 +1,23 @@ -[![GoDoc](https://godoc.org/github.com/containers/psgo?status.svg)](https://godoc.org/github.com/containers/psgo/ps) [![Build Status](https://travis-ci.org/containers/psgo.svg?branch=master)](https://travis-ci.org/containers/psgo) +[![GoDoc](https://godoc.org/github.com/containers/psgo?status.svg)](https://godoc.org/github.com/containers/psgo) [![Build Status](https://travis-ci.org/containers/psgo.svg?branch=master)](https://travis-ci.org/containers/psgo) # psgo -A ps (1) AIX-format compatible golang library extended with various descriptors useful for displaying container-related data. +A ps(1) AIX-format compatible golang library extended with various descriptors useful for displaying container-related data. -The idea behind the library is to provide an easy to use way of extracting process-related data, just as ps (1) does. The problem when using ps (1) is that the ps format strings split columns with whitespaces, making the output nearly impossible to parse. It also adds some jitter as we have to fork and execute ps either in the container or filter the output afterwards, further limiting applicability. +The idea behind the library is to provide an easy to use way of extracting process-related data, just as ps(1) does. The problem when using ps(1) is that the ps format strings split columns with whitespaces, making the output nearly impossible to parse. It also adds some jitter as we have to fork and execute ps either in the container or filter the output afterwards, further limiting applicability. This library aims to make things a bit more comfortable, especially for container runtimes, as the API allows to join the mount namespace of a given process and will parse `/proc` and `/dev/` from there. The API consists of the following functions: - - `ps.ProcessInfo(format string) ([]string, error)` - - ProcessInfo returns the process information of all processes in the current mount namespace. The input format must be a comma-separated list of supported AIX format descriptors. If the input string is empty, the DefaultFormat is used. The return value is a slice of tab-separated strings, to easily use the output for column-based formatting (e.g., with the `text/tabwriter` package). + - `psgo.ProcessInfo(descriptors []string) ([][]string, error)` + - ProcessInfo returns the process information of all processes in the current mount namespace. The input descriptors must be a slice of supported AIX format descriptors in the normal form or in the code form, if supported. If the input descriptor slice is empty, the `psgo.DefaultDescriptors` are used. The return value contains the string slice of process data, one per process. - - `ps.JoinNamespaceAndProcessInfo(pid, format string) ([]string, error)` + - `psgo.JoinNamespaceAndProcessInfo(pid string, descriptors []string) ([][]string, error)` - JoinNamespaceAndProcessInfo has the same semantics as ProcessInfo but joins the mount namespace of the specified pid before extracting data from /proc. This way, we can extract the `/proc` data from a container without executing any command inside the container. - - `ps.ListDescriptors() []string` + - `psgo.ListDescriptors() []string` - ListDescriptors returns a sorted string slice of all supported AIX format descriptors in the normal form (e.g., "args,comm,user"). It can be useful in the context of bash-completion, help messages, etc. ### Listing processes -We can use the [psgo](https://github.com/containers/psgo/blob/master/psgo.go) tool from this project to test the core components of this library. First, let's build `psgo` via `make build`. The binary is now located under `./bin/psgo`. By default `psgo` displays data about all running processes in the current mount namespace, similar to the output of `ps -ef`. +We can use the [psgo](https://github.com/containers/psgo/blob/master/sample/sample.go) sample tool from this project to test the core components of this library. First, let's build `psgo` via `make build`. The binary is now located under `./bin/psgo`. By default `psgo` displays data about all running processes in the current mount namespace, similar to the output of `ps -ef`. ``` $ ./bin/psgo | head -n5 @@ -46,18 +46,26 @@ root 1 0 0.000 17.249905587s ? 0s sleep ### Format descriptors The ps library is compatible with all AIX format descriptors of the ps command-line utility (see `man 1 ps` for details) but it also supports some additional descriptors that can be useful when seeking specific process-related information. +- **capbnd** + - Set of bounding capabilities. See capabilities(7) for more information. +- **capeff** + - Set of effective capabilities. See capabilities(7) for more information. - **capinh** - - Set of inheritable capabilities. See capabilities (7) for more information. + - Set of inheritable capabilities. See capabilities(7) for more information. - **capprm** - - Set of permitted capabilities. See capabilities (7) for more information. -- **capeff** - - Set of effective capabilities. See capabilities (7) for more information. -- **capbnd** - - Set of bounding capabilities. See capabilities (7) for more information. -- **seccomp** - - Seccomp mode of the process (i.e., disabled, strict or filter). See seccomp (2) for more information. + - Set of permitted capabilities. See capabilities(7) for more information. +- **hgroup** + - The corresponding effective group of a container process on the host. +- **hpid** + - The corresponding host PID of a container process. +- **huser** + - The corresponding effective user of a container process on the host. - **label** - Current security attributes of the process. +- **seccomp** + - Seccomp mode of the process (i.e., disabled, strict or filter). See seccomp(2) for more information. +- **state** + - Process state codes (e.g, **R** for *running*, **S** for *sleeping*). See proc(5) for more information. We can try out different format descriptors with the psgo binary: diff --git a/vendor/github.com/containers/psgo/internal/capabilities/capabilities.go b/vendor/github.com/containers/psgo/internal/capabilities/capabilities.go new file mode 100644 index 000000000..8aba94f39 --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/capabilities/capabilities.go @@ -0,0 +1,75 @@ +// Package capabilities provides a mapping from common kernel bit masks to the +// alphanumerical represenation of kernel capabilities. See capabilities(7) +// for additional information. +package capabilities + +var ( + // capabilities are a mapping from a numerical value to the textual + // representation of a given capability. A map allows to easily check + // if a given value is included or not. + // + // NOTE: this map must be maintained and kept in sync with the + // ./include/uapi/linux/capability.h kernel header. + capabilities = map[uint]string{ + 0: "CHOWN", + 1: "DAC_OVERRIDE", + 2: "DAC_READ_SEARCH", + 3: "FOWNER", + 4: "FSETID", + 5: "KILL", + 6: "SETGID", + 7: "SETUID", + 8: "SETPCAP", + 9: "LINUX_IMMUTABLE", + 10: "NET_BIND_SERVICE", + 11: "NET_BROADCAST", + 12: "NET_ADMIN", + 13: "NET_RAW", + 14: "IPC_LOCK", + 15: "IPC_OWNER", + 16: "SYS_MODULE", + 17: "SYS_RAWIO", + 18: "SYS_CHROOT", + 19: "SYS_PTRACE", + 20: "SYS_PACCT", + 21: "SYS_ADMIN", + 22: "SYS_BOOT", + 23: "SYS_NICE", + 24: "SYS_RESOURCE", + 25: "SYS_TIME", + 26: "SYS_TTY_CONFIG", + 27: "MKNOD", + 28: "LEASE", + 29: "AUDIT_WRITE", + 30: "AUDIT_CONTROL", + 31: "SETFCAP", + 32: "MAC_OVERRIDE", + 33: "MAC_ADMIN", + 34: "SYSLOG", + 35: "WAKE_ALARM", + 36: "BLOCK_SUSPEND", + 37: "AUDIT_READ", + } + + // FullCAPs represents the value of a bitmask with a full capability + // set. + FullCAPs = uint64(0x3FFFFFFFFF) +) + +// TranslateMask iterates over mask and returns a slice of corresponding +// capabilities. If a bit is out of range of known capabilities, it is set as +// "unknown" to catch potential regressions when new capabilities are added to +// the kernel. +func TranslateMask(mask uint64) []string { + caps := []string{} + for i := uint(0); i < 64; i++ { + if (mask>>i)&0x1 == 1 { + c, known := capabilities[i] + if !known { + c = "unknown" + } + caps = append(caps, c) + } + } + return caps +} diff --git a/vendor/github.com/containers/psgo/internal/dev/tty.go b/vendor/github.com/containers/psgo/internal/dev/tty.go new file mode 100644 index 000000000..37be9972d --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/dev/tty.go @@ -0,0 +1,113 @@ +package dev + +import ( + "os" + "strings" + "syscall" +) + +// TTY represents a tty including its minor and major device number and the +// path to the tty. +type TTY struct { + // Minor device number. + Minor uint64 + // Major device number. + Major uint64 + // Path to the tty device. + Path string +} + +// cache TTYs to avoid redundant lookups +var devices *[]TTY + +// FindTTY return the corresponding TTY to the ttyNr or nil of non could be +// found. +func FindTTY(ttyNr uint64) (*TTY, error) { + // (man 5 proc) The minor device number is contained in the combination + // of bits 31 to 20 and 7 to 0; the major device number is in bits 15 + // to 8. + maj := (ttyNr >> 8) & 0xFF + min := (ttyNr & 0xFF) | ((ttyNr >> 20) & 0xFFF) + + if devices == nil { + devs, err := getTTYs() + if err != nil { + return nil, err + } + devices = devs + } + + for _, t := range *devices { + if t.Minor == min && t.Major == maj { + return &t, nil + } + } + + return nil, nil +} + +// majDevNum returns the major device number of rdev (see stat_t.Rdev). +func majDevNum(rdev uint64) uint64 { + return (rdev >> 8) & 0xfff +} + +// minDevNum returns the minor device number of rdev (see stat_t.Rdev). +func minDevNum(rdev uint64) uint64 { + return (rdev & 0xff) | ((rdev >> 12) & 0xfff00) +} + +// getTTYs parses /dev for tty and pts devices. +func getTTYs() (*[]TTY, error) { + devDir, err := os.Open("/dev/") + if err != nil { + return nil, err + } + defer devDir.Close() + + devices := []string{} + devTTYs, err := devDir.Readdirnames(0) + if err != nil { + return nil, err + } + for _, d := range devTTYs { + if !strings.HasPrefix(d, "tty") { + continue + } + devices = append(devices, "/dev/"+d) + } + + devPTSDir, err := os.Open("/dev/pts/") + if err != nil { + return nil, err + } + defer devPTSDir.Close() + + devPTSs, err := devPTSDir.Readdirnames(0) + if err != nil { + return nil, err + } + for _, d := range devPTSs { + devices = append(devices, "/dev/pts/"+d) + } + + ttys := []TTY{} + for _, dev := range devices { + fi, err := os.Stat(dev) + if err != nil { + if os.IsNotExist(err) { + // catch race conditions + continue + } + return nil, err + } + s := fi.Sys().(*syscall.Stat_t) + t := TTY{ + Minor: minDevNum(s.Rdev), + Major: majDevNum(s.Rdev), + Path: dev, + } + ttys = append(ttys, t) + } + + return &ttys, nil +} diff --git a/vendor/github.com/containers/psgo/internal/host/host.go b/vendor/github.com/containers/psgo/internal/host/host.go new file mode 100644 index 000000000..7c2a48cb2 --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/host/host.go @@ -0,0 +1,67 @@ +// Package host extracts data from the host, such as the system's boot time or +// the tick rate of the system clock. +package host + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +/* +#include +*/ +import "C" + +var ( + // cache host queries to redundant calculations + clockTicks *int64 + bootTime *int64 +) + +// ClockTicks returns sysconf(SC_CLK_TCK). +func ClockTicks() int64 { + if clockTicks == nil { + ticks := int64(C.sysconf(C._SC_CLK_TCK)) + clockTicks = &ticks + } + return *clockTicks +} + +// BootTime parses /proc/uptime returns the boot time in seconds since the +// Epoch, 1970-01-01 00:00:00 +0000 (UTC). +func BootTime() (int64, error) { + if bootTime != nil { + return *bootTime, nil + } + + f, err := os.Open("/proc/stat") + if err != nil { + return 0, err + } + + btimeStr := "" + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) < 2 { + continue + } + if fields[0] == "btime" { + btimeStr = fields[1] + } + } + + if len(btimeStr) == 0 { + return 0, fmt.Errorf("couldn't extract boot time from /proc/stat") + } + + btimeSec, err := strconv.ParseInt(btimeStr, 10, 64) + if err != nil { + return 0, fmt.Errorf("error parsing boot time from /proc/stat: %s", err) + } + bootTime = &btimeSec + return btimeSec, nil +} diff --git a/vendor/github.com/containers/psgo/internal/proc/attr.go b/vendor/github.com/containers/psgo/internal/proc/attr.go new file mode 100644 index 000000000..0385ef9e6 --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/proc/attr.go @@ -0,0 +1,24 @@ +package proc + +import ( + "fmt" + "io/ioutil" + "os" + "strings" +) + +// ParseAttrCurrent returns the contents of /proc/$pid/attr/current of "?" if +// labeling is not supported on the host. +func ParseAttrCurrent(pid string) (string, error) { + data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/attr/current", pid)) + if err != nil { + _, err = os.Stat(fmt.Sprintf("/proc/%s", pid)) + if os.IsNotExist(err) { + // PID doesn't exist + return "", err + } + // PID exists but labeling seems to be unsupported + return "?", nil + } + return strings.Trim(string(data), "\n"), nil +} diff --git a/vendor/github.com/containers/psgo/internal/proc/cmdline.go b/vendor/github.com/containers/psgo/internal/proc/cmdline.go new file mode 100644 index 000000000..1899dd2dc --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/proc/cmdline.go @@ -0,0 +1,22 @@ +package proc + +import ( + "bytes" + "fmt" + "io/ioutil" +) + +// ParseCmdLine parses a /proc/$pid/cmdline file and returns a string slice. +func ParseCmdLine(pid string) ([]string, error) { + data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%s/cmdline", pid)) + if err != nil { + return nil, err + } + + cmdLine := []string{} + for _, rawCmd := range bytes.Split(data, []byte{0}) { + cmdLine = append(cmdLine, string(rawCmd)) + } + + return cmdLine, nil +} diff --git a/vendor/github.com/containers/psgo/internal/proc/ns.go b/vendor/github.com/containers/psgo/internal/proc/ns.go new file mode 100644 index 000000000..61b4b2b58 --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/proc/ns.go @@ -0,0 +1,15 @@ +package proc + +import ( + "fmt" + "os" +) + +// ParsePIDNamespace returns the content of /proc/$pid/ns/pid. +func ParsePIDNamespace(pid string) (string, error) { + pidNS, err := os.Readlink(fmt.Sprintf("/proc/%s/ns/pid", pid)) + if err != nil { + return "", err + } + return pidNS, nil +} diff --git a/vendor/github.com/containers/psgo/internal/proc/pids.go b/vendor/github.com/containers/psgo/internal/proc/pids.go new file mode 100644 index 000000000..ff1565cf1 --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/proc/pids.go @@ -0,0 +1,79 @@ +package proc + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +// GetPIDs extracts and returns all PIDs from /proc. +func GetPIDs() ([]string, error) { + procDir, err := os.Open("/proc/") + if err != nil { + return nil, err + } + defer procDir.Close() + + // extract string slice of all directories in procDir + pidDirs, err := procDir.Readdirnames(0) + if err != nil { + return nil, err + } + + pids := []string{} + for _, pidDir := range pidDirs { + _, err := strconv.Atoi(pidDir) + if err != nil { + // skip non-numerical entries (e.g., `/proc/softirqs`) + continue + } + pids = append(pids, pidDir) + } + + return pids, nil +} + +// pidCgroupPath returns the path to the pid's pids cgroup. +func pidCgroupPath(pid string) (string, error) { + f, err := os.Open(fmt.Sprintf("/proc/%s/cgroup", pid)) + if err != nil { + return "", err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := strings.Split(scanner.Text(), ":") + if len(fields) != 3 { + continue + } + if fields[1] == "pids" { + return fmt.Sprintf("/sys/fs/cgroup/pids/%s/cgroup.procs", fields[2]), nil + } + } + return "", fmt.Errorf("couldn't find pids group for PID %s", pid) +} + +// GetPIDsFromCgroup returns a strings slice of all pids listesd in pid's pids +// cgroup. +func GetPIDsFromCgroup(pid string) ([]string, error) { + cgroupPath, err := pidCgroupPath(pid) + if err != nil { + return nil, err + } + + f, err := os.Open(cgroupPath) + if err != nil { + return nil, err + } + defer f.Close() + + pids := []string{} + scanner := bufio.NewScanner(f) + for scanner.Scan() { + pids = append(pids, scanner.Text()) + } + return pids, nil +} diff --git a/vendor/github.com/containers/psgo/internal/proc/stat.go b/vendor/github.com/containers/psgo/internal/proc/stat.go new file mode 100644 index 000000000..b238b4fcf --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/proc/stat.go @@ -0,0 +1,145 @@ +package proc + +import ( + "fmt" + "io/ioutil" + "strings" +) + +// Stat is a direct translation of a `/proc/[pid]/stat` file as described in +// the proc(5) manpage. Please note that it is not a full translation as not +// all fields are in the scope of this library and higher indices are +// Kernel-version dependent. +type Stat struct { + // (1) The process ID + Pid string + // (2) The filename of the executable, in parentheses. This is visible + // whether or not the executable is swapped out. + Comm string + // (3) The process state (e.g., running, sleeping, zombie, dead). + // Refer to proc(5) for further deatils. + State string + // (4) The PID of the parent of this process. + Ppid string + // (5) The process group ID of the process. + Pgrp string + // (6) The session ID of the process. + Session string + // (7) The controlling terminal of the process. (The minor device + // number is contained in the combination of bits 31 to 20 and 7 to 0; + // the major device number is in bits 15 to 8.) + TtyNr string + // (8) The ID of the foreground process group of the controlling + // terminal of the process. + Tpgid string + // (9) The kernel flags word of the process. For bit meanings, see the + // PF_* defines in the Linux kernel source file + // include/linux/sched.h. Details depend on the kernel version. + Flags string + // (10) The number of minor faults the process has made which have not + // required loading a memory page from disk. + Minflt string + // (11) The number of minor faults that the process's waited-for + // children have made. + Cminflt string + // (12) The number of major faults the process has made which have + // required loading a memory page from disk. + Majflt string + // (13) The number of major faults that the process's waited-for + // children have made. + Cmajflt string + // (14) Amount of time that this process has been scheduled in user + // mode, measured in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). This includes guest time, guest_time + // (time spent running a virtual CPU, see below), so that applications + // that are not aware of the guest time field do not lose that time + // from their calculations. + Utime string + // (15) Amount of time that this process has been scheduled in kernel + // mode, measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). + Stime string + // (16) Amount of time that this process's waited-for children have + // been scheduled in user mode, measured in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). (See also times(2).) This includes guest + // time, cguest_time (time spent running a virtual CPU, see below). + Cutime string + // (17) Amount of time that this process's waited-for children have + // been scheduled in kernel mode, measured in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). + Cstime string + // (18) (Explanation for Linux 2.6+) For processes running a real-time + // scheduling policy (policy below; see sched_setscheduler(2)), this is + // the negated scheduling pri- ority, minus one; that is, a number + // in the range -2 to -100, corresponding to real-time priorities 1 to + // 99. For processes running under a non-real-time scheduling + // policy, this is the raw nice value (setpriority(2)) as represented + // in the kernel. The kernel stores nice values as numbers in the + // range 0 (high) to 39 (low), corresponding to the user-visible nice + // range of -20 to 19. + Priority string + // (19) The nice value (see setpriority(2)), a value in the range 19 + // (low priority) to -20 (high priority). + Nice string + // (20) Number of threads in this process (since Linux 2.6). Before + // kernel 2.6, this field was hard coded to 0 as a placeholder for an + // earlier removed field. + NumThreads string + // (21) The time in jiffies before the next SIGALRM is sent to the + // process due to an interval timer. Since kernel 2.6.17, this + // field is no longer maintained, and is hard coded as 0. + Itrealvalue string + // (22) The time the process started after system boot. In kernels + // before Linux 2.6, this value was expressed in jiffies. Since + // Linux 2.6, the value is expressed in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). + Starttime string + // (23) Virtual memory size in bytes. + Vsize string +} + +// readStat is used for mocking in unit tests. +var readStat = func(path string) ([]string, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + return strings.Fields(string(data)), nil +} + +// ParseStat parses the /proc/$pid/stat file and returns a Stat. +func ParseStat(pid string) (*Stat, error) { + fields, err := readStat(fmt.Sprintf("/proc/%s/stat", pid)) + if err != nil { + return nil, err + } + + fieldAt := func(i int) string { + return fields[i-1] + } + + return &Stat{ + Pid: fieldAt(1), + Comm: fieldAt(2), + State: fieldAt(3), + Ppid: fieldAt(4), + Pgrp: fieldAt(5), + Session: fieldAt(6), + TtyNr: fieldAt(7), + Tpgid: fieldAt(8), + Flags: fieldAt(9), + Minflt: fieldAt(10), + Cminflt: fieldAt(11), + Majflt: fieldAt(12), + Cmajflt: fieldAt(13), + Utime: fieldAt(14), + Stime: fieldAt(15), + Cutime: fieldAt(16), + Cstime: fieldAt(17), + Priority: fieldAt(18), + Nice: fieldAt(19), + NumThreads: fieldAt(20), + Itrealvalue: fieldAt(21), + Starttime: fieldAt(22), + Vsize: fieldAt(23), + }, nil +} diff --git a/vendor/github.com/containers/psgo/internal/proc/status.go b/vendor/github.com/containers/psgo/internal/proc/status.go new file mode 100644 index 000000000..b8e06dd6b --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/proc/status.go @@ -0,0 +1,310 @@ +package proc + +import ( + "bufio" + "fmt" + "os" + "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 +} + +// readStatus is used for mocking in unit tests. +var readStatus = func(path string) ([]string, error) { + 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) (*Status, error) { + path := fmt.Sprintf("/proc/%s/status", pid) + lines, err := readStatus(path) + if err != nil { + return nil, err + } + + s := Status{} + errUnexpectedInput := errors.New(fmt.Sprintf("unexpected input from %s", path)) + 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 +} diff --git a/vendor/github.com/containers/psgo/internal/process/process.go b/vendor/github.com/containers/psgo/internal/process/process.go new file mode 100644 index 000000000..b1ea076b5 --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/process/process.go @@ -0,0 +1,205 @@ +package process + +import ( + "os" + "strconv" + "time" + + "github.com/containers/psgo/internal/host" + "github.com/containers/psgo/internal/proc" + "github.com/opencontainers/runc/libcontainer/user" + "github.com/pkg/errors" +) + +// Process includes process-related from the /proc FS. +type Process struct { + // PID is the process ID. + Pid string + // Stat contains data from /proc/$pid/stat. + Stat proc.Stat + // Status containes data from /proc/$pid/status. + Status proc.Status + // CmdLine containes data from /proc/$pid/cmdline. + CmdLine []string + // Label containers data from /proc/$pid/attr/current. + Label string + // PidNS contains data from /proc/$pid/ns/pid. + PidNS string + // Huser is the effective host user of a container process. + Huser string + // Hgroup is the effective host group of a container process. + Hgroup string +} + +// LookupGID returns the textual group ID, if it can be optained, or the +// decimal representation otherwise. +func LookupGID(gid string) (string, error) { + gidNum, err := strconv.Atoi(gid) + if err != nil { + return "", errors.Wrap(err, "error parsing group ID") + } + g, err := user.LookupGid(gidNum) + if err != nil { + return gid, nil + } + return g.Name, nil +} + +// LookupUID return the textual user ID, if it can be optained, or the decimal +// representation otherwise. +func LookupUID(uid string) (string, error) { + uidNum, err := strconv.Atoi(uid) + if err != nil { + return "", errors.Wrap(err, "error parsing user ID") + } + u, err := user.LookupUid(uidNum) + if err != nil { + return uid, nil + } + return u.Name, nil +} + +// New returns a new Process with the specified pid and parses the relevant +// data from /proc and /dev. +func New(pid string) (*Process, error) { + p := Process{Pid: pid} + + if err := p.parseStat(); err != nil { + return nil, err + } + if err := p.parseStatus(); err != nil { + return nil, err + } + if err := p.parseCmdLine(); err != nil { + return nil, err + } + if err := p.parsePIDNamespace(); err != nil { + // Ignore permission errors as those occur for some pids when + // the caller has limited permissions. + if !os.IsPermission(err) { + return nil, err + } + } + if err := p.parseLabel(); err != nil { + return nil, err + } + + return &p, nil +} + +// FromPIDs creates a new Process for each pid. +func FromPIDs(pids []string) ([]*Process, error) { + processes := []*Process{} + for _, pid := range pids { + p, err := New(pid) + if err != nil { + if os.IsNotExist(err) { + // proc parsing is racy + // Let's ignore "does not exist" errors + continue + } + return nil, err + } + processes = append(processes, p) + } + return processes, nil +} + +// parseStat parses /proc/$pid/stat. +func (p *Process) parseStat() error { + s, err := proc.ParseStat(p.Pid) + if err != nil { + return err + } + p.Stat = *s + return nil +} + +// parseStatus parses /proc/$pid/status. +func (p *Process) parseStatus() error { + s, err := proc.ParseStatus(p.Pid) + if err != nil { + return err + } + p.Status = *s + return nil +} + +// parseCmdLine parses /proc/$pid/cmdline. +func (p *Process) parseCmdLine() error { + s, err := proc.ParseCmdLine(p.Pid) + if err != nil { + return err + } + p.CmdLine = s + return nil +} + +// parsePIDNamespace parses all host-related data fields. +func (p *Process) parsePIDNamespace() error { + pidNS, err := proc.ParsePIDNamespace(p.Pid) + if err != nil { + return err + } + p.PidNS = pidNS + return nil +} + +// parseLabel parses the security label. +func (p *Process) parseLabel() error { + label, err := proc.ParseAttrCurrent(p.Pid) + if err != nil { + return err + } + p.Label = label + return nil +} + +// SetHostData sets all host-related data fields. +func (p *Process) SetHostData() error { + var err error + + p.Huser, err = LookupUID(p.Status.Uids[1]) + if err != nil { + return err + } + + p.Hgroup, err = LookupGID(p.Status.Gids[1]) + if err != nil { + return err + } + + return nil +} + +// ElapsedTime returns the time.Duration since process p was created. +func (p *Process) ElapsedTime() (time.Duration, error) { + sinceBoot, err := strconv.ParseInt(p.Stat.Starttime, 10, 64) + if err != nil { + return 0, err + } + + sinceBoot = sinceBoot / host.ClockTicks() + + bootTime, err := host.BootTime() + if err != nil { + return 0, err + } + created := time.Unix(sinceBoot+bootTime, 0) + return (time.Now()).Sub(created), nil +} + +// CPUTime returns the cumlative CPU time of process p as a time.Duration. +func (p *Process) CPUTime() (time.Duration, error) { + user, err := strconv.ParseInt(p.Stat.Utime, 10, 64) + if err != nil { + return 0, err + } + system, err := strconv.ParseInt(p.Stat.Stime, 10, 64) + if err != nil { + return 0, err + } + secs := (user + system) / host.ClockTicks() + cpu := time.Unix(secs, 0) + return cpu.Sub(time.Unix(0, 0)), nil +} diff --git a/vendor/github.com/containers/psgo/ps/capabilities.go b/vendor/github.com/containers/psgo/ps/capabilities.go deleted file mode 100644 index 4301792a5..000000000 --- a/vendor/github.com/containers/psgo/ps/capabilities.go +++ /dev/null @@ -1,71 +0,0 @@ -package ps - -var ( - // capabilities are a mapping from a numerical value to the textual - // representation of a given capability. A map allows to easily check - // if a given value is included or not. - // - // NOTE: this map must be maintained and kept in sync with the - // ./include/uapi/linux/capability.h kernel header. - capabilities = map[uint]string{ - 0: "CHOWN", - 1: "DAC_OVERRIDE", - 2: "DAC_READ_SEARCH", - 3: "FOWNER", - 4: "FSETID", - 5: "KILL", - 6: "SETGID", - 7: "SETUID", - 8: "SETPCAP", - 9: "LINUX_IMMUTABLE", - 10: "NET_BIND_SERVICE", - 11: "NET_BROADCAST", - 12: "NET_ADMIN", - 13: "NET_RAW", - 14: "IPC_LOCK", - 15: "IPC_OWNER", - 16: "SYS_MODULE", - 17: "SYS_RAWIO", - 18: "SYS_CHROOT", - 19: "SYS_PTRACE", - 20: "SYS_PACCT", - 21: "SYS_ADMIN", - 22: "SYS_BOOT", - 23: "SYS_NICE", - 24: "SYS_RESOURCE", - 25: "SYS_TIME", - 26: "SYS_TTY_CONFIG", - 27: "MKNOD", - 28: "LEASE", - 29: "AUDIT_WRITE", - 30: "AUDIT_CONTROL", - 31: "SETFCAP", - 32: "MAC_OVERRIDE", - 33: "MAC_ADMIN", - 34: "SYSLOG", - 35: "WAKE_ALARM", - 36: "BLOCK_SUSPEND", - 37: "AUDIT_READ", - } - // fullCAPs represents the value of a bitmask with a full capability - // set. - fullCAPs = uint64(0x3FFFFFFFFF) -) - -// maskToCaps iterates over mask and returns a slice of corresponding -// capabilities. If a bit is out of range of known capabilities, it is set as -// "unknown" to catch potential regressions when new capabilities are added to -// the kernel. -func maskToCaps(mask uint64) []string { - caps := []string{} - for i := uint(0); i < 64; i++ { - if (mask>>i)&0x1 == 1 { - c, known := capabilities[i] - if !known { - c = "unknown" - } - caps = append(caps, c) - } - } - return caps -} diff --git a/vendor/github.com/containers/psgo/ps/cmdline.go b/vendor/github.com/containers/psgo/ps/cmdline.go deleted file mode 100644 index d9991a9a3..000000000 --- a/vendor/github.com/containers/psgo/ps/cmdline.go +++ /dev/null @@ -1,34 +0,0 @@ -package ps - -import ( - "bytes" - "io/ioutil" - "os" -) - -// readCmdline can be used for mocking in unit tests. -func readCmdline(path string) (string, error) { - data, err := ioutil.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - err = errNoSuchPID - } - return "", err - } - - return string(data), nil -} - -// parseCmdline parses a /proc/$pid/cmdline file and returns a string slice. -func parseCmdline(path string) ([]string, error) { - raw, err := readCmdline(path) - if err != nil { - return nil, err - } - - cmdLine := []string{} - for _, rawCmd := range bytes.Split([]byte(raw), []byte{0}) { - cmdLine = append(cmdLine, string(rawCmd)) - } - return cmdLine, nil -} diff --git a/vendor/github.com/containers/psgo/ps/ps.go b/vendor/github.com/containers/psgo/ps/ps.go deleted file mode 100644 index f8d8d03b5..000000000 --- a/vendor/github.com/containers/psgo/ps/ps.go +++ /dev/null @@ -1,662 +0,0 @@ -// Package ps is a ps (1) AIX-format compatible golang library extended with -// various descriptors useful for displaying container-related data. -// -// The idea behind the library is to provide an easy to use way of extracting -// process-related data, just as ps (1) does. The problem when using ps (1) is -// that the ps format strings split columns with whitespaces, making the output -// nearly impossible to parse. It also adds some jitter as we have to fork and -// execute ps either in the container or filter the output afterwards, further -// limiting applicability. -// -// Please visit https://github.com/containers/psgo for further details about -// supported format descriptors and to see some usage examples. -package ps - -import ( - "fmt" - "io/ioutil" - "os" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/opencontainers/runc/libcontainer/user" - "github.com/pkg/errors" - "golang.org/x/sys/unix" -) - -// DefaultFormat is the `ps -ef` compatible default format. -const DefaultFormat = "user,pid,ppid,pcpu,etime,tty,time,comm" - -var ( - // ErrUnkownDescriptor is returned when an unknown descriptor is parsed. - ErrUnkownDescriptor = errors.New("unknown descriptor") - - // errNoSuchPID is returned when `/proc/PID` does not exist (anymore). - errNoSuchPID = errors.New("PID does not exist in /proc") - - // bootTime holds the host's boot time. Singleton to safe some time and - // energy. - bootTime int64 - - // clockTicks is the value of sysconf(SC_CLK_TCK) - clockTicks = getClockTicks() - - // ttyDevices is a slice of ttys. Singledton to safe some time and - // energy. - ttyDevices []*tty - - descriptors = []aixFormatDescriptor{ - { - code: "%C", - normal: "pcpu", - header: "%CPU", - procFn: processPCPU, - }, - { - code: "%G", - normal: "group", - header: "GROUP", - procFn: processGROUP, - }, - { - code: "%P", - normal: "ppid", - header: "PPID", - procFn: processPPID, - }, - { - code: "%U", - normal: "user", - header: "USER", - procFn: processUSER, - }, - { - code: "%a", - normal: "args", - header: "COMMAND", - procFn: processARGS, - }, - { - code: "%c", - normal: "comm", - header: "COMMAND", - procFn: processCOMM, - }, - { - code: "%g", - normal: "rgroup", - header: "RGROUP", - procFn: processRGROUP, - }, - { - code: "%n", - normal: "nice", - header: "NI", - procFn: processNICE, - }, - { - code: "%p", - normal: "pid", - header: "PID", - procFn: processPID, - }, - { - code: "%r", - normal: "pgid", - header: "PGID", - procFn: processPGID, - }, - { - code: "%t", - normal: "etime", - header: "ELAPSED", - procFn: processETIME, - }, - { - code: "%u", - normal: "ruser", - header: "RUSER", - procFn: processRUSER, - }, - { - code: "%x", - normal: "time", - header: "TIME", - procFn: processTIME, - }, - { - code: "%y", - normal: "tty", - header: "TTY", - procFn: processTTY, - }, - { - code: "%z", - normal: "vsz", - header: "VSZ", - procFn: processVSZ, - }, - { - normal: "capinh", - header: "CAPABILITIES", - procFn: processCAPINH, - }, - { - normal: "capprm", - header: "CAPABILITIES", - procFn: processCAPPRM, - }, - { - normal: "capeff", - header: "CAPABILITIES", - procFn: processCAPEFF, - }, - { - normal: "capbnd", - header: "CAPABILITIES", - procFn: processCAPBND, - }, - { - normal: "seccomp", - header: "SECCOMP", - procFn: processSECCOMP, - }, - { - normal: "label", - header: "LABEL", - procFn: processLABEL, - }, - } -) - -// process includes a process ID and the corresponding data from /proc/pid/stat, -// /proc/pid/status and from /prod/pid/cmdline. -type process struct { - pid int - pstat *stat - pstatus *status - cmdline []string -} - -// elapsedTime returns the time.Duration since process p was created. -func (p *process) elapsedTime() (time.Duration, error) { - sinceBoot, err := strconv.ParseInt(p.pstat.starttime, 10, 64) - if err != nil { - return 0, err - } - sinceBoot = sinceBoot / clockTicks - - if bootTime == 0 { - bootTime, err = getBootTime() - if err != nil { - return 0, err - } - } - created := time.Unix(sinceBoot+bootTime, 0) - return (time.Now()).Sub(created), nil -} - -// cpuTime returns the cumlative CPU time of process p as a time.Duration. -func (p *process) cpuTime() (time.Duration, error) { - user, err := strconv.ParseInt(p.pstat.utime, 10, 64) - if err != nil { - return 0, err - } - system, err := strconv.ParseInt(p.pstat.stime, 10, 64) - if err != nil { - return 0, err - } - secs := (user + system) / clockTicks - cpu := time.Unix(secs, 0) - return cpu.Sub(time.Unix(0, 0)), nil -} - -// processes returns a process slice of processes mentioned in /proc. -func processes() ([]*process, error) { - pids, err := getPIDs() - if err != nil { - panic(err) - } - - processes := []*process{} - for _, pid := range pids { - var ( - err error - p process - ) - p.pid = pid - p.pstat, err = parseStat(fmt.Sprintf("/proc/%d/stat", pid)) - if err != nil { - if err == errNoSuchPID { - continue - } - return nil, err - } - p.pstatus, err = parseStatus(fmt.Sprintf("/proc/%d/status", pid)) - if err != nil { - if err == errNoSuchPID { - continue - } - return nil, err - } - p.cmdline, err = parseCmdline(fmt.Sprintf("/proc/%d/cmdline", pid)) - if err != nil { - if err == errNoSuchPID { - continue - } - return nil, err - } - processes = append(processes, &p) - } - - return processes, nil -} - -// getPIDs extracts and returns all PIDs from /proc. -func getPIDs() ([]int, error) { - procDir, err := os.Open("/proc/") - if err != nil { - return nil, err - } - defer procDir.Close() - - // extract string slice of all directories in procDir - pidDirs, err := procDir.Readdirnames(0) - if err != nil { - return nil, err - } - - // convert pidDirs to int - pids := []int{} - for _, pidDir := range pidDirs { - pid, err := strconv.Atoi(pidDir) - if err != nil { - // skip non-numerical entries (e.g., `/proc/softirqs`) - continue - } - pids = append(pids, pid) - } - - return pids, nil -} - -// processFunc is used to map a given aixFormatDescriptor to a corresponding -// function extracting the desired data from a process. -type processFunc func(*process) (string, error) - -// aixFormatDescriptor as mentioned in the ps(1) manpage. A given descriptor -// can either be specified via its code (e.g., "%C") or its normal representation -// (e.g., "pcpu") and will be printed under its corresponding header (e.g, "%CPU"). -type aixFormatDescriptor struct { - code string - normal string - header string - procFn processFunc -} - -// processDescriptors calls each `procFn` of all formatDescriptors on each -// process and returns an array of tab-separated strings. -func processDescriptors(formatDescriptors []aixFormatDescriptor, processes []*process) ([]string, error) { - data := []string{} - // create header - headerArr := []string{} - for _, desc := range formatDescriptors { - headerArr = append(headerArr, desc.header) - } - data = append(data, strings.Join(headerArr, "\t")) - - // dispatch all descriptor functions on each process - for _, proc := range processes { - pData := []string{} - for _, desc := range formatDescriptors { - dataStr, err := desc.procFn(proc) - if err != nil { - return nil, err - } - pData = append(pData, dataStr) - } - data = append(data, strings.Join(pData, "\t")) - } - - return data, nil -} - -// ListDescriptors returns a string slice of all supported AIX format -// descriptors in the normal form. -func ListDescriptors() (list []string) { - for _, d := range descriptors { - list = append(list, d.normal) - } - sort.Strings(list) - return -} - -// JoinNamespaceAndProcessInfo has the same semantics as ProcessInfo but joins -// the mount namespace of the specified pid before extracting data from `/proc`. -func JoinNamespaceAndProcessInfo(pid, format string) ([]string, error) { - var ( - data []string - dataErr error - wg sync.WaitGroup - ) - - wg.Add(1) - go func() { - defer wg.Done() - runtime.LockOSThread() - - fd, err := os.Open(fmt.Sprintf("/proc/%s/ns/mnt", pid)) - if err != nil { - dataErr = err - return - } - defer fd.Close() - - // create a new mountns on the current thread - if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { - dataErr = err - return - } - unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS) - data, dataErr = ProcessInfo(format) - }() - wg.Wait() - - return data, dataErr -} - -// ProcessInfo returns the process information of all processes in the current -// mount namespace. The input format must be a comma-separated list of -// supported AIX format descriptors. If the input string is empty, the -// DefaultFormat is used. -// The return value is an array of tab-separated strings, to easily use the -// output for column-based formatting (e.g., with the `text/tabwriter` package). -func ProcessInfo(format string) ([]string, error) { - if len(format) == 0 { - format = DefaultFormat - } - - formatDescriptors, err := parseDescriptors(format) - if err != nil { - return nil, err - } - - processes, err := processes() - if err != nil { - return nil, err - } - - return processDescriptors(formatDescriptors, processes) -} - -// parseDescriptors parses the input string and returns a correspodning array -// of aixFormatDescriptors, which are expected to be separated by commas. -// The input format is "desc1, desc2, ..., desN" where a given descriptor can be -// specified both, in the code and in the normal form. A concrete example is -// "pid, %C, nice, %a". -func parseDescriptors(input string) ([]aixFormatDescriptor, error) { - formatDescriptors := []aixFormatDescriptor{} - for _, s := range strings.Split(input, ",") { - s = strings.TrimSpace(s) - found := false - for _, d := range descriptors { - if s == d.code || s == d.normal { - formatDescriptors = append(formatDescriptors, d) - found = true - } - } - if !found { - return nil, errors.Wrapf(ErrUnkownDescriptor, "'%s'", s) - } - } - return formatDescriptors, nil -} - -// lookupGID returns the textual group ID, if it can be optained, or the -// decimal input representation otherwise. -func lookupGID(gid string) (string, error) { - gidNum, err := strconv.Atoi(gid) - if err != nil { - return "", errors.Wrap(err, "error parsing group ID") - } - g, err := user.LookupGid(gidNum) - if err != nil { - return gid, nil - } - return g.Name, nil -} - -// processGROUP returns the effective group ID of the process. This will be -// the textual group ID, if it can be optained, or a decimal representation -// otherwise. -func processGROUP(p *process) (string, error) { - return lookupGID(p.pstatus.gids[1]) -} - -// processRGROUP returns the real group ID of the process. This will be -// the textual group ID, if it can be optained, or a decimal representation -// otherwise. -func processRGROUP(p *process) (string, error) { - return lookupGID(p.pstatus.gids[0]) -} - -// processPPID returns the parent process ID of process p. -func processPPID(p *process) (string, error) { - return p.pstatus.pPid, nil -} - -// lookupUID return the textual user ID, if it can be optained, or the decimal -// input representation otherwise. -func lookupUID(uid string) (string, error) { - uidNum, err := strconv.Atoi(uid) - if err != nil { - return "", errors.Wrap(err, "error parsing user ID") - } - u, err := user.LookupUid(uidNum) - if err != nil { - return uid, nil - } - return u.Name, nil - -} - -// processUSER returns the effective user name of the process. This will be -// the textual group ID, if it can be optained, or a decimal representation -// otherwise. -func processUSER(p *process) (string, error) { - return lookupUID(p.pstatus.uids[1]) -} - -// processRUSER returns the effective user name of the process. This will be -// the textual group ID, if it can be optained, or a decimal representation -// otherwise. -func processRUSER(p *process) (string, error) { - return lookupUID(p.pstatus.uids[0]) -} - -// processName returns the name of process p in the format "[$name]". -func processName(p *process) (string, error) { - return fmt.Sprintf("[%s]", p.pstatus.name), nil -} - -// processARGS returns the command of p with all its arguments. -func processARGS(p *process) (string, error) { - args := p.cmdline - // ps (1) returns "[$name]" if command/args are empty - if len(args) == 0 { - return processName(p) - } - return strings.Join(args, " "), nil -} - -// processCOMM returns the command name (i.e., executable name) of process p. -func processCOMM(p *process) (string, error) { - args := p.cmdline - // ps (1) returns "[$name]" if command/args are empty - if len(args) == 0 { - return processName(p) - } - spl := strings.Split(args[0], "/") - return spl[len(spl)-1], nil -} - -// processNICE returns the nice value of process p. -func processNICE(p *process) (string, error) { - return p.pstat.nice, nil -} - -// processPID returns the process ID of process p. -func processPID(p *process) (string, error) { - return p.pstatus.pid, nil -} - -// processPGID returns the process group ID of process p. -func processPGID(p *process) (string, error) { - return p.pstat.pgrp, nil -} - -// processPCPU returns how many percent of the CPU time process p uses as -// a three digit float as string. -func processPCPU(p *process) (string, error) { - elapsed, err := p.elapsedTime() - if err != nil { - return "", err - } - cpu, err := p.cpuTime() - if err != nil { - return "", err - } - pcpu := 100 * cpu.Seconds() / elapsed.Seconds() - - return strconv.FormatFloat(pcpu, 'f', 3, 64), nil -} - -// processETIME returns the elapsed time since the process was started. -func processETIME(p *process) (string, error) { - elapsed, err := p.elapsedTime() - if err != nil { - return "", nil - } - return fmt.Sprintf("%v", elapsed), nil -} - -// processTIME returns the cumulative CPU time of process p. -func processTIME(p *process) (string, error) { - cpu, err := p.cpuTime() - if err != nil { - return "", err - } - return fmt.Sprintf("%v", cpu), nil -} - -// processTTY returns the controlling tty (terminal) of process p. -func processTTY(p *process) (string, error) { - ttyNr, err := strconv.ParseUint(p.pstat.ttyNr, 10, 64) - if err != nil { - return "", nil - } - - maj, min := ttyNrToDev(ttyNr) - t, err := findTTY(maj, min) - if err != nil { - return "", err - } - - ttyS := "?" - if t != nil { - ttyS = strings.TrimPrefix(t.device, "/dev/") - } - return ttyS, nil -} - -// processVSZ returns the virtual memory size of process p in KiB (1024-byte -// units). -func processVSZ(p *process) (string, error) { - vmsize, err := strconv.Atoi(p.pstat.vsize) - if err != nil { - return "", err - } - return fmt.Sprintf("%d", vmsize/1024), nil -} - -// parseCAP parses cap (a string bit mask) and returns the associated set of -// capabilities. If all capabilties are set, "full" is returned. If no -// capability is enabled, "none" is returned. -func parseCAP(cap string) (string, error) { - mask, err := strconv.ParseUint(cap, 16, 64) - if err != nil { - return "", err - } - if mask == fullCAPs { - return "full", nil - } - caps := maskToCaps(mask) - if len(caps) == 0 { - return "none", nil - } - sort.Strings(caps) - return strings.Join(caps, ","), nil -} - -// processCAPINH returns the set of inheritable capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no -// capability is enabled, "none" is returned. -func processCAPINH(p *process) (string, error) { - return parseCAP(p.pstatus.capInh) -} - -// processCAPPRM returns the set of permitted capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no -// capability is enabled, "none" is returned. -func processCAPPRM(p *process) (string, error) { - return parseCAP(p.pstatus.capPrm) -} - -// processCAPEFF returns the set of effective capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no -// capability is enabled, "none" is returned. -func processCAPEFF(p *process) (string, error) { - return parseCAP(p.pstatus.capEff) -} - -// processCAPBND returns the set of bounding capabilties associated with -// process p. If all capabilties are set, "full" is returned. If no -// capability is enabled, "none" is returned. -func processCAPBND(p *process) (string, error) { - return parseCAP(p.pstatus.capBnd) -} - -// processSECCOMP returns the seccomp mode of the process (i.e., disabled, -// strict or filter) or "?" if /proc/$pid/status.seccomp has a unknown value. -func processSECCOMP(p *process) (string, error) { - switch p.pstatus.seccomp { - case "0": - return "disabled", nil - case "1": - return "strict", nil - case "2": - return "filter", nil - default: - return "?", nil - } -} - -// processLABEL returns the process label of process p. -func processLABEL(p *process) (string, error) { - data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/attr/current", p.pid)) - if err != nil { - if os.IsNotExist(err) { - // make sure the pid does not exist, - // could be system does not support labeling. - if _, err2 := os.Stat(fmt.Sprintf("/proc/%d", p.pid)); err2 != nil { - return "", errNoSuchPID - } - } - return "", err - } - return strings.Trim(string(data), "\x00"), nil -} diff --git a/vendor/github.com/containers/psgo/ps/stat.go b/vendor/github.com/containers/psgo/ps/stat.go deleted file mode 100644 index 0488d4616..000000000 --- a/vendor/github.com/containers/psgo/ps/stat.go +++ /dev/null @@ -1,193 +0,0 @@ -package ps - -import ( - "bufio" - "fmt" - "io/ioutil" - "os" - "strconv" - "strings" -) - -/* -#include -*/ -import "C" - -// getClockTicks returns sysconf(SC_CLK_TCK). -func getClockTicks() int64 { - return int64(C.sysconf(C._SC_CLK_TCK)) -} - -// bootTime parses /proc/uptime returns the time.Time of system boot. -func getBootTime() (int64, error) { - f, err := os.Open("/proc/stat") - if err != nil { - return 0, err - } - - btimeStr := "" - scanner := bufio.NewScanner(f) - for scanner.Scan() { - fields := strings.Fields(scanner.Text()) - if len(fields) < 2 { - continue - } - if fields[0] == "btime" { - btimeStr = fields[1] - } - } - - if len(btimeStr) == 0 { - return 0, fmt.Errorf("couldn't extract boot time from /proc/stat") - } - - btimeSec, err := strconv.ParseInt(btimeStr, 10, 64) - if err != nil { - return 0, fmt.Errorf("error parsing boot time from /proc/stat: %s", err) - } - - return btimeSec, nil -} - -// stat is a direct translation of a `/proc/[pid]/stat` file as described in -// the proc(5) manpage. Please note that it is not a full translation as not -// all fields are in the scope of this library and higher indices are -// Kernel-version dependent. -type stat struct { - // (1) The process ID - pid string - // (2) The filename of the executable, in parentheses. This is visible - // whether or not the executable is swapped out. - comm string - // (3) The process state (e.g., running, sleeping, zombie, dead). - // Refer to proc(5) for further deatils. - state string - // (4) The PID of the parent of this process. - ppid string - // (5) The process group ID of the process. - pgrp string - // (6) The session ID of the process. - session string - // (7) The controlling terminal of the process. (The minor device - // number is contained in the combination of bits 31 to 20 and 7 to 0; - // the major device number is in bits 15 to 8.) - ttyNr string - // (8) The ID of the foreground process group of the controlling - // terminal of the process. - tpgid string - // (9) The kernel flags word of the process. For bit meanings, see the - // PF_* defines in the Linux kernel source file - // include/linux/sched.h. Details depend on the kernel version. - flags string - // (10) The number of minor faults the process has made which have not - // required loading a memory page from disk. - minflt string - // (11) The number of minor faults that the process's waited-for - // children have made. - cminflt string - // (12) The number of major faults the process has made which have - // required loading a memory page from disk. - majflt string - // (13) The number of major faults that the process's waited-for - // children have made. - cmajflt string - // (14) Amount of time that this process has been scheduled in user - // mode, measured in clock ticks (divide by - // sysconf(_SC_CLK_TCK)). This includes guest time, guest_time - // (time spent running a virtual CPU, see below), so that applications - // that are not aware of the guest time field do not lose that time - // from their calculations. - utime string - // (15) Amount of time that this process has been scheduled in kernel - // mode, measured in clock ticks (divide by sysconf(_SC_CLK_TCK)). - stime string - // (16) Amount of time that this process's waited-for children have - // been scheduled in user mode, measured in clock ticks (divide by - // sysconf(_SC_CLK_TCK)). (See also times(2).) This includes guest - // time, cguest_time (time spent running a virtual CPU, see below). - cutime string - // (17) Amount of time that this process's waited-for children have - // been scheduled in kernel mode, measured in clock ticks (divide by - // sysconf(_SC_CLK_TCK)). - cstime string - // (18) (Explanation for Linux 2.6+) For processes running a real-time - // scheduling policy (policy below; see sched_setscheduler(2)), this is - // the negated scheduling pri- ority, minus one; that is, a number - // in the range -2 to -100, corresponding to real-time priorities 1 to - // 99. For processes running under a non-real-time scheduling - // policy, this is the raw nice value (setpriority(2)) as represented - // in the kernel. The kernel stores nice values as numbers in the - // range 0 (high) to 39 (low), corresponding to the user-visible nice - // range of -20 to 19. - priority string - // (19) The nice value (see setpriority(2)), a value in the range 19 - // (low priority) to -20 (high priority). - nice string - // (20) Number of threads in this process (since Linux 2.6). Before - // kernel 2.6, this field was hard coded to 0 as a placeholder for an - // earlier removed field. - numThreads string - // (21) The time in jiffies before the next SIGALRM is sent to the - // process due to an interval timer. Since kernel 2.6.17, this - // field is no longer maintained, and is hard coded as 0. - itrealvalue string - // (22) The time the process started after system boot. In kernels - // before Linux 2.6, this value was expressed in jiffies. Since - // Linux 2.6, the value is expressed in clock ticks (divide by - // sysconf(_SC_CLK_TCK)). - starttime string - // (23) Virtual memory size in bytes. - vsize string -} - -// readStat is used for mocking in unit tests. -var readStat = func(path string) ([]string, error) { - data, err := ioutil.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - err = errNoSuchPID - } - return nil, err - } - - return strings.Fields(string(data)), nil -} - -// parseStat parses the /proc/$pid/stat file and returns a stat. -func parseStat(path string) (*stat, error) { - fields, err := readStat(path) - if err != nil { - return nil, err - } - - fieldAt := func(i int) string { - return fields[i-1] - } - - return &stat{ - pid: fieldAt(1), - comm: fieldAt(2), - state: fieldAt(3), - ppid: fieldAt(4), - pgrp: fieldAt(5), - session: fieldAt(6), - ttyNr: fieldAt(7), - tpgid: fieldAt(8), - flags: fieldAt(9), - minflt: fieldAt(10), - cminflt: fieldAt(11), - majflt: fieldAt(12), - cmajflt: fieldAt(13), - utime: fieldAt(14), - stime: fieldAt(15), - cutime: fieldAt(16), - cstime: fieldAt(17), - priority: fieldAt(18), - nice: fieldAt(19), - numThreads: fieldAt(20), - itrealvalue: fieldAt(21), - starttime: fieldAt(22), - vsize: fieldAt(23), - }, nil -} diff --git a/vendor/github.com/containers/psgo/ps/status.go b/vendor/github.com/containers/psgo/ps/status.go deleted file mode 100644 index 7f496bc48..000000000 --- a/vendor/github.com/containers/psgo/ps/status.go +++ /dev/null @@ -1,314 +0,0 @@ -package ps - -import ( - "bufio" - "fmt" - "os" - "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 -} - -// readStatus is used for mocking in unit tests. -var readStatus = func(path string) ([]string, error) { - f, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - err = errNoSuchPID - } - 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(path string) (*status, error) { - lines, err := readStatus(path) - if err != nil { - return nil, err - } - - s := status{} - errUnexpectedInput := errors.New(fmt.Sprintf("unexpected input from %s", path)) - 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:": - for _, g := range fields[1:] { - s.groups = append(s.groups, g) - } - 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 -} diff --git a/vendor/github.com/containers/psgo/ps/tty.go b/vendor/github.com/containers/psgo/ps/tty.go deleted file mode 100644 index 7c5f3a53b..000000000 --- a/vendor/github.com/containers/psgo/ps/tty.go +++ /dev/null @@ -1,113 +0,0 @@ -package ps - -import ( - "os" - "strings" - "syscall" -) - -// tty represents a tty including its minor and major device number and the -// path to the tty. -type tty struct { - // minor device number - minor uint64 - // major device number - major uint64 - // path to the tty - device string -} - -// majDevNum returns the major device number of rdev (see stat_t.Rdev). -func majDevNum(rdev uint64) uint64 { - return (rdev >> 8) & 0xfff -} - -// minDevNum returns the minor device number of rdev (see stat_t.Rdev). -func minDevNum(rdev uint64) uint64 { - return (rdev & 0xff) | ((rdev >> 12) & 0xfff00) -} - -// ttyNrToDev returns the major and minor number of the tty device from -// /proc/$pid/stat.tty_nr as described in man 5 proc. -func ttyNrToDev(ttyNr uint64) (uint64, uint64) { - // (man 5 proc) The minor device number is contained in the combination - // of bits 31 to 20 and 7 to 0; the major device number is in bits 15 - // to 8. - maj := (ttyNr >> 8) & 0xFF - min := (ttyNr & 0xFF) | ((ttyNr >> 20) & 0xFFF) - return maj, min -} - -// findTTY returns a tty with the corresponding major and minor device number -// or nil if no matching tty is found. -func findTTY(maj, min uint64) (*tty, error) { - if len(ttyDevices) == 0 { - var err error - ttyDevices, err = getTTYs() - if err != nil { - return nil, err - } - } - for _, t := range ttyDevices { - if t.minor == min && t.major == maj { - return t, nil - } - } - return nil, nil -} - -// getTTYs parses /dev for tty and pts devices. -func getTTYs() ([]*tty, error) { - devDir, err := os.Open("/dev/") - if err != nil { - return nil, err - } - defer devDir.Close() - - devices := []string{} - devTTYs, err := devDir.Readdirnames(0) - if err != nil { - return nil, err - } - for _, d := range devTTYs { - if !strings.HasPrefix(d, "tty") { - continue - } - devices = append(devices, "/dev/"+d) - } - - devPTSDir, err := os.Open("/dev/pts/") - if err != nil { - return nil, err - } - defer devPTSDir.Close() - - devPTSs, err := devPTSDir.Readdirnames(0) - if err != nil { - return nil, err - } - for _, d := range devPTSs { - devices = append(devices, "/dev/pts/"+d) - } - - ttys := []*tty{} - for _, dev := range devices { - fi, err := os.Stat(dev) - if err != nil { - if os.IsNotExist(err) { - // catch race conditions - continue - } - return nil, err - } - s := fi.Sys().(*syscall.Stat_t) - t := tty{ - minor: minDevNum(s.Rdev), - major: majDevNum(s.Rdev), - device: dev, - } - ttys = append(ttys, &t) - } - - return ttys, nil -} diff --git a/vendor/github.com/containers/psgo/psgo.go b/vendor/github.com/containers/psgo/psgo.go new file mode 100644 index 000000000..e52089b3c --- /dev/null +++ b/vendor/github.com/containers/psgo/psgo.go @@ -0,0 +1,640 @@ +// Package psgo is a ps (1) AIX-format compatible golang library extended with +// various descriptors useful for displaying container-related data. +// +// The idea behind the library is to provide an easy to use way of extracting +// process-related data, just as ps (1) does. The problem when using ps (1) is +// that the ps format strings split columns with whitespaces, making the output +// nearly impossible to parse. It also adds some jitter as we have to fork and +// execute ps either in the container or filter the output afterwards, further +// limiting applicability. +// +// Please visit https://github.com/containers/psgo for further details about +// supported format descriptors and to see some usage examples. +package psgo + +import ( + "fmt" + "os" + "runtime" + "sort" + "strconv" + "strings" + "sync" + + "github.com/containers/psgo/internal/capabilities" + "github.com/containers/psgo/internal/dev" + "github.com/containers/psgo/internal/proc" + "github.com/containers/psgo/internal/process" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// processFunc is used to map a given aixFormatDescriptor to a corresponding +// function extracting the desired data from a process. +type processFunc func(*process.Process) (string, error) + +// aixFormatDescriptor as mentioned in the ps(1) manpage. A given descriptor +// can either be specified via its code (e.g., "%C") or its normal representation +// (e.g., "pcpu") and will be printed under its corresponding header (e.g, "%CPU"). +type aixFormatDescriptor struct { + // code descriptor in the short form (e.g., "%C"). + code string + // normal descriptor in the long form (e.g., "pcpu"). + normal string + // header of the descriptor (e.g., "%CPU"). + header string + // onHost controls if data of the corresponding host processes will be + // extracted as well. + onHost bool + // procFN points to the corresponding method to etract the desired data. + procFn processFunc +} + +// translateDescriptors parses the descriptors and returns a correspodning slice of +// aixFormatDescriptors. Descriptors can be specified in the normal and in the +// code form (if supported). If the descriptors slice is empty, the +// `DefaultDescriptors` is used. +func translateDescriptors(descriptors []string) ([]aixFormatDescriptor, error) { + if len(descriptors) == 0 { + descriptors = DefaultDescriptors + } + + formatDescriptors := []aixFormatDescriptor{} + for _, d := range descriptors { + d = strings.TrimSpace(d) + found := false + for _, aix := range aixFormatDescriptors { + if d == aix.code || d == aix.normal { + formatDescriptors = append(formatDescriptors, aix) + found = true + } + } + if !found { + return nil, errors.Wrapf(ErrUnkownDescriptor, "'%s'", d) + } + } + + return formatDescriptors, nil +} + +var ( + // DefaultDescriptors is the `ps -ef` compatible default format. + DefaultDescriptors = []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "comm"} + + // ErrUnkownDescriptor is returned when an unknown descriptor is parsed. + ErrUnkownDescriptor = errors.New("unknown descriptor") + + // hostProcesses are the processes on the host. It should only be used + // in the context of containers and is meant to display data of the + // container processes from the host's (i.e., calling process) view. + // Currently, all host processes contain only the required data from + // /proc/$pid/status. + hostProcesses []*process.Process + + aixFormatDescriptors = []aixFormatDescriptor{ + { + code: "%C", + normal: "pcpu", + header: "%CPU", + procFn: processPCPU, + }, + { + code: "%G", + normal: "group", + header: "GROUP", + procFn: processGROUP, + }, + { + code: "%P", + normal: "ppid", + header: "PPID", + procFn: processPPID, + }, + { + code: "%U", + normal: "user", + header: "USER", + procFn: processUSER, + }, + { + code: "%a", + normal: "args", + header: "COMMAND", + procFn: processARGS, + }, + { + code: "%c", + normal: "comm", + header: "COMMAND", + procFn: processCOMM, + }, + { + code: "%g", + normal: "rgroup", + header: "RGROUP", + procFn: processRGROUP, + }, + { + code: "%n", + normal: "nice", + header: "NI", + procFn: processNICE, + }, + { + code: "%p", + normal: "pid", + header: "PID", + procFn: processPID, + }, + { + code: "%r", + normal: "pgid", + header: "PGID", + procFn: processPGID, + }, + { + code: "%t", + normal: "etime", + header: "ELAPSED", + procFn: processETIME, + }, + { + code: "%u", + normal: "ruser", + header: "RUSER", + procFn: processRUSER, + }, + { + code: "%x", + normal: "time", + header: "TIME", + procFn: processTIME, + }, + { + code: "%y", + normal: "tty", + header: "TTY", + procFn: processTTY, + }, + { + code: "%z", + normal: "vsz", + header: "VSZ", + procFn: processVSZ, + }, + { + normal: "capinh", + header: "CAPABILITIES", + procFn: processCAPINH, + }, + { + normal: "capprm", + header: "CAPABILITIES", + procFn: processCAPPRM, + }, + { + normal: "capeff", + header: "CAPABILITIES", + procFn: processCAPEFF, + }, + { + normal: "capbnd", + header: "CAPABILITIES", + procFn: processCAPBND, + }, + { + normal: "seccomp", + header: "SECCOMP", + procFn: processSECCOMP, + }, + { + normal: "label", + header: "LABEL", + procFn: processLABEL, + }, + { + normal: "hpid", + header: "HPID", + onHost: true, + procFn: processHPID, + }, + { + normal: "huser", + header: "HUSER", + onHost: true, + procFn: processHUSER, + }, + { + normal: "hgroup", + header: "HGROUP", + onHost: true, + procFn: processHGROUP, + }, + { + normal: "state", + header: "STATE", + procFn: processState, + }, + } +) + +// ListDescriptors returns a string slice of all supported AIX format +// descriptors in the normal form. +func ListDescriptors() (list []string) { + for _, d := range aixFormatDescriptors { + list = append(list, d.normal) + } + sort.Strings(list) + return +} + +// JoinNamespaceAndProcessInfo has the same semantics as ProcessInfo but joins +// the mount namespace of the specified pid before extracting data from `/proc`. +func JoinNamespaceAndProcessInfo(pid string, descriptors []string) ([][]string, error) { + var ( + data [][]string + dataErr error + wg sync.WaitGroup + ) + + aixDescriptors, err := translateDescriptors(descriptors) + if err != nil { + return nil, err + } + + // extract data from host processes only on-demand / when at least one + // of the specified descriptors requires host data + for _, d := range aixDescriptors { + if d.onHost { + setHostProcesses(pid) + break + } + } + + wg.Add(1) + go func() { + defer wg.Done() + runtime.LockOSThread() + + fd, err := os.Open(fmt.Sprintf("/proc/%s/ns/mnt", pid)) + if err != nil { + dataErr = err + return + } + defer fd.Close() + + // create a new mountns on the current thread + if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { + dataErr = err + return + } + unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS) + + pids, err := proc.GetPIDs() + if err != nil { + dataErr = err + return + } + processes, err := process.FromPIDs(pids) + if err != nil { + dataErr = err + return + } + + data, dataErr = processDescriptors(aixDescriptors, processes) + }() + wg.Wait() + + return data, dataErr +} + +// ProcessInfo returns the process information of all processes in the current +// mount namespace. The input format must be a comma-separated list of +// supported AIX format descriptors. If the input string is empty, the +// `DefaultDescriptors` is used. +// The return value is an array of tab-separated strings, to easily use the +// output for column-based formatting (e.g., with the `text/tabwriter` package). +func ProcessInfo(descriptors []string) ([][]string, error) { + aixDescriptors, err := translateDescriptors(descriptors) + if err != nil { + return nil, err + } + + pids, err := proc.GetPIDs() + if err != nil { + return nil, err + } + processes, err := process.FromPIDs(pids) + if err != nil { + return nil, err + } + + return processDescriptors(aixDescriptors, processes) +} + +// setHostProcesses sets `hostProcesses`. +func setHostProcesses(pid string) error { + // get processes + pids, err := proc.GetPIDsFromCgroup(pid) + if err != nil { + return err + } + + processes, err := process.FromPIDs(pids) + if err != nil { + return err + } + + // set the additional host data + for _, p := range processes { + if err := p.SetHostData(); err != nil { + return err + } + } + + hostProcesses = processes + return nil +} + +// processDescriptors calls each `procFn` of all formatDescriptors on each +// process and returns an array of tab-separated strings. +func processDescriptors(formatDescriptors []aixFormatDescriptor, processes []*process.Process) ([][]string, error) { + data := [][]string{} + // create header + header := []string{} + for _, desc := range formatDescriptors { + header = append(header, desc.header) + } + data = append(data, header) + + // dispatch all descriptor functions on each process + for _, proc := range processes { + pData := []string{} + for _, desc := range formatDescriptors { + dataStr, err := desc.procFn(proc) + if err != nil { + return nil, err + } + pData = append(pData, dataStr) + } + data = append(data, pData) + } + + return data, nil +} + +// findHostProcess returns the corresponding process from `hostProcesses` or +// nil if non is found. +func findHostProcess(p *process.Process) *process.Process { + for _, hp := range hostProcesses { + // We expect the host process to be in another namespace, so + // /proc/$pid/status.NSpid must have at least two entries. + if len(hp.Status.NSpid) < 2 { + continue + } + // The process' PID must match the one in the NS of the host + // process and both must share the same pid NS. + if p.Pid == hp.Status.NSpid[1] && p.PidNS == hp.PidNS { + return hp + } + } + return nil +} + +// processGROUP returns the effective group ID of the process. This will be +// the textual group ID, if it can be optained, or a decimal representation +// otherwise. +func processGROUP(p *process.Process) (string, error) { + return process.LookupGID(p.Status.Gids[1]) +} + +// processRGROUP returns the real group ID of the process. This will be +// the textual group ID, if it can be optained, or a decimal representation +// otherwise. +func processRGROUP(p *process.Process) (string, error) { + return process.LookupGID(p.Status.Gids[0]) +} + +// processPPID returns the parent process ID of process p. +func processPPID(p *process.Process) (string, error) { + return p.Status.PPid, nil +} + +// processUSER returns the effective user name of the process. This will be +// the textual group ID, if it can be optained, or a decimal representation +// otherwise. +func processUSER(p *process.Process) (string, error) { + return process.LookupUID(p.Status.Uids[1]) +} + +// processRUSER returns the effective user name of the process. This will be +// the textual group ID, if it can be optained, or a decimal representation +// otherwise. +func processRUSER(p *process.Process) (string, error) { + return process.LookupUID(p.Status.Uids[0]) +} + +// processName returns the name of process p in the format "[$name]". +func processName(p *process.Process) (string, error) { + return fmt.Sprintf("[%s]", p.Status.Name), nil +} + +// processARGS returns the command of p with all its arguments. +func processARGS(p *process.Process) (string, error) { + args := p.CmdLine + // ps (1) returns "[$name]" if command/args are empty + if len(args) == 0 { + return processName(p) + } + return strings.Join(args, " "), nil +} + +// processCOMM returns the command name (i.e., executable name) of process p. +func processCOMM(p *process.Process) (string, error) { + args := p.CmdLine + // ps (1) returns "[$name]" if command/args are empty + if len(args) == 0 { + return processName(p) + } + spl := strings.Split(args[0], "/") + return spl[len(spl)-1], nil +} + +// processNICE returns the nice value of process p. +func processNICE(p *process.Process) (string, error) { + return p.Stat.Nice, nil +} + +// processPID returns the process ID of process p. +func processPID(p *process.Process) (string, error) { + return p.Pid, nil +} + +// processPGID returns the process group ID of process p. +func processPGID(p *process.Process) (string, error) { + return p.Stat.Pgrp, nil +} + +// processPCPU returns how many percent of the CPU time process p uses as +// a three digit float as string. +func processPCPU(p *process.Process) (string, error) { + elapsed, err := p.ElapsedTime() + if err != nil { + return "", err + } + cpu, err := p.CPUTime() + if err != nil { + return "", err + } + pcpu := 100 * cpu.Seconds() / elapsed.Seconds() + + return strconv.FormatFloat(pcpu, 'f', 3, 64), nil +} + +// processETIME returns the elapsed time since the process was started. +func processETIME(p *process.Process) (string, error) { + elapsed, err := p.ElapsedTime() + if err != nil { + return "", nil + } + return fmt.Sprintf("%v", elapsed), nil +} + +// processTIME returns the cumulative CPU time of process p. +func processTIME(p *process.Process) (string, error) { + cpu, err := p.CPUTime() + if err != nil { + return "", err + } + return fmt.Sprintf("%v", cpu), nil +} + +// processTTY returns the controlling tty (terminal) of process p. +func processTTY(p *process.Process) (string, error) { + ttyNr, err := strconv.ParseUint(p.Stat.TtyNr, 10, 64) + if err != nil { + return "", nil + } + + tty, err := dev.FindTTY(ttyNr) + if err != nil { + return "", nil + } + + ttyS := "?" + if tty != nil { + ttyS = strings.TrimPrefix(tty.Path, "/dev/") + } + return ttyS, nil +} + +// processVSZ returns the virtual memory size of process p in KiB (1024-byte +// units). +func processVSZ(p *process.Process) (string, error) { + vmsize, err := strconv.Atoi(p.Stat.Vsize) + if err != nil { + return "", err + } + return fmt.Sprintf("%d", vmsize/1024), nil +} + +// parseCAP parses cap (a string bit mask) and returns the associated set of +// capabilities. If all capabilties are set, "full" is returned. If no +// capability is enabled, "none" is returned. +func parseCAP(cap string) (string, error) { + mask, err := strconv.ParseUint(cap, 16, 64) + if err != nil { + return "", err + } + if mask == capabilities.FullCAPs { + return "full", nil + } + caps := capabilities.TranslateMask(mask) + if len(caps) == 0 { + return "none", nil + } + sort.Strings(caps) + return strings.Join(caps, ","), nil +} + +// processCAPINH returns the set of inheritable capabilties associated with +// process p. If all capabilties are set, "full" is returned. If no +// capability is enabled, "none" is returned. +func processCAPINH(p *process.Process) (string, error) { + return parseCAP(p.Status.CapInh) +} + +// processCAPPRM returns the set of permitted capabilties associated with +// process p. If all capabilties are set, "full" is returned. If no +// capability is enabled, "none" is returned. +func processCAPPRM(p *process.Process) (string, error) { + return parseCAP(p.Status.CapPrm) +} + +// processCAPEFF returns the set of effective capabilties associated with +// process p. If all capabilties are set, "full" is returned. If no +// capability is enabled, "none" is returned. +func processCAPEFF(p *process.Process) (string, error) { + return parseCAP(p.Status.CapEff) +} + +// processCAPBND returns the set of bounding capabilties associated with +// process p. If all capabilties are set, "full" is returned. If no +// capability is enabled, "none" is returned. +func processCAPBND(p *process.Process) (string, error) { + return parseCAP(p.Status.CapBnd) +} + +// processSECCOMP returns the seccomp mode of the process (i.e., disabled, +// strict or filter) or "?" if /proc/$pid/status.seccomp has a unknown value. +func processSECCOMP(p *process.Process) (string, error) { + switch p.Status.Seccomp { + case "0": + return "disabled", nil + case "1": + return "strict", nil + case "2": + return "filter", nil + default: + return "?", nil + } +} + +// processLABEL returns the process label of process p or "?" if the system +// doesn't support labeling. +func processLABEL(p *process.Process) (string, error) { + return p.Label, nil +} + +// processHPID returns the PID of the corresponding host process of the +// (container) or "?" if no corresponding process could be found. +func processHPID(p *process.Process) (string, error) { + if hp := findHostProcess(p); hp != nil { + return hp.Pid, nil + } + return "?", nil +} + +// processHUSER returns the effective user ID of the corresponding host process +// of the (container) or "?" if no corresponding process could be found. +func processHUSER(p *process.Process) (string, error) { + if hp := findHostProcess(p); hp != nil { + return hp.Huser, nil + } + return "?", nil +} + +// processHGROUP returns the effective group ID of the corresponding host +// process of the (container) or "?" if no corresponding process could be +// found. +func processHGROUP(p *process.Process) (string, error) { + if hp := findHostProcess(p); hp != nil { + return hp.Hgroup, nil + } + return "?", nil +} + +// processState returns the process state of process p. +func processState(p *process.Process) (string, error) { + return p.Status.State, nil +} -- cgit v1.2.3-54-g00ecf