xref: /aosp_15_r20/external/libcap/goapps/captree/captree.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1*2810ac1bSKiyoung Kim// Program captree explores a process tree rooted in the supplied
2*2810ac1bSKiyoung Kim// argument(s) and displays a process tree indicating the capabilities
3*2810ac1bSKiyoung Kim// of all the dependent PID values.
4*2810ac1bSKiyoung Kim//
5*2810ac1bSKiyoung Kim// This was inspired by the pstree utility. The key idea here, however,
6*2810ac1bSKiyoung Kim// is to explore a process tree for capability state.
7*2810ac1bSKiyoung Kim//
8*2810ac1bSKiyoung Kim// Each line of output is intended to capture a brief representation
9*2810ac1bSKiyoung Kim// of the capability state of a process (both *Set and *IAB) and
10*2810ac1bSKiyoung Kim// for its related threads.
11*2810ac1bSKiyoung Kim//
12*2810ac1bSKiyoung Kim// Ex:
13*2810ac1bSKiyoung Kim//
14*2810ac1bSKiyoung Kim//   $ bash -c 'exec captree $$'
15*2810ac1bSKiyoung Kim//   --captree(9758+{9759,9760,9761,9762})
16*2810ac1bSKiyoung Kim//
17*2810ac1bSKiyoung Kim// In the normal case, such as the above, where the targeted process
18*2810ac1bSKiyoung Kim// is not privileged, no distracting capability strings are displayed.
19*2810ac1bSKiyoung Kim// Where a process is thread group leader to a set of other thread
20*2810ac1bSKiyoung Kim// ids, they are listed as `+{...}`.
21*2810ac1bSKiyoung Kim//
22*2810ac1bSKiyoung Kim// For privileged binaries, we have:
23*2810ac1bSKiyoung Kim//
24*2810ac1bSKiyoung Kim//   $ captree 551
25*2810ac1bSKiyoung Kim//   --polkitd(551) "=ep"
26*2810ac1bSKiyoung Kim//     :>-gmain{552} "=ep"
27*2810ac1bSKiyoung Kim//     :>-gdbus{555} "=ep"
28*2810ac1bSKiyoung Kim//
29*2810ac1bSKiyoung Kim// That is, the text representation of the process capability state is
30*2810ac1bSKiyoung Kim// displayed in double quotes "..." as a suffix to the process/thread.
31*2810ac1bSKiyoung Kim// If the name of any thread of this process, or its own capability
32*2810ac1bSKiyoung Kim// state, is in some way different from the primary process then it is
33*2810ac1bSKiyoung Kim// displayed on a subsequent line prefixed with ":>-" and threads
34*2810ac1bSKiyoung Kim// sharing name and capability state are listed on that line. Here we
35*2810ac1bSKiyoung Kim// have two sub-threads with the same capability state, but unique
36*2810ac1bSKiyoung Kim// names.
37*2810ac1bSKiyoung Kim//
38*2810ac1bSKiyoung Kim// Sometimes members of a process group have different capabilities:
39*2810ac1bSKiyoung Kim//
40*2810ac1bSKiyoung Kim//   $ captree 1368
41*2810ac1bSKiyoung Kim//   --dnsmasq(1368) "cap_net_bind_service,cap_net_admin,cap_net_raw=ep"
42*2810ac1bSKiyoung Kim//     +-dnsmasq(1369) "=ep"
43*2810ac1bSKiyoung Kim//
44*2810ac1bSKiyoung Kim// Where the A and B components of the IAB tuple are non-default, the
45*2810ac1bSKiyoung Kim// output also includes these:
46*2810ac1bSKiyoung Kim//
47*2810ac1bSKiyoung Kim//   $ captree 925
48*2810ac1bSKiyoung Kim//   --dbus-broker-lau(925) [!cap_sys_rawio,!cap_mknod]
49*2810ac1bSKiyoung Kim//     +-dbus-broker(965) "cap_audit_write=eip" [!cap_sys_rawio,!cap_mknod,cap_audit_write]
50*2810ac1bSKiyoung Kim//
51*2810ac1bSKiyoung Kim// That is, the `[...]` appendage captures the IAB text representation
52*2810ac1bSKiyoung Kim// of that tuple. Note, if only the I part of that tuple is
53*2810ac1bSKiyoung Kim// non-default, it is already captured in the quoted process
54*2810ac1bSKiyoung Kim// capability state, so the IAB tuple is omitted.
55*2810ac1bSKiyoung Kim//
56*2810ac1bSKiyoung Kim// To view the complete system process map, rooted at the kernel, try
57*2810ac1bSKiyoung Kim// this:
58*2810ac1bSKiyoung Kim//
59*2810ac1bSKiyoung Kim//   $ captree 0
60*2810ac1bSKiyoung Kim//
61*2810ac1bSKiyoung Kim// To view a specific binary (as named in /proc/<PID>/status as 'Name:
62*2810ac1bSKiyoung Kim// ...'), matched by a glob, try this:
63*2810ac1bSKiyoung Kim//
64*2810ac1bSKiyoung Kim//   $ captree 'cap*ree'
65*2810ac1bSKiyoung Kim//
66*2810ac1bSKiyoung Kim// The quotes might be needed to avoid the '*' confusing your shell.
67*2810ac1bSKiyoung Kimpackage main
68*2810ac1bSKiyoung Kim
69*2810ac1bSKiyoung Kimimport (
70*2810ac1bSKiyoung Kim	"flag"
71*2810ac1bSKiyoung Kim	"fmt"
72*2810ac1bSKiyoung Kim	"io/ioutil"
73*2810ac1bSKiyoung Kim	"log"
74*2810ac1bSKiyoung Kim	"os"
75*2810ac1bSKiyoung Kim	"path/filepath"
76*2810ac1bSKiyoung Kim	"sort"
77*2810ac1bSKiyoung Kim	"strconv"
78*2810ac1bSKiyoung Kim	"strings"
79*2810ac1bSKiyoung Kim	"sync"
80*2810ac1bSKiyoung Kim
81*2810ac1bSKiyoung Kim	"kernel.org/pub/linux/libs/security/libcap/cap"
82*2810ac1bSKiyoung Kim)
83*2810ac1bSKiyoung Kim
84*2810ac1bSKiyoung Kimvar (
85*2810ac1bSKiyoung Kim	proc    = flag.String("proc", "/proc", "root of proc filesystem")
86*2810ac1bSKiyoung Kim	depth   = flag.Int("depth", 0, "how many processes deep (0=all)")
87*2810ac1bSKiyoung Kim	verbose = flag.Bool("verbose", false, "display empty capabilities")
88*2810ac1bSKiyoung Kim	color   = flag.Bool("color", true, "color targeted PIDs on tty in red")
89*2810ac1bSKiyoung Kim	colour  = flag.Bool("colour", true, "colour targeted PIDs on tty in red")
90*2810ac1bSKiyoung Kim)
91*2810ac1bSKiyoung Kim
92*2810ac1bSKiyoung Kimtype task struct {
93*2810ac1bSKiyoung Kim	mu       sync.Mutex
94*2810ac1bSKiyoung Kim	viewed   bool
95*2810ac1bSKiyoung Kim	depth    int
96*2810ac1bSKiyoung Kim	pid      string
97*2810ac1bSKiyoung Kim	cmd      string
98*2810ac1bSKiyoung Kim	cap      *cap.Set
99*2810ac1bSKiyoung Kim	iab      *cap.IAB
100*2810ac1bSKiyoung Kim	parent   string
101*2810ac1bSKiyoung Kim	threads  []*task
102*2810ac1bSKiyoung Kim	children []string
103*2810ac1bSKiyoung Kim}
104*2810ac1bSKiyoung Kim
105*2810ac1bSKiyoung Kimfunc (ts *task) String() string {
106*2810ac1bSKiyoung Kim	return fmt.Sprintf("%s %q [%v] %s %v %v", ts.cmd, ts.cap, ts.iab, ts.parent, ts.threads, ts.children)
107*2810ac1bSKiyoung Kim}
108*2810ac1bSKiyoung Kim
109*2810ac1bSKiyoung Kimvar (
110*2810ac1bSKiyoung Kim	wg      sync.WaitGroup
111*2810ac1bSKiyoung Kim	mu      sync.Mutex
112*2810ac1bSKiyoung Kim	colored bool
113*2810ac1bSKiyoung Kim)
114*2810ac1bSKiyoung Kim
115*2810ac1bSKiyoung Kimfunc isATTY() bool {
116*2810ac1bSKiyoung Kim	s, err := os.Stdout.Stat()
117*2810ac1bSKiyoung Kim	if err == nil && (s.Mode()&os.ModeCharDevice) != 0 {
118*2810ac1bSKiyoung Kim		return true
119*2810ac1bSKiyoung Kim	}
120*2810ac1bSKiyoung Kim	return false
121*2810ac1bSKiyoung Kim}
122*2810ac1bSKiyoung Kim
123*2810ac1bSKiyoung Kimfunc highlight(text string) string {
124*2810ac1bSKiyoung Kim	if colored {
125*2810ac1bSKiyoung Kim		return fmt.Sprint("\033[31m", text, "\033[0m")
126*2810ac1bSKiyoung Kim	}
127*2810ac1bSKiyoung Kim	return text
128*2810ac1bSKiyoung Kim}
129*2810ac1bSKiyoung Kim
130*2810ac1bSKiyoung Kimfunc (ts *task) fill(pid string, n int, thread bool) {
131*2810ac1bSKiyoung Kim	defer wg.Done()
132*2810ac1bSKiyoung Kim	wg.Add(1)
133*2810ac1bSKiyoung Kim	go func() {
134*2810ac1bSKiyoung Kim		defer wg.Done()
135*2810ac1bSKiyoung Kim		c, _ := cap.GetPID(n)
136*2810ac1bSKiyoung Kim		iab, _ := cap.IABGetPID(n)
137*2810ac1bSKiyoung Kim		ts.mu.Lock()
138*2810ac1bSKiyoung Kim		defer ts.mu.Unlock()
139*2810ac1bSKiyoung Kim		ts.pid = pid
140*2810ac1bSKiyoung Kim		ts.cap = c
141*2810ac1bSKiyoung Kim		ts.iab = iab
142*2810ac1bSKiyoung Kim	}()
143*2810ac1bSKiyoung Kim
144*2810ac1bSKiyoung Kim	d, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/status", *proc, pid))
145*2810ac1bSKiyoung Kim	if err != nil {
146*2810ac1bSKiyoung Kim		ts.mu.Lock()
147*2810ac1bSKiyoung Kim		defer ts.mu.Unlock()
148*2810ac1bSKiyoung Kim		ts.cmd = "<zombie>"
149*2810ac1bSKiyoung Kim		ts.parent = "1"
150*2810ac1bSKiyoung Kim		return
151*2810ac1bSKiyoung Kim	}
152*2810ac1bSKiyoung Kim	for _, line := range strings.Split(string(d), "\n") {
153*2810ac1bSKiyoung Kim		if strings.HasPrefix(line, "Name:\t") {
154*2810ac1bSKiyoung Kim			ts.mu.Lock()
155*2810ac1bSKiyoung Kim			ts.cmd = line[6:]
156*2810ac1bSKiyoung Kim			ts.mu.Unlock()
157*2810ac1bSKiyoung Kim			continue
158*2810ac1bSKiyoung Kim		}
159*2810ac1bSKiyoung Kim		if strings.HasPrefix(line, "PPid:\t") {
160*2810ac1bSKiyoung Kim			ppid := line[6:]
161*2810ac1bSKiyoung Kim			if ppid == pid {
162*2810ac1bSKiyoung Kim				continue
163*2810ac1bSKiyoung Kim			}
164*2810ac1bSKiyoung Kim			ts.mu.Lock()
165*2810ac1bSKiyoung Kim			ts.parent = ppid
166*2810ac1bSKiyoung Kim			ts.mu.Unlock()
167*2810ac1bSKiyoung Kim		}
168*2810ac1bSKiyoung Kim	}
169*2810ac1bSKiyoung Kim	if thread {
170*2810ac1bSKiyoung Kim		return
171*2810ac1bSKiyoung Kim	}
172*2810ac1bSKiyoung Kim
173*2810ac1bSKiyoung Kim	threads, err := ioutil.ReadDir(fmt.Sprintf("%s/%s/task", *proc, pid))
174*2810ac1bSKiyoung Kim	if err != nil {
175*2810ac1bSKiyoung Kim		return
176*2810ac1bSKiyoung Kim	}
177*2810ac1bSKiyoung Kim	var ths []*task
178*2810ac1bSKiyoung Kim	for _, t := range threads {
179*2810ac1bSKiyoung Kim		tid := t.Name()
180*2810ac1bSKiyoung Kim		if tid == pid {
181*2810ac1bSKiyoung Kim			continue
182*2810ac1bSKiyoung Kim		}
183*2810ac1bSKiyoung Kim		n, err := strconv.ParseInt(pid, 10, 64)
184*2810ac1bSKiyoung Kim		if err != nil {
185*2810ac1bSKiyoung Kim			continue
186*2810ac1bSKiyoung Kim		}
187*2810ac1bSKiyoung Kim		thread := &task{}
188*2810ac1bSKiyoung Kim		wg.Add(1)
189*2810ac1bSKiyoung Kim		go thread.fill(tid, int(n), true)
190*2810ac1bSKiyoung Kim		ths = append(ths, thread)
191*2810ac1bSKiyoung Kim	}
192*2810ac1bSKiyoung Kim	ts.mu.Lock()
193*2810ac1bSKiyoung Kim	defer ts.mu.Unlock()
194*2810ac1bSKiyoung Kim	ts.threads = ths
195*2810ac1bSKiyoung Kim}
196*2810ac1bSKiyoung Kim
197*2810ac1bSKiyoung Kimvar empty = cap.NewSet()
198*2810ac1bSKiyoung Kimvar noiab = cap.IABInit()
199*2810ac1bSKiyoung Kim
200*2810ac1bSKiyoung Kim// rDump prints out the tree of processes rooted at pid.
201*2810ac1bSKiyoung Kimfunc rDump(pids map[string]*task, requested map[string]bool, pid, stub, lstub, estub string, depth int) {
202*2810ac1bSKiyoung Kim	info, ok := pids[pid]
203*2810ac1bSKiyoung Kim	if !ok {
204*2810ac1bSKiyoung Kim		panic("programming error")
205*2810ac1bSKiyoung Kim		return
206*2810ac1bSKiyoung Kim	}
207*2810ac1bSKiyoung Kim	if info.viewed {
208*2810ac1bSKiyoung Kim		// This process (tree) has already been viewed so skip
209*2810ac1bSKiyoung Kim		// repeating it.
210*2810ac1bSKiyoung Kim		return
211*2810ac1bSKiyoung Kim	}
212*2810ac1bSKiyoung Kim	info.viewed = true
213*2810ac1bSKiyoung Kim
214*2810ac1bSKiyoung Kim	c := ""
215*2810ac1bSKiyoung Kim	set := info.cap
216*2810ac1bSKiyoung Kim	if set != nil {
217*2810ac1bSKiyoung Kim		if val, _ := set.Cf(empty); val != 0 || *verbose {
218*2810ac1bSKiyoung Kim			c = fmt.Sprintf(" %q", set)
219*2810ac1bSKiyoung Kim		}
220*2810ac1bSKiyoung Kim	}
221*2810ac1bSKiyoung Kim	iab := ""
222*2810ac1bSKiyoung Kim	tup := info.iab
223*2810ac1bSKiyoung Kim	if tup != nil {
224*2810ac1bSKiyoung Kim		if val, _ := tup.Cf(noiab); val.Has(cap.Bound) || val.Has(cap.Amb) || *verbose {
225*2810ac1bSKiyoung Kim			iab = fmt.Sprintf(" [%s]", tup)
226*2810ac1bSKiyoung Kim		}
227*2810ac1bSKiyoung Kim	}
228*2810ac1bSKiyoung Kim	var misc []*task
229*2810ac1bSKiyoung Kim	var same []string
230*2810ac1bSKiyoung Kim	for _, t := range info.threads {
231*2810ac1bSKiyoung Kim		if val, _ := t.cap.Cf(set); val != 0 {
232*2810ac1bSKiyoung Kim			misc = append(misc, t)
233*2810ac1bSKiyoung Kim			continue
234*2810ac1bSKiyoung Kim		}
235*2810ac1bSKiyoung Kim		if val, _ := t.iab.Cf(tup); val != 0 {
236*2810ac1bSKiyoung Kim			misc = append(misc, t)
237*2810ac1bSKiyoung Kim			continue
238*2810ac1bSKiyoung Kim		}
239*2810ac1bSKiyoung Kim		if t.cmd != info.cmd {
240*2810ac1bSKiyoung Kim			misc = append(misc, t)
241*2810ac1bSKiyoung Kim			continue
242*2810ac1bSKiyoung Kim		}
243*2810ac1bSKiyoung Kim		same = append(same, t.pid)
244*2810ac1bSKiyoung Kim	}
245*2810ac1bSKiyoung Kim	tids := ""
246*2810ac1bSKiyoung Kim	if len(same) != 0 {
247*2810ac1bSKiyoung Kim		tids = fmt.Sprintf("+{%s}", strings.Join(same, ","))
248*2810ac1bSKiyoung Kim	}
249*2810ac1bSKiyoung Kim	hPID := pid
250*2810ac1bSKiyoung Kim	if requested[pid] {
251*2810ac1bSKiyoung Kim		hPID = highlight(pid)
252*2810ac1bSKiyoung Kim		requested[pid] = false
253*2810ac1bSKiyoung Kim	}
254*2810ac1bSKiyoung Kim	fmt.Printf("%s%s%s(%s%s)%s%s\n", stub, lstub, info.cmd, hPID, tids, c, iab)
255*2810ac1bSKiyoung Kim	// loop over any threads that differ in capability state.
256*2810ac1bSKiyoung Kim	for len(misc) != 0 {
257*2810ac1bSKiyoung Kim		this := misc[0]
258*2810ac1bSKiyoung Kim		var nmisc []*task
259*2810ac1bSKiyoung Kim		var hPID = this.pid
260*2810ac1bSKiyoung Kim		if requested[this.pid] {
261*2810ac1bSKiyoung Kim			hPID = highlight(this.pid)
262*2810ac1bSKiyoung Kim			requested[this.pid] = false
263*2810ac1bSKiyoung Kim		}
264*2810ac1bSKiyoung Kim		same := []string{hPID}
265*2810ac1bSKiyoung Kim		for _, t := range misc[1:] {
266*2810ac1bSKiyoung Kim			if val, _ := this.cap.Cf(t.cap); val != 0 {
267*2810ac1bSKiyoung Kim				nmisc = append(nmisc, t)
268*2810ac1bSKiyoung Kim				continue
269*2810ac1bSKiyoung Kim			}
270*2810ac1bSKiyoung Kim			if val, _ := this.iab.Cf(t.iab); val != 0 {
271*2810ac1bSKiyoung Kim				nmisc = append(nmisc, t)
272*2810ac1bSKiyoung Kim				continue
273*2810ac1bSKiyoung Kim			}
274*2810ac1bSKiyoung Kim			if this.cmd != t.cmd {
275*2810ac1bSKiyoung Kim				nmisc = append(nmisc, t)
276*2810ac1bSKiyoung Kim				continue
277*2810ac1bSKiyoung Kim			}
278*2810ac1bSKiyoung Kim			hPID = t.pid
279*2810ac1bSKiyoung Kim			if requested[t.pid] {
280*2810ac1bSKiyoung Kim				hPID = highlight(t.pid)
281*2810ac1bSKiyoung Kim				requested[t.pid] = false
282*2810ac1bSKiyoung Kim			}
283*2810ac1bSKiyoung Kim			same = append(same, hPID)
284*2810ac1bSKiyoung Kim		}
285*2810ac1bSKiyoung Kim		c := ""
286*2810ac1bSKiyoung Kim		set := this.cap
287*2810ac1bSKiyoung Kim		if set != nil {
288*2810ac1bSKiyoung Kim			if val, _ := set.Cf(empty); val != 0 || *verbose {
289*2810ac1bSKiyoung Kim				c = fmt.Sprintf(" %q", set)
290*2810ac1bSKiyoung Kim			}
291*2810ac1bSKiyoung Kim		}
292*2810ac1bSKiyoung Kim		iab := ""
293*2810ac1bSKiyoung Kim		tup := this.iab
294*2810ac1bSKiyoung Kim		if tup != nil {
295*2810ac1bSKiyoung Kim			if val, _ := tup.Cf(noiab); val.Has(cap.Bound) || val.Has(cap.Amb) || *verbose {
296*2810ac1bSKiyoung Kim				iab = fmt.Sprintf(" [%s]", tup)
297*2810ac1bSKiyoung Kim			}
298*2810ac1bSKiyoung Kim		}
299*2810ac1bSKiyoung Kim		fmt.Printf("%s%s:>-%s{%s}%s%s\n", stub, estub, this.cmd, strings.Join(same, ","), c, iab)
300*2810ac1bSKiyoung Kim		misc = nmisc
301*2810ac1bSKiyoung Kim	}
302*2810ac1bSKiyoung Kim	if depth == 1 {
303*2810ac1bSKiyoung Kim		return
304*2810ac1bSKiyoung Kim	}
305*2810ac1bSKiyoung Kim	if depth > 1 {
306*2810ac1bSKiyoung Kim		depth--
307*2810ac1bSKiyoung Kim	}
308*2810ac1bSKiyoung Kim	x := info.children
309*2810ac1bSKiyoung Kim	sort.Slice(x, func(i, j int) bool {
310*2810ac1bSKiyoung Kim		a, _ := strconv.Atoi(x[i])
311*2810ac1bSKiyoung Kim		b, _ := strconv.Atoi(x[j])
312*2810ac1bSKiyoung Kim		return a < b
313*2810ac1bSKiyoung Kim	})
314*2810ac1bSKiyoung Kim	stub = fmt.Sprintf("%s%s", stub, estub)
315*2810ac1bSKiyoung Kim	lstub = "+-"
316*2810ac1bSKiyoung Kim	for i, cid := range x {
317*2810ac1bSKiyoung Kim		estub := "| "
318*2810ac1bSKiyoung Kim		if i+1 == len(x) {
319*2810ac1bSKiyoung Kim			estub = "  "
320*2810ac1bSKiyoung Kim		}
321*2810ac1bSKiyoung Kim		rDump(pids, requested, cid, stub, lstub, estub, depth)
322*2810ac1bSKiyoung Kim	}
323*2810ac1bSKiyoung Kim}
324*2810ac1bSKiyoung Kim
325*2810ac1bSKiyoung Kimfunc findPIDs(list []string, pids map[string]*task, glob string) <-chan string {
326*2810ac1bSKiyoung Kim	finds := make(chan string)
327*2810ac1bSKiyoung Kim	go func() {
328*2810ac1bSKiyoung Kim		defer close(finds)
329*2810ac1bSKiyoung Kim		found := false
330*2810ac1bSKiyoung Kim		// search for PIDs, if found exit.
331*2810ac1bSKiyoung Kim		for _, pid := range list {
332*2810ac1bSKiyoung Kim			match, _ := filepath.Match(glob, pids[pid].cmd)
333*2810ac1bSKiyoung Kim			if !match {
334*2810ac1bSKiyoung Kim				continue
335*2810ac1bSKiyoung Kim			}
336*2810ac1bSKiyoung Kim			found = true
337*2810ac1bSKiyoung Kim			finds <- pid
338*2810ac1bSKiyoung Kim		}
339*2810ac1bSKiyoung Kim		if found {
340*2810ac1bSKiyoung Kim			return
341*2810ac1bSKiyoung Kim		}
342*2810ac1bSKiyoung Kim		fmt.Printf("no process matched %q\n", glob)
343*2810ac1bSKiyoung Kim		os.Exit(1)
344*2810ac1bSKiyoung Kim	}()
345*2810ac1bSKiyoung Kim	return finds
346*2810ac1bSKiyoung Kim}
347*2810ac1bSKiyoung Kim
348*2810ac1bSKiyoung Kimfunc setDepth(pids map[string]*task, pid string) int {
349*2810ac1bSKiyoung Kim	if pid == "0" {
350*2810ac1bSKiyoung Kim		return 0
351*2810ac1bSKiyoung Kim	}
352*2810ac1bSKiyoung Kim	x := pids[pid]
353*2810ac1bSKiyoung Kim	if x.depth == 0 {
354*2810ac1bSKiyoung Kim		x.depth = setDepth(pids, x.parent) + 1
355*2810ac1bSKiyoung Kim	}
356*2810ac1bSKiyoung Kim	return x.depth
357*2810ac1bSKiyoung Kim}
358*2810ac1bSKiyoung Kim
359*2810ac1bSKiyoung Kimfunc main() {
360*2810ac1bSKiyoung Kim	flag.Usage = func() {
361*2810ac1bSKiyoung Kim		fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] [pid|glob] ...\nOptions:\n", os.Args[0])
362*2810ac1bSKiyoung Kim		flag.PrintDefaults()
363*2810ac1bSKiyoung Kim	}
364*2810ac1bSKiyoung Kim	flag.Parse()
365*2810ac1bSKiyoung Kim
366*2810ac1bSKiyoung Kim	// Honor the command line request if possible.
367*2810ac1bSKiyoung Kim	colored = *color && *colour && isATTY()
368*2810ac1bSKiyoung Kim
369*2810ac1bSKiyoung Kim	// Just in case the user wants to override this, we set the
370*2810ac1bSKiyoung Kim	// cap package up to find it.
371*2810ac1bSKiyoung Kim	cap.ProcRoot(*proc)
372*2810ac1bSKiyoung Kim
373*2810ac1bSKiyoung Kim	pids := make(map[string]*task)
374*2810ac1bSKiyoung Kim	pids["0"] = &task{
375*2810ac1bSKiyoung Kim		cmd: "<kernel>",
376*2810ac1bSKiyoung Kim	}
377*2810ac1bSKiyoung Kim
378*2810ac1bSKiyoung Kim	// Ingest the entire process tree
379*2810ac1bSKiyoung Kim	fs, err := ioutil.ReadDir(*proc)
380*2810ac1bSKiyoung Kim	if err != nil {
381*2810ac1bSKiyoung Kim		log.Fatalf("unable to open %q: %v", *proc, err)
382*2810ac1bSKiyoung Kim	}
383*2810ac1bSKiyoung Kim	for _, f := range fs {
384*2810ac1bSKiyoung Kim		pid := f.Name()
385*2810ac1bSKiyoung Kim		n, err := strconv.ParseInt(pid, 10, 64)
386*2810ac1bSKiyoung Kim		if err != nil {
387*2810ac1bSKiyoung Kim			continue
388*2810ac1bSKiyoung Kim		}
389*2810ac1bSKiyoung Kim		ts := &task{}
390*2810ac1bSKiyoung Kim		mu.Lock()
391*2810ac1bSKiyoung Kim		pids[pid] = ts
392*2810ac1bSKiyoung Kim		mu.Unlock()
393*2810ac1bSKiyoung Kim		wg.Add(1)
394*2810ac1bSKiyoung Kim		go ts.fill(pid, int(n), false)
395*2810ac1bSKiyoung Kim	}
396*2810ac1bSKiyoung Kim	wg.Wait()
397*2810ac1bSKiyoung Kim
398*2810ac1bSKiyoung Kim	var list []string
399*2810ac1bSKiyoung Kim	for pid, ts := range pids {
400*2810ac1bSKiyoung Kim		setDepth(pids, pid)
401*2810ac1bSKiyoung Kim		list = append(list, pid)
402*2810ac1bSKiyoung Kim		if pid == "0" {
403*2810ac1bSKiyoung Kim			continue
404*2810ac1bSKiyoung Kim		}
405*2810ac1bSKiyoung Kim		if pts, ok := pids[ts.parent]; ok {
406*2810ac1bSKiyoung Kim			pts.children = append(pts.children, pid)
407*2810ac1bSKiyoung Kim		}
408*2810ac1bSKiyoung Kim	}
409*2810ac1bSKiyoung Kim
410*2810ac1bSKiyoung Kim	// Sort the process tree by tree depth - shallowest first,
411*2810ac1bSKiyoung Kim	// with numerical order breaking ties.
412*2810ac1bSKiyoung Kim	sort.Slice(list, func(i, j int) bool {
413*2810ac1bSKiyoung Kim		x, y := pids[list[i]], pids[list[j]]
414*2810ac1bSKiyoung Kim		if x.depth == y.depth {
415*2810ac1bSKiyoung Kim			a, _ := strconv.Atoi(x.pid)
416*2810ac1bSKiyoung Kim			b, _ := strconv.Atoi(y.pid)
417*2810ac1bSKiyoung Kim			return a < b
418*2810ac1bSKiyoung Kim		}
419*2810ac1bSKiyoung Kim		return x.depth < y.depth
420*2810ac1bSKiyoung Kim	})
421*2810ac1bSKiyoung Kim
422*2810ac1bSKiyoung Kim	args := flag.Args()
423*2810ac1bSKiyoung Kim	if len(args) == 0 {
424*2810ac1bSKiyoung Kim		args = []string{"1"}
425*2810ac1bSKiyoung Kim	}
426*2810ac1bSKiyoung Kim
427*2810ac1bSKiyoung Kim	wanted := make(map[string]int)
428*2810ac1bSKiyoung Kim	requested := make(map[string]bool)
429*2810ac1bSKiyoung Kim	for _, pid := range args {
430*2810ac1bSKiyoung Kim		if _, err := strconv.ParseUint(pid, 10, 64); err == nil {
431*2810ac1bSKiyoung Kim			requested[pid] = true
432*2810ac1bSKiyoung Kim			if info, ok := pids[pid]; ok {
433*2810ac1bSKiyoung Kim				wanted[pid] = info.depth
434*2810ac1bSKiyoung Kim				continue
435*2810ac1bSKiyoung Kim			}
436*2810ac1bSKiyoung Kim			if requested[pid] {
437*2810ac1bSKiyoung Kim				continue
438*2810ac1bSKiyoung Kim			}
439*2810ac1bSKiyoung Kim			requested[pid] = true
440*2810ac1bSKiyoung Kim			continue
441*2810ac1bSKiyoung Kim		}
442*2810ac1bSKiyoung Kim		for pid := range findPIDs(list, pids, pid) {
443*2810ac1bSKiyoung Kim			requested[pid] = true
444*2810ac1bSKiyoung Kim			if info, ok := pids[pid]; ok {
445*2810ac1bSKiyoung Kim				wanted[pid] = info.depth
446*2810ac1bSKiyoung Kim			}
447*2810ac1bSKiyoung Kim		}
448*2810ac1bSKiyoung Kim	}
449*2810ac1bSKiyoung Kim
450*2810ac1bSKiyoung Kim	var noted []string
451*2810ac1bSKiyoung Kim	for pid := range wanted {
452*2810ac1bSKiyoung Kim		noted = append(noted, pid)
453*2810ac1bSKiyoung Kim	}
454*2810ac1bSKiyoung Kim	sort.Slice(noted, func(i, j int) bool {
455*2810ac1bSKiyoung Kim		return wanted[noted[i]] < wanted[noted[j]]
456*2810ac1bSKiyoung Kim	})
457*2810ac1bSKiyoung Kim
458*2810ac1bSKiyoung Kim	// We've boiled down the processes to a unique set of targets.
459*2810ac1bSKiyoung Kim	for _, pid := range noted {
460*2810ac1bSKiyoung Kim		rDump(pids, requested, pid, "", "--", "  ", *depth)
461*2810ac1bSKiyoung Kim	}
462*2810ac1bSKiyoung Kim
463*2810ac1bSKiyoung Kim	for pid, missed := range requested {
464*2810ac1bSKiyoung Kim		if missed {
465*2810ac1bSKiyoung Kim			fmt.Println("[PID", pid, "not found]")
466*2810ac1bSKiyoung Kim		}
467*2810ac1bSKiyoung Kim	}
468*2810ac1bSKiyoung Kim}
469