summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-08-29 15:09:59 -0700
committerAndrew G. Morgan <morgan@kernel.org>2021-08-29 15:42:17 -0700
commitb972c50c0989a81da308886e5d602c272e90f8cb (patch)
tree4c6e9b3560b6c555776dd127735d749322a47b94
parent61b2fcc4510641ffd691d8e5a82e968b458f0cb9 (diff)
downloadlibcap2-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/Makefile2
-rw-r--r--doc/captree.863
-rw-r--r--goapps/captree/captree.go61
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)
+ }
}
}