diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2021-08-29 15:09:59 -0700 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2021-08-29 15:42:17 -0700 |
commit | b972c50c0989a81da308886e5d602c272e90f8cb (patch) | |
tree | 4c6e9b3560b6c555776dd127735d749322a47b94 | |
parent | 61b2fcc4510641ffd691d8e5a82e968b458f0cb9 (diff) | |
download | libcap2-b972c50c0989a81da308886e5d602c272e90f8cb.tar.gz |
Add captree command line options and support process by name.
Add some features to captree. I plan to post a companion article
here:
https://sites.google.com/site/fullycapable/captree
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r-- | doc/Makefile | 2 | ||||
-rw-r--r-- | doc/captree.8 | 63 | ||||
-rw-r--r-- | goapps/captree/captree.go | 61 |
3 files changed, 117 insertions, 9 deletions
diff --git a/doc/Makefile b/doc/Makefile index 943dbfa..e2802dc 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -26,7 +26,7 @@ MAN3S = cap_init.3 cap_free.3 cap_dup.3 \ cap_iab_to_text.3 cap_iab_from_text.3 cap_iab_get_vector.3 \ cap_iab_set_vector.3 cap_iab_fill.3 \ psx_syscall.3 psx_syscall3.3 psx_syscall6.3 libpsx.3 -MAN8S = getcap.8 setcap.8 getpcaps.8 +MAN8S = getcap.8 setcap.8 getpcaps.8 captree.8 MANS = $(MAN1S) $(MAN3S) $(MAN8S) diff --git a/doc/captree.8 b/doc/captree.8 new file mode 100644 index 0000000..700610f --- /dev/null +++ b/doc/captree.8 @@ -0,0 +1,63 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH CAPTREE 8 "2021-08-29" +.\" Please adjust this date whenever revising the manpage. +.SH NAME +captree \- display process tree capabilities +.SH SYNOPSIS +.BR captree " [optional args] " +.IR [pid|glob-name ... ] +.SH DESCRIPTION +.B captree +displays the capabilities on the mentioned processes indicated by +.IR pid or glob-name +value(s) given on the command line. If no +.I pid +etc values are supplied, +.IR pid =1 +is implied. A +.I pid +value of 0 displays all the processes known to the kernel. +.PP +The POSIX.1e capabilities are displayed in double quotes in the +.BR cap_from_text (3) +format. The IAB tuple of capabilities is displayed between square +brackets in the text format described in +.BR cap_iab (3). +Note, the IAB tuple text is omitted if it contains empty A and B +components. This is because the regular POSIX.1e text contains +information about the Inheritable flag already. This behavior can be +overridden with the +.B --verbose +command line argument. +.PP +Optional arguments (which must precede the list of pid|glob-name +values): +.TP +.B \-\-help +Displays usage information and exits. +.TP +.BR \-\-verbose +Displays capability sets and IAB tuples even when they are empty, or +redundant. +.TP +.BI \-\-depth =n +Displays the process tree to a depth of +.IR n . +Note, the default value for this parameter is 0, which implies +infinite depth. +.SH REPORTING BUGS +Please report bugs via: +.TP +https://bugzilla.kernel.org/buglist.cgi?component=libcap&list_id=1090757 +.SH SEE ALSO +.BR cap_from_text(3), +.BR capabilities (7), +and +.BR cap_iab (3). + +There is a longer article about \fBcaptree\fP, which includes some +examples, here: + + https://sites.google.com/site/fullycapable/captree +.SH AUTHOR +Andrew G. Morgan <morgan@kernel.org> diff --git a/goapps/captree/captree.go b/goapps/captree/captree.go index aa94cd3..4c7a586 100644 --- a/goapps/captree/captree.go +++ b/goapps/captree/captree.go @@ -58,6 +58,12 @@ // // $ captree 0 // +// To view a specific binary (as named in /proc/<PID>/status as 'Name: +// ...'), matched by a glob, try this: +// +// $ captree 'cap*ree' +// +// The quotes might be needed to avoid the '*' confusing your shell. package main import ( @@ -65,6 +71,7 @@ import ( "fmt" "io/ioutil" "log" + "path/filepath" "sort" "strconv" "strings" @@ -74,7 +81,9 @@ import ( ) var ( - proc = flag.String("proc", "/proc", "root of proc filesystem") + proc = flag.String("proc", "/proc", "root of proc filesystem") + depth = flag.Int("depth", 0, "how many processes deep (0=all)") + verbose = flag.Bool("verbose", false, "display empty capabilities") ) type task struct { @@ -168,7 +177,7 @@ var empty = cap.NewSet() var noiab = cap.IABInit() // rDump prints out the tree of processes rooted at pid. -func rDump(pids map[string]*task, pid, stub, lstub, estub string) { +func rDump(pids map[string]*task, pid, stub, lstub, estub string, depth int) { info, ok := pids[pid] if !ok { fmt.Println("[PID:", pid, "not found]") @@ -177,14 +186,14 @@ func rDump(pids map[string]*task, pid, stub, lstub, estub string) { c := "" set := info.cap if set != nil { - if val, _ := set.Cf(empty); val != 0 { + if val, _ := set.Cf(empty); val != 0 || *verbose { c = fmt.Sprintf(" %q", set) } } iab := "" tup := info.iab if tup != nil { - if val, _ := tup.Cf(noiab); val.Has(cap.Bound) || val.Has(cap.Amb) { + if val, _ := tup.Cf(noiab); val.Has(cap.Bound) || val.Has(cap.Amb) || *verbose { iab = fmt.Sprintf(" [%s]", tup) } } @@ -233,20 +242,26 @@ func rDump(pids map[string]*task, pid, stub, lstub, estub string) { c := "" set := this.cap if set != nil { - if val, _ := set.Cf(empty); val != 0 { + if val, _ := set.Cf(empty); val != 0 || *verbose { c = fmt.Sprintf(" %q", set) } } iab := "" tup := this.iab if tup != nil { - if val, _ := tup.Cf(noiab); val.Has(cap.Bound) || val.Has(cap.Amb) { + if val, _ := tup.Cf(noiab); val.Has(cap.Bound) || val.Has(cap.Amb) || *verbose { iab = fmt.Sprintf(" [%s]", tup) } } fmt.Printf("%s%s:>-%s{%s}%s%s\n", stub, estub, this.cmd, strings.Join(same, ","), c, iab) misc = nmisc } + if depth == 1 { + return + } + if depth > 1 { + depth-- + } x := info.children sort.Slice(x, func(i, j int) bool { a, _ := strconv.Atoi(x[i]) @@ -260,10 +275,34 @@ func rDump(pids map[string]*task, pid, stub, lstub, estub string) { if i+1 == len(x) { estub = " " } - rDump(pids, cid, stub, lstub, estub) + rDump(pids, cid, stub, lstub, estub, depth) } } +func findPIDs(list []string, pids map[string]*task, glob string) <-chan string { + finds := make(chan string) + go func() { + defer close(finds) + found := false + // search for PIDs, if found exit. + for _, pid := range list { + match, _ := filepath.Match(glob, pids[pid].cmd) + if !match { + continue + } + found = true + finds <- pid + } + if found { + return + } + // TODO if no processes found, should we search the + // threads? + fmt.Printf("no process matched %q\n", glob) + }() + return finds +} + func main() { flag.Parse() @@ -318,6 +357,12 @@ func main() { } for _, pid := range args { - rDump(pids, pid, "", "--", " ") + if _, err := strconv.ParseUint(pid, 10, 64); err == nil { + rDump(pids, pid, "", "--", " ", *depth) + continue + } + for pid := range findPIDs(list, pids, pid) { + rDump(pids, pid, "", "--", " ", *depth) + } } } |