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