123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962 |
- // Copyright 2021 Gridfinity, LLC.
- // Copyright 2021 Jeffrey H. Johnson.
- // Copyright 2020 The psgo authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // Package gfpsgo 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.
- //
- package gfpsgo // import "go.gridfinity.dev/gfpsgo"
- import (
- "fmt"
- "io/ioutil"
- "os"
- "runtime"
- "sort"
- "strconv"
- "strings"
- "sync"
- "github.com/pkg/errors"
- "go.gridfinity.dev/gfpsgo/internal/capabilities"
- "go.gridfinity.dev/gfpsgo/internal/dev"
- "go.gridfinity.dev/gfpsgo/internal/proc"
- "go.gridfinity.dev/gfpsgo/internal/process"
- "golang.org/x/sys/unix"
- )
- // IDMap specifies a mapping range from the host to the container IDs.
- type IDMap struct {
- // ContainerID is the first ID in the container.
- ContainerID int
- // HostID is the first ID in the host.
- HostID int
- // Size specifies how long is the range. e.g. 1 means a single user
- // is mapped.
- Size int
- }
- // JoinNamespaceOpts specifies different options for joining the specified
- // namespaces.
- type JoinNamespaceOpts struct {
- // UIDMap specifies a mapping for UIDs in the container. If specified
- // huser will perform the reverse mapping.
- UIDMap []IDMap
- // GIDMap specifies a mapping for GIDs in the container. If specified
- // hgroup will perform the reverse mapping.
- GIDMap []IDMap
- // FillMappings specified whether UIDMap and GIDMap must be initialized
- // with the current user namespace.
- FillMappings bool
- }
- type psContext struct {
- // Processes in the container.
- containersProcesses []*process.Process
- // Processes on the host. Used to map those to the ones running in the
- // container.
- hostProcesses []*process.Process
- // tty and pty devices.
- ttys *[]dev.TTY
- // Various options
- opts *JoinNamespaceOpts
- }
- // processFunc is used to map a given aixFormatDescriptor to a corresponding
- // function extracting the desired data from a process.
- type processFunc func(*process.Process, *psContext) (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 extract the desired data.
- procFn processFunc
- }
- // findID converts the specified id to the host mapping
- func findID(
- idStr string,
- mapping []IDMap,
- lookupFunc func(uid string) (string, error),
- overflowFile string,
- ) (string, error) {
- if len(mapping) == 0 {
- return idStr, nil
- }
- id, err := strconv.ParseInt(idStr, 10, 0)
- if err != nil {
- return "", errors.Wrapf(err, "cannot parse %s", idStr)
- }
- for _, m := range mapping {
- if int(id) >= m.ContainerID && int(id) < m.ContainerID+m.Size {
- user := fmt.Sprintf("%d", m.HostID+(int(id)-m.ContainerID))
- return lookupFunc(user)
- }
- }
- // User not found, read the overflow
- overflow, err := ioutil.ReadFile(overflowFile)
- if err != nil {
- return "", errors.Wrapf(err, "cannot read %s", overflowFile)
- }
- return string(overflow), nil
- }
- // 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(ErrUnknownDescriptor, "'%s'", d)
- }
- }
- return formatDescriptors, nil
- }
- var (
- // DefaultDescriptors is the `ps -ef` compatible default format.
- DefaultDescriptors = []string{
- "user",
- "pid",
- "ppid",
- "pcpu",
- "etime",
- "tty",
- "time",
- "args",
- }
- // ErrUnknownDescriptor is returned when an unknown descriptor is parsed.
- ErrUnknownDescriptor = errors.New("unknown descriptor")
- 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: "capamb",
- header: "AMBIENT CAPS",
- procFn: processCAPAMB,
- },
- {
- normal: "capinh",
- header: "INHERITED CAPS",
- procFn: processCAPINH,
- },
- {
- normal: "capprm",
- header: "PERMITTED CAPS",
- procFn: processCAPPRM,
- },
- {
- normal: "capeff",
- header: "EFFECTIVE CAPS",
- procFn: processCAPEFF,
- },
- {
- normal: "capbnd",
- header: "BOUNDING CAPS",
- 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: "rss",
- header: "RSS",
- procFn: processRSS,
- },
- {
- normal: "state",
- header: "STATE",
- procFn: processState,
- },
- {
- normal: "stime",
- header: "STIME",
- procFn: processStartTime,
- },
- }
- )
- // 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) {
- return JoinNamespaceAndProcessInfoWithOptions(
- pid,
- descriptors,
- &JoinNamespaceOpts{},
- )
- }
- // ReadMappings ...
- func ReadMappings(path string) ([]IDMap, error) {
- mappings, err := proc.ReadMappings(path)
- if err != nil {
- return nil, err
- }
- var res []IDMap
- for _, i := range mappings {
- m := IDMap{
- ContainerID: i.ContainerID,
- HostID: i.HostID,
- Size: i.Size,
- }
- res = append(res, m)
- }
- return res, nil
- }
- func contextFromOptions(options *JoinNamespaceOpts) (*psContext, error) {
- ctx := new(psContext)
- ctx.opts = options
- if ctx.opts != nil && ctx.opts.FillMappings {
- uidMappings, err := ReadMappings("/proc/self/uid_map")
- if err != nil {
- return nil, err
- }
- gidMappings, err := ReadMappings("/proc/self/gid_map")
- if err != nil {
- return nil, err
- }
- ctx.opts.UIDMap = uidMappings
- ctx.opts.GIDMap = gidMappings
- ctx.opts.FillMappings = false
- }
- return ctx, nil
- }
- // JoinNamespaceAndProcessInfoWithOptions has the same semantics as
- // ProcessInfo but joins
- // the mount namespace of the specified pid before extracting data from
- // `/proc`.
- func JoinNamespaceAndProcessInfoWithOptions(
- pid string,
- descriptors []string,
- options *JoinNamespaceOpts,
- ) ([][]string, error) {
- var (
- data [][]string
- dataErr error
- wg sync.WaitGroup
- )
- aixDescriptors, err := translateDescriptors(descriptors)
- if err != nil {
- return nil, err
- }
- ctx, err := contextFromOptions(options)
- 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 {
- ctx.hostProcesses, err = hostProcesses(pid)
- if err != nil {
- return nil, err
- }
- break
- }
- }
- wg.Add(1)
- go func() {
- defer wg.Done()
- runtime.LockOSThread()
- // extract user namespaces prior to joining the mount namespace
- currentUserNs, err := proc.ParseUserNamespace("self")
- if err != nil {
- dataErr = errors.Wrapf(err, "error determining user namespace")
- return
- }
- pidUserNs, err := proc.ParseUserNamespace(pid)
- if err != nil {
- dataErr = errors.Wrapf(
- err,
- "error determining user namespace of PID %s",
- pid,
- )
- }
- // join the mount namespace of pid
- 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
- }
- if err := unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS); err != nil {
- dataErr = err
- return
- }
- // extract all pids mentioned in pid's mount namespace
- pids, err := proc.GetPIDs()
- if err != nil {
- dataErr = err
- return
- }
- // join the user NS if the pid's user NS is different
- // to the caller's user NS.
- joinUserNS := currentUserNs != pidUserNs
- ctx.containersProcesses, err = process.FromPIDs(pids, joinUserNS)
- if err != nil {
- dataErr = err
- return
- }
- data, dataErr = processDescriptors(aixDescriptors, ctx)
- }()
- wg.Wait()
- return data, dataErr
- }
- // JoinNamespaceAndProcessInfoByPidsWithOptions has similar semantics to
- // JoinNamespaceAndProcessInfo and avoids duplicate entries by joining a
- // giving
- // PID namespace only once.
- func JoinNamespaceAndProcessInfoByPidsWithOptions(
- pids, descriptors []string,
- options *JoinNamespaceOpts,
- ) ([][]string, error) {
- // Extracting data from processes that share the same PID namespace
- // would yield duplicate results. Avoid that by extracting data only
- // from the first process in `pids` from a given PID namespace.
- // `nsMap` is used for quick lookups if a given PID namespace is
- // already covered, `pidList` is used to preserve the order which is
- // not guaranteed by nondeterministic maps in golang.
- nsMap := make(map[string]bool)
- pidList := []string{}
- for _, pid := range pids {
- ns, err := proc.ParsePIDNamespace(pid)
- if err != nil {
- if os.IsNotExist(errors.Cause(err)) {
- // catch race conditions
- continue
- }
- return nil, errors.Wrapf(err, "error extracting PID namespace")
- }
- if _, exists := nsMap[ns]; !exists {
- nsMap[ns] = true
- pidList = append(pidList, pid)
- }
- }
- data := [][]string{}
- for i, pid := range pidList {
- pidData, err := JoinNamespaceAndProcessInfoWithOptions(
- pid,
- descriptors,
- options,
- )
- if os.IsNotExist(errors.Cause(err)) {
- // catch race conditions
- continue
- }
- if err != nil {
- return nil, err
- }
- if i == 0 {
- data = append(data, pidData[0])
- }
- data = append(data, pidData[1:]...)
- }
- return data, nil
- }
- // JoinNamespaceAndProcessInfoByPids has similar semantics to
- // JoinNamespaceAndProcessInfo and avoids duplicate entries by joining a
- // giving
- // PID namespace only once.
- func JoinNamespaceAndProcessInfoByPids(
- pids, descriptors []string,
- ) ([][]string, error) {
- return JoinNamespaceAndProcessInfoByPidsWithOptions(
- pids,
- descriptors,
- &JoinNamespaceOpts{},
- )
- }
- // 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) {
- pids, err := proc.GetPIDs()
- if err != nil {
- return nil, err
- }
- return ProcessInfoByPids(pids, descriptors)
- }
- // ProcessInfoByPids is like ProcessInfo, but the process information
- // returned
- // is limited to a list of user specified PIDs.
- func ProcessInfoByPids(pids, descriptors []string) ([][]string, error) {
- aixDescriptors, err := translateDescriptors(descriptors)
- if err != nil {
- return nil, err
- }
- ctx, err := contextFromOptions(nil)
- if err != nil {
- return nil, err
- }
- ctx.containersProcesses, err = process.FromPIDs(pids, false)
- if err != nil {
- return nil, err
- }
- return processDescriptors(aixDescriptors, ctx)
- }
- // hostProcesses returns all processes running in the current namespace.
- func hostProcesses(pid string) ([]*process.Process, error) {
- // get processes
- pids, err := proc.GetPIDsFromCgroup(pid)
- if err != nil {
- return nil, err
- }
- processes, err := process.FromPIDs(pids, false)
- if err != nil {
- return nil, err
- }
- // set the additional host data
- for _, p := range processes {
- if err := p.SetHostData(); err != nil {
- return nil, err
- }
- }
- return processes, nil
- }
- // processDescriptors calls each `procFn` of all formatDescriptors on each
- // process and returns an array of tab-separated strings.
- func processDescriptors(
- formatDescriptors []aixFormatDescriptor,
- ctx *psContext,
- ) ([][]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 ctx.containersProcesses {
- pData := []string{}
- for _, desc := range formatDescriptors {
- dataStr, err := desc.procFn(proc, ctx)
- 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, ctx *psContext) *process.Process {
- for _, hp := range ctx.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, _ *psContext) (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, _ *psContext) (string, error) {
- return process.LookupGID(p.Status.Gids[0])
- }
- // processPPID returns the parent process ID of process p.
- func processPPID(p *process.Process, _ *psContext) (string, error) {
- return p.Status.PPid, nil
- }
- // processUSER returns the effective user name of the process. This will be
- // the textual user ID, if it can be optained, or a decimal representation
- // otherwise.
- func processUSER(p *process.Process, _ *psContext) (string, error) {
- return process.LookupUID(p.Status.Uids[1])
- }
- // processRUSER returns the effective user name of the process. This will be
- // the textual user ID, if it can be optained, or a decimal representation
- // otherwise.
- func processRUSER(p *process.Process, _ *psContext) (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, _ *psContext) (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, ctx *psContext) (string, error) {
- // ps (1) returns "[$name]" if command/args are empty
- if p.CmdLine[0] == "" {
- return processName(p, ctx)
- }
- return strings.Join(p.CmdLine, " "), nil
- }
- // processCOMM returns the command name (i.e., executable name) of process p.
- func processCOMM(p *process.Process, _ *psContext) (string, error) {
- return p.Stat.Comm, nil
- }
- // processNICE returns the nice value of process p.
- func processNICE(p *process.Process, _ *psContext) (string, error) {
- return p.Stat.Nice, nil
- }
- // processPID returns the process ID of process p.
- func processPID(p *process.Process, _ *psContext) (string, error) {
- return p.Pid, nil
- }
- // processPGID returns the process group ID of process p.
- func processPGID(p *process.Process, _ *psContext) (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, _ *psContext) (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, _ *psContext) (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, _ *psContext) (string, error) {
- cpu, err := p.CPUTime()
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%v", cpu), nil
- }
- // processStartTime returns the start time of process p.
- func processStartTime(p *process.Process, _ *psContext) (string, error) {
- sTime, err := p.StartTime()
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%v", sTime), nil
- }
- // processTTY returns the controlling tty (terminal) of process p.
- func processTTY(p *process.Process, ctx *psContext) (string, error) {
- ttyNr, err := strconv.ParseUint(p.Stat.TtyNr, 10, 64)
- if err != nil {
- return "", nil
- }
- tty, err := dev.FindTTY(ttyNr, ctx.ttys)
- 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, _ *psContext) (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 capabilities are set, "full" is returned. If no
- // capability is enabled, "none" is returned.
- func parseCAP(capz string) (string, error) {
- mask, err := strconv.ParseUint(capz, 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
- }
- // processCAPAMB returns the set of ambient capabilities associated with
- // process p. If all capabilities are set, "full" is returned. If no
- // capability is enabled, "none" is returned.
- func processCAPAMB(p *process.Process, _ *psContext) (string, error) {
- return parseCAP(p.Status.CapAmb)
- }
- // processCAPINH returns the set of inheritable capabilities associated with
- // process p. If all capabilities are set, "full" is returned. If no
- // capability is enabled, "none" is returned.
- func processCAPINH(p *process.Process, _ *psContext) (string, error) {
- return parseCAP(p.Status.CapInh)
- }
- // processCAPPRM returns the set of permitted capabilities associated with
- // process p. If all capabilities are set, "full" is returned. If no
- // capability is enabled, "none" is returned.
- func processCAPPRM(p *process.Process, _ *psContext) (string, error) {
- return parseCAP(p.Status.CapPrm)
- }
- // processCAPEFF returns the set of effective capabilities associated with
- // process p. If all capabilities are set, "full" is returned. If no
- // capability is enabled, "none" is returned.
- func processCAPEFF(p *process.Process, _ *psContext) (string, error) {
- return parseCAP(p.Status.CapEff)
- }
- // processCAPBND returns the set of bounding capabilities associated with
- // process p. If all capabilities are set, "full" is returned. If no
- // capability is enabled, "none" is returned.
- func processCAPBND(p *process.Process, _ *psContext) (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, _ *psContext) (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, _ *psContext) (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, ctx *psContext) (string, error) {
- if hp := findHostProcess(p, ctx); 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, ctx *psContext) (string, error) {
- if hp := findHostProcess(p, ctx); hp != nil {
- if ctx.opts != nil && len(ctx.opts.UIDMap) > 0 {
- return findID(
- hp.Status.Uids[1],
- ctx.opts.UIDMap,
- process.LookupUID,
- "/proc/sys/fs/overflowuid",
- )
- }
- 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, ctx *psContext) (string, error) {
- if hp := findHostProcess(p, ctx); hp != nil {
- if ctx.opts != nil && len(ctx.opts.GIDMap) > 0 {
- return findID(
- hp.Status.Gids[1],
- ctx.opts.GIDMap,
- process.LookupGID,
- "/proc/sys/fs/overflowgid",
- )
- }
- return hp.Hgroup, nil
- }
- return "?", nil
- }
- // processRSS returns the resident set size of process p in KiB (1024-byte
- // units).
- func processRSS(p *process.Process, _ *psContext) (string, error) {
- if p.Status.VMRSS == "" {
- // probably a kernel thread
- return "0", nil
- }
- return p.Status.VMRSS, nil
- }
- // processState returns the process state of process p.
- func processState(p *process.Process, _ *psContext) (string, error) {
- return p.Status.State, nil
- }
|