dtpstree.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. // DT PS Tree
  2. //
  3. // Douglas Thrift
  4. //
  5. // dtpstree.cpp
  6. /* Copyright 2010 Douglas Thrift
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. #include <cerrno>
  21. #include <climits>
  22. #include <cstdarg>
  23. #include <cstdio>
  24. #include <cstdlib>
  25. #include <cstring>
  26. #include <iostream>
  27. #include <map>
  28. #include <set>
  29. #include <sstream>
  30. #include <string>
  31. #include <vector>
  32. #ifdef __GLIBC__
  33. #include <bsd/stdlib.h>
  34. #else
  35. #include <libgen.h>
  36. #endif
  37. #include <curses.h>
  38. #include <err.h>
  39. #include <fcntl.h>
  40. #include <getopt.h>
  41. #include <kvm.h>
  42. #include <paths.h>
  43. #include <pwd.h>
  44. #include <sys/param.h>
  45. #include <sys/sysctl.h>
  46. #include <sys/user.h>
  47. #include <term.h>
  48. #include <vis.h>
  49. #include "foreach.hpp"
  50. #define DTPSTREE_PROGRAM "dtpstree"
  51. #define DTPSTREE_VERSION "1.0.1"
  52. class Proc;
  53. typedef std::map<pid_t, Proc *> PidMap;
  54. typedef std::multimap<std::string, Proc *> NameMap;
  55. enum Flags
  56. {
  57. Arguments = 0x0001,
  58. Ascii = 0x0002,
  59. Compact = 0x0004,
  60. Highlight = 0x0008,
  61. Vt100 = 0x0010,
  62. ShowKernel = 0x0020,
  63. Long = 0x0040,
  64. NumericSort = 0x0080,
  65. ShowPids = 0x0100,
  66. ShowTitles = 0x0200,
  67. UidChanges = 0x0400,
  68. Unicode = 0x0800,
  69. Version = 0x1000,
  70. Pid = 0x2000,
  71. User = 0x4000
  72. };
  73. enum Escape { None, BoxDrawing, Bright };
  74. struct Segment
  75. {
  76. size_t width_;
  77. Escape escape_;
  78. char *string_;
  79. inline Segment(size_t width, Escape escape, char *string) : width_(width), escape_(escape), string_(string) {}
  80. };
  81. struct Branch
  82. {
  83. std::string indentation_;
  84. bool done_;
  85. inline Branch(size_t indentation) : indentation_(indentation, ' '), done_(false) {}
  86. };
  87. class Tree
  88. {
  89. const uint16_t &flags_;
  90. bool vt100_;
  91. wchar_t horizontal_, vertical_, upAndRight_, verticalAndRight_, downAndHorizontal_;
  92. size_t maxWidth_, width_;
  93. bool max_, suppress_;
  94. std::vector<Segment> segments_;
  95. std::vector<Branch> branches_;
  96. bool first_, last_;
  97. public:
  98. Tree(const uint16_t &flags) : flags_(flags), vt100_(false), maxWidth_(0), width_(0), max_(false), suppress_(false)
  99. {
  100. bool tty(isatty(1));
  101. if (flags & Ascii)
  102. {
  103. ascii:
  104. horizontal_ = L'-';
  105. vertical_ = L'|';
  106. upAndRight_ = L'`';
  107. verticalAndRight_ = L'|';
  108. downAndHorizontal_ = L'+';
  109. }
  110. else if (flags & Unicode)
  111. {
  112. unicode:
  113. if (!std::setlocale(LC_CTYPE, ""))
  114. goto vt100;
  115. horizontal_ = L'\x2500';
  116. vertical_ = L'\x2502';
  117. upAndRight_ = L'\x2514';
  118. verticalAndRight_ = L'\x251c';
  119. downAndHorizontal_ = L'\x252c';
  120. char *test;
  121. if (asprintf(&test, "%lc%lc%lc%lc%lc", horizontal_, vertical_, upAndRight_, verticalAndRight_, downAndHorizontal_) == -1)
  122. goto vt100;
  123. std::free(test);
  124. }
  125. else if (flags & Vt100)
  126. {
  127. vt100:
  128. vt100_ = true;
  129. horizontal_ = L'\x71';
  130. vertical_ = L'\x78';
  131. upAndRight_ = L'\x6d';
  132. verticalAndRight_ = L'\x74';
  133. downAndHorizontal_ = L'\x77';
  134. }
  135. else if (tty)
  136. goto unicode;
  137. else
  138. goto ascii;
  139. if (!(flags & Long) && tty)
  140. {
  141. int code;
  142. if (setupterm(NULL, 1, &code) == OK)
  143. {
  144. maxWidth_ = tigetnum("cols");
  145. if (tigetflag("am") && !tigetflag("xenl"))
  146. suppress_ = true;
  147. }
  148. else
  149. maxWidth_ = 80;
  150. }
  151. }
  152. void print(const std::string &string, bool highlight)
  153. {
  154. Escape escape(vt100_ ? BoxDrawing : None);
  155. if (!first_ || flags_ & Arguments)
  156. {
  157. size_t last(branches_.size() - 1);
  158. _foreach (std::vector<Branch>, branch, branches_)
  159. {
  160. size_t width(branch->indentation_.size() + 2);
  161. if (_index == last)
  162. {
  163. wchar_t line;
  164. if (last_)
  165. {
  166. branch->done_ = true;
  167. line = upAndRight_;
  168. }
  169. else
  170. line = verticalAndRight_;
  171. print(width, escape, "%s%lc%lc", branch->indentation_.c_str(), line, horizontal_);
  172. }
  173. else
  174. print(width, escape, "%s%lc ", branch->indentation_.c_str(), branch->done_ ? ' ' : vertical_);
  175. }
  176. }
  177. else if (branches_.size())
  178. {
  179. wchar_t line;
  180. if (last_)
  181. {
  182. branches_.back().done_ = true;
  183. line = horizontal_;
  184. }
  185. else
  186. line = downAndHorizontal_;
  187. print(3, escape, "%lc%lc%lc", horizontal_, line, horizontal_);
  188. }
  189. print(string.size(), highlight ? Bright : None, "%s", string.c_str());
  190. branches_.push_back(Branch(!(flags_ & Arguments) ? string.size() + 1 : 2));
  191. }
  192. inline void printArg(const std::string &arg, bool last)
  193. {
  194. if (max_)
  195. return;
  196. size_t width(arg.size() + 1);
  197. width_ += width;
  198. char *string;
  199. if (maxWidth_ && !(flags_ & Long))
  200. if (width_ > maxWidth_ || !last && width_ + 3 >= maxWidth_)
  201. {
  202. width -= width_ - maxWidth_;
  203. width_ = maxWidth_;
  204. max_ = true;
  205. ssize_t size(static_cast<ssize_t>(width) - 4);
  206. asprintf(&string, " %s...", size > 0 ? arg.substr(0, size).c_str() : "");
  207. }
  208. else
  209. goto print;
  210. else
  211. print:
  212. asprintf(&string, " %s", arg.c_str());
  213. segments_.push_back(Segment(width, None, string));
  214. }
  215. inline void pop(bool children)
  216. {
  217. branches_.pop_back();
  218. if (!(flags_ & Arguments) && !children)
  219. done();
  220. }
  221. void done()
  222. {
  223. size_t last(segments_.size() - 1);
  224. _foreach (std::vector<Segment>, segment, segments_)
  225. {
  226. const char *begin, *end;
  227. switch (segment->escape_)
  228. {
  229. case BoxDrawing:
  230. begin = !_index || (segment - 1)->escape_ != BoxDrawing ? "\033(0\017" : "";
  231. end = _index == last || (segment + 1)->escape_ != BoxDrawing ? "\033(B\017" : "";
  232. break;
  233. case Bright:
  234. begin = "\033[1m";
  235. end = "\033[22m";
  236. break;
  237. default:
  238. begin = end = ""; break;
  239. }
  240. std::printf("%s%s%s", begin, segment->string_, end);
  241. std::free(segment->string_);
  242. }
  243. segments_.clear();
  244. if (suppress_ && width_ == maxWidth_)
  245. std::fflush(stdout);
  246. else
  247. std::printf("\n");
  248. width_ = 0;
  249. max_ = false;
  250. }
  251. inline Tree &operator()(bool first, bool last)
  252. {
  253. first_ = first;
  254. last_ = last;
  255. return *this;
  256. }
  257. private:
  258. void print(size_t width, Escape escape, const char * format, ...)
  259. {
  260. if (max_)
  261. return;
  262. std::va_list args;
  263. va_start(args, format);
  264. char *string;
  265. vasprintf(&string, format, args);
  266. va_end(args);
  267. width_ += width;
  268. if (maxWidth_ && !(flags_ & Long))
  269. if (width_ > maxWidth_)
  270. {
  271. width -= width_ - maxWidth_;
  272. width_ = maxWidth_;
  273. max_ = true;
  274. bool previous = !width;
  275. if (previous)
  276. {
  277. std::free(string);
  278. const Segment &segment(segments_.back());
  279. width = segment.width_;
  280. string = segment.string_;
  281. }
  282. std::wstring wide(width - 1, '\0');
  283. std::mbstowcs(const_cast<wchar_t *>(wide.data()), string, wide.size());
  284. std::free(string);
  285. asprintf(&string, "%ls+", wide.c_str());
  286. if (previous)
  287. {
  288. segments_.back().string_ = string;
  289. return;
  290. }
  291. }
  292. segments_.push_back(Segment(width, escape, string));
  293. }
  294. };
  295. class Proc
  296. {
  297. const uint16_t &flags_;
  298. kvm_t *kd_;
  299. kinfo_proc *proc_;
  300. mutable std::string name_;
  301. Proc *parent_;
  302. PidMap childrenByPid_;
  303. NameMap childrenByName_;
  304. bool highlight_, root_;
  305. public:
  306. inline Proc(const uint16_t &flags, kvm_t *kd, kinfo_proc *proc) : flags_(flags), kd_(kd), proc_(proc), parent_(NULL), highlight_(false), root_(false) {}
  307. inline std::string name() const
  308. {
  309. if (name_.empty())
  310. name_ = visual(proc_->ki_comm);
  311. return name_;
  312. }
  313. inline pid_t parent() const { return proc_->ki_ppid; }
  314. inline pid_t pid() const { return proc_->ki_pid; }
  315. inline void child(Proc *proc)
  316. {
  317. if (proc == this)
  318. return;
  319. proc->parent_ = this;
  320. childrenByPid_[proc->pid()] = proc;
  321. childrenByName_.insert(NameMap::value_type(proc->name(), proc));
  322. }
  323. inline void highlight()
  324. {
  325. highlight_ = true;
  326. if (parent_)
  327. parent_->highlight();
  328. }
  329. inline bool root(uid_t uid)
  330. {
  331. if (flags_ & User)
  332. {
  333. if (uid == this->uid())
  334. {
  335. Proc *parent(parent_);
  336. while (parent)
  337. {
  338. if (parent->uid() == uid)
  339. return false;
  340. parent = parent->parent_;
  341. }
  342. return root_ = true;
  343. }
  344. return false;
  345. }
  346. return root_ = !parent_;
  347. }
  348. void printByPid(Tree &tree) const
  349. {
  350. print(tree);
  351. size_t last(childrenByPid_.size() - 1);
  352. _foreach (const PidMap, child, childrenByPid_)
  353. child->second->printByPid(tree(!_index, _index == last));
  354. tree.pop(children());
  355. }
  356. void printByName(Tree &tree) const
  357. {
  358. print(tree);
  359. size_t last(childrenByName_.size() - 1);
  360. _foreach (const NameMap, child, childrenByName_)
  361. child->second->printByName(tree(!_index, _index == last));
  362. tree.pop(children());
  363. }
  364. private:
  365. inline std::string visual(const char *string) const
  366. {
  367. std::string visual(std::strlen(string) * 4 + 1, '\0');
  368. visual.resize(strvis(const_cast<char *>(visual.data()), string, VIS_TAB | VIS_NL | VIS_NOSLASH));
  369. return visual;
  370. }
  371. void print(Tree &tree) const
  372. {
  373. bool titles(flags_ & ShowTitles);
  374. char **argv(NULL);
  375. std::ostringstream print;
  376. if (titles)
  377. {
  378. argv = kvm_getargv(kd_, proc_, 0);
  379. if (argv)
  380. print << visual(*argv);
  381. else
  382. print << name();
  383. }
  384. else
  385. print << name();
  386. bool _pid(flags_ & ShowPids), args(flags_ & Arguments);
  387. bool change(flags_ & UidChanges && (root_ ? !(flags_ & User) && uid() : parent_ && uid() != parent_->uid()));
  388. bool parens((_pid || change) && !args);
  389. if (parens)
  390. print << '(';
  391. if (_pid)
  392. {
  393. if (!parens)
  394. print << ',';
  395. print << pid();
  396. }
  397. if (change)
  398. {
  399. if (!parens || _pid)
  400. print << ',';
  401. passwd *user(getpwuid(uid()));
  402. print << user->pw_name;
  403. }
  404. if (parens)
  405. print << ')';
  406. tree.print(print.str(), highlight_);
  407. if (args)
  408. {
  409. if (!titles && !argv)
  410. argv = kvm_getargv(kd_, proc_, 0);
  411. if (argv && *argv)
  412. for (++argv; *argv; ++argv)
  413. tree.printArg(visual(*argv), !*(argv + 1));
  414. tree.done();
  415. }
  416. }
  417. inline bool children() const { return childrenByPid_.size(); }
  418. inline uid_t uid() const { return proc_->ki_ruid; }
  419. };
  420. static void help(const char *program, option options[], int code = 0)
  421. {
  422. std::printf("Usage: %s [options] [PID|USER]\n\nOptions:\n", basename(program));
  423. for (option *option(options); option->name; ++option)
  424. {
  425. std::string name(option->name);
  426. std::ostringstream arguments;
  427. switch (option->val)
  428. {
  429. case 'H':
  430. if (name != "highlight")
  431. continue;
  432. arguments << "-H[PID], --highlight[=PID]"; break;
  433. case 0:
  434. if (name == "pid")
  435. arguments << "PID, --pid=PID";
  436. else if (name == "user")
  437. arguments << "USER, --user=USER";
  438. else
  439. goto argument;
  440. break;
  441. default:
  442. arguments << '-' << static_cast<char>(option->val) << ", ";
  443. argument:
  444. arguments << "--" << name;
  445. }
  446. const char *description("");
  447. switch (option->val)
  448. {
  449. case 'a':
  450. description = "show command line arguments"; break;
  451. case 'A':
  452. description = "use ASCII line drawing characters"; break;
  453. case 'c':
  454. description = "don't compact identical subtrees"; break;
  455. case 'h':
  456. description = "show this help message and exit"; break;
  457. case 'H':
  458. description = "highlight the current process (or PID) and its\n ancestors"; break;
  459. case 'G':
  460. description = "use VT100 line drawing characters"; break;
  461. case 'k':
  462. description = "show kernel processes"; break;
  463. case 'l':
  464. description = "don't truncate long lines"; break;
  465. case 'n':
  466. description = "sort output by PID"; break;
  467. case 'p':
  468. description = "show PIDs; implies -c"; break;
  469. case 't':
  470. description = "show process titles"; break;
  471. case 'u':
  472. description = "show uid transitions"; break;
  473. case 'U':
  474. description = "use Unicode line drawing characters"; break;
  475. case 'V':
  476. description = "show version information and exit"; break;
  477. case 0:
  478. if (name == "pid")
  479. description = "show only the tree rooted at the process PID";
  480. else if (name == "user")
  481. description = "show only trees rooted at processes of USER";
  482. }
  483. std::printf(" %-27s %s\n", arguments.str().c_str(), description);
  484. }
  485. std::exit(code);
  486. }
  487. template <typename Type, long long minimum, long long maximum>
  488. static Type value(char *program, option options[], bool *success = NULL)
  489. {
  490. const char *error;
  491. long long value(strtonum(optarg, minimum, maximum, &error));
  492. if (error)
  493. if (success && errno == EINVAL)
  494. *success = false;
  495. else
  496. {
  497. warnx("Number is %s: \"%s\"", error, optarg);
  498. help(program, options, 1);
  499. }
  500. else if (success)
  501. *success = true;
  502. return value;
  503. }
  504. static uint16_t options(int argc, char *argv[], pid_t &hpid, pid_t &pid, char *&user)
  505. {
  506. option options[] = {
  507. { "arguments", no_argument, NULL, 'a' },
  508. { "ascii", no_argument, NULL, 'A' },
  509. { "compact", no_argument, NULL, 'c' },
  510. { "help", no_argument, NULL, 'h' },
  511. { "highlight", optional_argument, NULL, 'H' },
  512. { "highlight-all", no_argument, NULL, 'H' },
  513. { "highlight-pid", required_argument, NULL, 'H' },
  514. { "vt100", no_argument, NULL, 'G' },
  515. { "show-kernel", no_argument, NULL, 'k' },
  516. { "long", no_argument, NULL, 'l' },
  517. { "numeric-sort", no_argument, NULL, 'n' },
  518. { "show-pids", no_argument, NULL, 'p' },
  519. { "show-titles", no_argument, NULL, 't' },
  520. { "uid-changes", no_argument, NULL, 'u' },
  521. { "unicode", no_argument, NULL, 'U' },
  522. { "version", no_argument, NULL, 'V' },
  523. { "pid", required_argument, NULL, 0 },
  524. { "user", required_argument, NULL, 0 },
  525. { NULL, 0, NULL, 0 }
  526. };
  527. int option, index;
  528. uint16_t flags(0);
  529. char *program(argv[0]);
  530. while ((option = getopt_long(argc, argv, "aAchH::GklnptuUV", options, &index)) != -1)
  531. switch (option)
  532. {
  533. case 'a':
  534. flags |= Arguments | Compact; break;
  535. case 'A':
  536. flags |= Ascii;
  537. flags &= ~Vt100;
  538. flags &= ~Unicode;
  539. break;
  540. case 'c':
  541. flags |= Compact; break;
  542. case 'h':
  543. help(program, options);
  544. case 'H':
  545. hpid = optarg ? value<pid_t, 0, INT_MAX>(program, options) : getpid();
  546. flags |= Highlight;
  547. break;
  548. case 'G':
  549. flags |= Vt100;
  550. flags &= ~Ascii;
  551. flags &= ~Unicode;
  552. break;
  553. case 'k':
  554. flags |= ShowKernel; break;
  555. case 'l':
  556. flags |= Long; break;
  557. case 'n':
  558. flags |= NumericSort; break;
  559. case 'p':
  560. flags |= Compact | ShowPids; break;
  561. case 't':
  562. flags |= ShowTitles; break;
  563. case 'u':
  564. flags |= UidChanges; break;
  565. case 'U':
  566. flags |= Unicode;
  567. flags &= ~Ascii;
  568. flags &= ~Vt100;
  569. break;
  570. case 'V':
  571. flags |= Version; break;
  572. case 0:
  573. {
  574. std::string option(options[index].name);
  575. if (option == "pid")
  576. {
  577. pid = value<pid_t, 0, INT_MAX>(program, options);
  578. flags |= Pid;
  579. flags &= ~User;
  580. }
  581. else if (option == "user")
  582. {
  583. std::free(user);
  584. user = strdup(optarg);
  585. flags |= User;
  586. flags &= ~Pid;
  587. }
  588. }
  589. break;
  590. case '?':
  591. help(program, options, 1);
  592. }
  593. _forall (int, index, optind, argc)
  594. {
  595. bool success;
  596. optarg = argv[index];
  597. pid = value<pid_t, 0, INT_MAX>(program, options, &success);
  598. if (success)
  599. {
  600. flags |= Pid;
  601. flags &= ~User;
  602. }
  603. else
  604. {
  605. std::free(user);
  606. user = strdup(optarg);
  607. flags |= User;
  608. flags &= ~Pid;
  609. }
  610. }
  611. return flags;
  612. }
  613. int main(int argc, char *argv[])
  614. {
  615. pid_t hpid(0), pid(0);
  616. char *user(NULL);
  617. uint16_t flags(options(argc, argv, hpid, pid, user));
  618. if (flags & Version)
  619. {
  620. std::printf(DTPSTREE_PROGRAM " " DTPSTREE_VERSION "\n");
  621. return 0;
  622. }
  623. uid_t uid(0);
  624. if (flags & User)
  625. {
  626. errno = 0;
  627. passwd *_user(getpwnam(user));
  628. if (!_user)
  629. errno ? err(1, NULL) : errx(1, "Unknown user: \"%s\"", user);
  630. uid = _user->pw_uid;
  631. }
  632. char error[_POSIX2_LINE_MAX];
  633. kvm_t *kd(kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, error));
  634. if (!kd)
  635. errx(1, "%s", error);
  636. typedef kinfo_proc *InfoProc;
  637. int count;
  638. InfoProc procs(kvm_getprocs(kd, KERN_PROC_PROC, 0, &count));
  639. if (!procs)
  640. errx(1, "%s", kvm_geterr(kd));
  641. PidMap pids;
  642. _forall (InfoProc, proc, procs, procs + count)
  643. if (flags & ShowKernel || proc->ki_ppid != 0 || proc->ki_pid == 1)
  644. pids[proc->ki_pid] = new Proc(flags, kd, proc);
  645. enum { PidSort, NameSort } sort(flags & NumericSort ? PidSort : NameSort);
  646. _foreach (PidMap, pid, pids)
  647. {
  648. Proc *proc(pid->second);
  649. PidMap::iterator parent(pids.find(proc->parent()));
  650. if (parent != pids.end())
  651. parent->second->child(proc);
  652. }
  653. if (flags & Highlight)
  654. {
  655. PidMap::iterator pid(pids.find(hpid));
  656. if (pid != pids.end())
  657. pid->second->highlight();
  658. }
  659. Tree tree(flags);
  660. if (flags & Pid)
  661. {
  662. PidMap::iterator _pid(pids.find(pid));
  663. if (_pid != pids.end())
  664. {
  665. Proc *proc(_pid->second);
  666. switch (sort)
  667. {
  668. case PidSort:
  669. proc->printByPid(tree);
  670. break;
  671. case NameSort:
  672. proc->printByName(tree);
  673. }
  674. }
  675. }
  676. else
  677. {
  678. NameMap names;
  679. _foreach (PidMap, pid, pids)
  680. {
  681. Proc *proc(pid->second);
  682. if (proc->root(uid))
  683. switch (sort)
  684. {
  685. case PidSort:
  686. proc->printByPid(tree);
  687. break;
  688. case NameSort:
  689. names.insert(NameMap::value_type(proc->name(), proc));
  690. }
  691. }
  692. switch (sort)
  693. {
  694. case NameSort:
  695. _foreach (NameMap, name, names)
  696. name->second->printByName(tree);
  697. default:
  698. break;
  699. }
  700. }
  701. _foreach (PidMap, pid, pids)
  702. delete pid->second;
  703. return 0;
  704. }
  705. // display a tree of processes