dtpstree.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  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. NoCompact = 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. size_t duplicate_;
  98. public:
  99. Tree(const uint16_t &flags) : flags_(flags), vt100_(false), maxWidth_(0), width_(0), max_(false), suppress_(false), duplicate_(0)
  100. {
  101. bool tty(isatty(1));
  102. if (flags & Ascii)
  103. {
  104. ascii:
  105. horizontal_ = L'-';
  106. vertical_ = L'|';
  107. upAndRight_ = L'`';
  108. verticalAndRight_ = L'|';
  109. downAndHorizontal_ = L'+';
  110. }
  111. else if (flags & Unicode)
  112. {
  113. unicode:
  114. if (!std::setlocale(LC_CTYPE, ""))
  115. goto vt100;
  116. horizontal_ = L'\x2500';
  117. vertical_ = L'\x2502';
  118. upAndRight_ = L'\x2514';
  119. verticalAndRight_ = L'\x251c';
  120. downAndHorizontal_ = L'\x252c';
  121. char *test;
  122. if (asprintf(&test, "%lc%lc%lc%lc%lc", horizontal_, vertical_, upAndRight_, verticalAndRight_, downAndHorizontal_) == -1)
  123. goto vt100;
  124. std::free(test);
  125. }
  126. else if (flags & Vt100)
  127. {
  128. vt100:
  129. vt100_ = true;
  130. horizontal_ = L'\x71';
  131. vertical_ = L'\x78';
  132. upAndRight_ = L'\x6d';
  133. verticalAndRight_ = L'\x74';
  134. downAndHorizontal_ = L'\x77';
  135. }
  136. else if (tty)
  137. goto unicode;
  138. else
  139. goto ascii;
  140. if (!(flags & Long) && tty)
  141. {
  142. int code;
  143. if (setupterm(NULL, 1, &code) == OK)
  144. {
  145. maxWidth_ = tigetnum("cols");
  146. if (tigetflag("am") && !tigetflag("xenl"))
  147. suppress_ = true;
  148. }
  149. else
  150. maxWidth_ = 80;
  151. }
  152. }
  153. void print(const std::string &string, bool highlight, size_t duplicate)
  154. {
  155. Escape escape(vt100_ ? BoxDrawing : None);
  156. if (!first_ || flags_ & Arguments)
  157. {
  158. size_t last(branches_.size() - 1);
  159. _foreach (std::vector<Branch>, branch, branches_)
  160. {
  161. size_t width(branch->indentation_.size() + 2);
  162. if (_index == last)
  163. {
  164. wchar_t line;
  165. if (last_)
  166. {
  167. branch->done_ = true;
  168. line = upAndRight_;
  169. }
  170. else
  171. line = verticalAndRight_;
  172. print(width, escape, "%s%lc%lc", branch->indentation_.c_str(), line, horizontal_);
  173. }
  174. else
  175. print(width, escape, "%s%lc ", branch->indentation_.c_str(), branch->done_ ? ' ' : vertical_);
  176. }
  177. }
  178. else if (branches_.size())
  179. {
  180. wchar_t line;
  181. if (last_)
  182. {
  183. branches_.back().done_ = true;
  184. line = horizontal_;
  185. }
  186. else
  187. line = downAndHorizontal_;
  188. print(3, escape, "%lc%lc%lc", horizontal_, line, horizontal_);
  189. }
  190. size_t size(0);
  191. if (duplicate)
  192. {
  193. std::ostringstream string;
  194. string << duplicate << "*[";
  195. size = string.str().size();
  196. print(size, None, "%s", string.str().c_str());
  197. ++duplicate_;
  198. }
  199. print(string.size(), highlight ? Bright : None, "%s", string.c_str());
  200. branches_.push_back(Branch(!(flags_ & Arguments) ? size + string.size() + 1 : 2));
  201. }
  202. inline void printArg(const std::string &arg, bool last)
  203. {
  204. if (max_)
  205. return;
  206. size_t width(arg.size() + 1);
  207. width_ += width;
  208. char *string;
  209. if (maxWidth_ && !(flags_ & Long))
  210. if (width_ > maxWidth_ || !last && width_ + 3 >= maxWidth_)
  211. {
  212. width -= width_ - maxWidth_;
  213. width_ = maxWidth_;
  214. max_ = true;
  215. ssize_t size(static_cast<ssize_t>(width) - 4);
  216. asprintf(&string, " %s...", size > 0 ? arg.substr(0, size).c_str() : "");
  217. }
  218. else
  219. goto print;
  220. else
  221. print:
  222. asprintf(&string, " %s", arg.c_str());
  223. segments_.push_back(Segment(width, None, string));
  224. }
  225. inline void pop(bool children)
  226. {
  227. branches_.pop_back();
  228. if (!(flags_ & Arguments) && !children)
  229. done();
  230. }
  231. void done()
  232. {
  233. if (duplicate_)
  234. {
  235. print(duplicate_, None, "%s", std::string(duplicate_, ']').c_str());
  236. duplicate_ = 0;
  237. }
  238. size_t last(segments_.size() - 1);
  239. _foreach (std::vector<Segment>, segment, segments_)
  240. {
  241. const char *begin, *end;
  242. switch (segment->escape_)
  243. {
  244. case BoxDrawing:
  245. begin = !_index || (segment - 1)->escape_ != BoxDrawing ? "\033(0\017" : "";
  246. end = _index == last || (segment + 1)->escape_ != BoxDrawing ? "\033(B\017" : "";
  247. break;
  248. case Bright:
  249. begin = "\033[1m";
  250. end = "\033[22m";
  251. break;
  252. default:
  253. begin = end = ""; break;
  254. }
  255. std::printf("%s%s%s", begin, segment->string_, end);
  256. std::free(segment->string_);
  257. }
  258. segments_.clear();
  259. if (suppress_ && width_ == maxWidth_)
  260. std::fflush(stdout);
  261. else
  262. std::printf("\n");
  263. width_ = 0;
  264. max_ = false;
  265. }
  266. inline Tree &operator()(bool first, bool last)
  267. {
  268. first_ = first;
  269. last_ = last;
  270. return *this;
  271. }
  272. private:
  273. void print(size_t width, Escape escape, const char * format, ...)
  274. {
  275. if (max_)
  276. return;
  277. std::va_list args;
  278. va_start(args, format);
  279. char *string;
  280. vasprintf(&string, format, args);
  281. va_end(args);
  282. width_ += width;
  283. if (maxWidth_ && !(flags_ & Long))
  284. if (width_ > maxWidth_)
  285. {
  286. width -= width_ - maxWidth_;
  287. width_ = maxWidth_;
  288. max_ = true;
  289. bool previous = !width;
  290. if (previous)
  291. {
  292. std::free(string);
  293. const Segment &segment(segments_.back());
  294. width = segment.width_;
  295. string = segment.string_;
  296. }
  297. std::wstring wide(width - 1, '\0');
  298. std::mbstowcs(const_cast<wchar_t *>(wide.data()), string, wide.size());
  299. std::free(string);
  300. asprintf(&string, "%ls+", wide.c_str());
  301. if (previous)
  302. {
  303. segments_.back().string_ = string;
  304. return;
  305. }
  306. }
  307. segments_.push_back(Segment(width, escape, string));
  308. }
  309. };
  310. class Proc
  311. {
  312. const uint16_t &flags_;
  313. kvm_t *kd_;
  314. kinfo_proc *proc_;
  315. mutable std::string name_, print_;
  316. Proc *parent_;
  317. PidMap childrenByPid_;
  318. NameMap childrenByName_;
  319. bool highlight_, root_;
  320. int8_t compact_;
  321. size_t duplicate_;
  322. public:
  323. inline Proc(const uint16_t &flags, kvm_t *kd, kinfo_proc *proc) : flags_(flags), kd_(kd), proc_(proc), parent_(NULL), highlight_(false), root_(false), compact_(-1), duplicate_(0) {}
  324. inline const std::string &name() const
  325. {
  326. if (name_.empty())
  327. name_ = visual(proc_->ki_comm);
  328. return name_;
  329. }
  330. inline pid_t parent() const { return proc_->ki_ppid; }
  331. inline pid_t pid() const { return proc_->ki_pid; }
  332. inline void child(Proc *proc)
  333. {
  334. if (proc == this)
  335. return;
  336. proc->parent_ = this;
  337. childrenByPid_[proc->pid()] = proc;
  338. childrenByName_.insert(NameMap::value_type(proc->name(), proc));
  339. }
  340. inline void highlight()
  341. {
  342. highlight_ = true;
  343. if (parent_)
  344. parent_->highlight();
  345. }
  346. inline bool compact()
  347. {
  348. if (compact_ == -1)
  349. compact_ = compact(childrenByName_);
  350. return compact_;
  351. }
  352. bool root(uid_t uid)
  353. {
  354. if (flags_ & User)
  355. {
  356. if (uid == this->uid())
  357. {
  358. Proc *parent(parent_);
  359. while (parent)
  360. {
  361. if (parent->uid() == uid)
  362. return false;
  363. parent = parent->parent_;
  364. }
  365. return root_ = true;
  366. }
  367. return false;
  368. }
  369. return root_ = !parent_;
  370. }
  371. inline void printByPid(Tree &tree) const
  372. {
  373. print(tree, childrenByPid_);
  374. }
  375. inline void printByName(Tree &tree) const
  376. {
  377. print(tree, childrenByName_);
  378. }
  379. static bool compact(NameMap &names)
  380. {
  381. Proc *previous(NULL);
  382. bool compact(true);
  383. _foreach (NameMap, name, names)
  384. {
  385. Proc *proc(name->second);
  386. if (proc->duplicate_)
  387. continue;
  388. size_t duplicate(proc->compact());
  389. if (compact && duplicate && (!previous || proc->print() == previous->print()))
  390. previous = proc;
  391. else
  392. compact = false;
  393. size_t count(names.count(name->first));
  394. if (!duplicate || count == 1)
  395. continue;
  396. _forall(NameMap::iterator, n4me, (++name)--, names.upper_bound(name->first))
  397. {
  398. Proc *pr0c(n4me->second);
  399. if (pr0c->compact() && Proc::compact(proc, pr0c))
  400. duplicate += ++pr0c->duplicate_;
  401. }
  402. if (duplicate != 1)
  403. proc->duplicate_ = duplicate;
  404. }
  405. return compact;
  406. }
  407. private:
  408. inline std::string visual(const char *string) const
  409. {
  410. std::string visual(std::strlen(string) * 4 + 1, '\0');
  411. visual.resize(strvis(const_cast<char *>(visual.data()), string, VIS_TAB | VIS_NL | VIS_NOSLASH));
  412. return visual;
  413. }
  414. template <typename Type>
  415. void print(Tree &tree, const Type &children) const
  416. {
  417. if (duplicate_ == 1)
  418. return;
  419. print(tree);
  420. size_t size(children.size()), last(size - 1);
  421. _tforeach (const Type, child, children)
  422. {
  423. Proc *proc(child->second);
  424. bool l4st(_index + (proc->duplicate_ ? proc->duplicate_ - 1 : 0) == last);
  425. if (!l4st)
  426. {
  427. l4st = true;
  428. for (++child; child != _end; ++child)
  429. if (child->second->duplicate_ != 1)
  430. {
  431. l4st = false;
  432. break;
  433. }
  434. --child;
  435. }
  436. proc->print(tree(!_index, l4st), proc->children<Type>());
  437. if (l4st)
  438. break;
  439. }
  440. tree.pop(size);
  441. }
  442. void print(Tree &tree) const
  443. {
  444. tree.print(print(), highlight_, duplicate_);
  445. if (flags_ & Arguments)
  446. {
  447. char **argv(kvm_getargv(kd_, proc_, 0));
  448. if (argv && *argv)
  449. for (++argv; *argv; ++argv)
  450. tree.printArg(visual(*argv), !*(argv + 1));
  451. tree.done();
  452. }
  453. }
  454. const std::string &print() const
  455. {
  456. if (print_.empty())
  457. {
  458. std::ostringstream print;
  459. if (flags_ & ShowTitles)
  460. {
  461. char **argv(kvm_getargv(kd_, proc_, 0));
  462. if (argv)
  463. print << visual(*argv);
  464. else
  465. print << name();
  466. }
  467. else
  468. print << name();
  469. bool p1d(flags_ & ShowPids), args(flags_ & Arguments);
  470. bool change(flags_ & UidChanges && (root_ ? !(flags_ & User) && uid() : parent_ && uid() != parent_->uid()));
  471. bool parens((p1d || change) && !args);
  472. if (parens)
  473. print << '(';
  474. if (p1d)
  475. {
  476. if (!parens)
  477. print << ',';
  478. print << pid();
  479. }
  480. if (change)
  481. {
  482. if (!parens || p1d)
  483. print << ',';
  484. passwd *user(getpwuid(uid()));
  485. print << user->pw_name;
  486. }
  487. if (parens)
  488. print << ')';
  489. print_ = print.str();
  490. }
  491. return print_;
  492. }
  493. inline uid_t uid() const { return proc_->ki_ruid; }
  494. template <typename Type>
  495. inline const Type &children() const;
  496. inline bool children() const { return childrenByName_.size(); }
  497. inline Proc *child() const { return childrenByName_.begin()->second; }
  498. inline static bool compact(Proc *one, Proc *two)
  499. {
  500. if (one->print() != two->print())
  501. return false;
  502. if (one->children() != two->children())
  503. return false;
  504. if (one->children() && !compact(one->child(), two->child()))
  505. return false;
  506. if (two->highlight_)
  507. one->highlight_ = true;
  508. return true;
  509. }
  510. };
  511. template <>
  512. inline const PidMap &Proc::children() const
  513. {
  514. return childrenByPid_;
  515. }
  516. template <>
  517. inline const NameMap &Proc::children() const
  518. {
  519. return childrenByName_;
  520. }
  521. static void help(const char *program, option options[], int code = 0)
  522. {
  523. std::printf("Usage: %s [options] [PID|USER]\n\nOptions:\n", basename(program));
  524. for (option *option(options); option->name; ++option)
  525. {
  526. std::string name(option->name);
  527. std::ostringstream arguments;
  528. switch (option->val)
  529. {
  530. case 'H':
  531. if (name != "highlight")
  532. continue;
  533. arguments << "-H[PID], --highlight[=PID]"; break;
  534. case 0:
  535. if (name == "pid")
  536. arguments << "PID, --pid=PID";
  537. else if (name == "user")
  538. arguments << "USER, --user=USER";
  539. else
  540. goto argument;
  541. break;
  542. case 'c':
  543. if (name != "no-compact")
  544. continue;
  545. default:
  546. arguments << '-' << static_cast<char>(option->val) << ", ";
  547. argument:
  548. arguments << "--" << name;
  549. }
  550. const char *description("");
  551. switch (option->val)
  552. {
  553. case 'a':
  554. description = "show command line arguments"; break;
  555. case 'A':
  556. description = "use ASCII line drawing characters"; break;
  557. case 'c':
  558. description = "don't compact identical subtrees"; break;
  559. case 'h':
  560. description = "show this help message and exit"; break;
  561. case 'H':
  562. description = "highlight the current process (or PID) and its\n ancestors"; break;
  563. case 'G':
  564. description = "use VT100 line drawing characters"; break;
  565. case 'k':
  566. description = "show kernel processes"; break;
  567. case 'l':
  568. description = "don't truncate long lines"; break;
  569. case 'n':
  570. description = "sort output by PID"; break;
  571. case 'p':
  572. description = "show PIDs; implies -c"; break;
  573. case 't':
  574. description = "show process titles"; break;
  575. case 'u':
  576. description = "show uid transitions"; break;
  577. case 'U':
  578. description = "use Unicode line drawing characters"; break;
  579. case 'V':
  580. description = "show version information and exit"; break;
  581. case 0:
  582. if (name == "pid")
  583. description = "show only the tree rooted at the process PID";
  584. else if (name == "user")
  585. description = "show only trees rooted at processes of USER";
  586. }
  587. std::printf(" %-27s %s\n", arguments.str().c_str(), description);
  588. }
  589. std::exit(code);
  590. }
  591. template <typename Type, long long minimum, long long maximum>
  592. static Type value(char *program, option options[], bool *success = NULL)
  593. {
  594. const char *error;
  595. long long value(strtonum(optarg, minimum, maximum, &error));
  596. if (error)
  597. if (success && errno == EINVAL)
  598. *success = false;
  599. else
  600. {
  601. warnx("Number is %s: \"%s\"", error, optarg);
  602. help(program, options, 1);
  603. }
  604. else if (success)
  605. *success = true;
  606. return value;
  607. }
  608. static uint16_t options(int argc, char *argv[], pid_t &hpid, pid_t &pid, char *&user)
  609. {
  610. option options[] = {
  611. { "arguments", no_argument, NULL, 'a' },
  612. { "ascii", no_argument, NULL, 'A' },
  613. { "compact", no_argument, NULL, 'c' },
  614. { "no-compact", no_argument, NULL, 'c' },
  615. { "help", no_argument, NULL, 'h' },
  616. { "highlight", optional_argument, NULL, 'H' },
  617. { "highlight-all", no_argument, NULL, 'H' },
  618. { "highlight-pid", required_argument, NULL, 'H' },
  619. { "vt100", no_argument, NULL, 'G' },
  620. { "show-kernel", no_argument, NULL, 'k' },
  621. { "long", no_argument, NULL, 'l' },
  622. { "numeric-sort", no_argument, NULL, 'n' },
  623. { "show-pids", no_argument, NULL, 'p' },
  624. { "show-titles", no_argument, NULL, 't' },
  625. { "uid-changes", no_argument, NULL, 'u' },
  626. { "unicode", no_argument, NULL, 'U' },
  627. { "version", no_argument, NULL, 'V' },
  628. { "pid", required_argument, NULL, 0 },
  629. { "user", required_argument, NULL, 0 },
  630. { NULL, 0, NULL, 0 }
  631. };
  632. int option, index;
  633. uint16_t flags(0);
  634. char *program(argv[0]);
  635. while ((option = getopt_long(argc, argv, "aAchH::GklnptuUV", options, &index)) != -1)
  636. switch (option)
  637. {
  638. case 'a':
  639. flags |= Arguments | NoCompact; break;
  640. case 'A':
  641. flags |= Ascii;
  642. flags &= ~Vt100;
  643. flags &= ~Unicode;
  644. break;
  645. case 'c':
  646. flags |= NoCompact; break;
  647. case 'h':
  648. help(program, options);
  649. case 'H':
  650. hpid = optarg ? value<pid_t, 0, INT_MAX>(program, options) : getpid();
  651. flags |= Highlight;
  652. break;
  653. case 'G':
  654. flags |= Vt100;
  655. flags &= ~Ascii;
  656. flags &= ~Unicode;
  657. break;
  658. case 'k':
  659. flags |= ShowKernel; break;
  660. case 'l':
  661. flags |= Long; break;
  662. case 'n':
  663. flags |= NumericSort; break;
  664. case 'p':
  665. flags |= NoCompact | ShowPids; break;
  666. case 't':
  667. flags |= ShowTitles; break;
  668. case 'u':
  669. flags |= UidChanges; break;
  670. case 'U':
  671. flags |= Unicode;
  672. flags &= ~Ascii;
  673. flags &= ~Vt100;
  674. break;
  675. case 'V':
  676. flags |= Version; break;
  677. case 0:
  678. {
  679. std::string option(options[index].name);
  680. if (option == "pid")
  681. {
  682. pid = value<pid_t, 0, INT_MAX>(program, options);
  683. flags |= Pid;
  684. flags &= ~User;
  685. }
  686. else if (option == "user")
  687. {
  688. std::free(user);
  689. user = strdup(optarg);
  690. flags |= User;
  691. flags &= ~Pid;
  692. }
  693. }
  694. break;
  695. case '?':
  696. help(program, options, 1);
  697. }
  698. _forall (int, index, optind, argc)
  699. {
  700. bool success(false);
  701. optarg = argv[index];
  702. pid = value<pid_t, 0, INT_MAX>(program, options, &success);
  703. if (success)
  704. {
  705. flags |= Pid;
  706. flags &= ~User;
  707. }
  708. else
  709. {
  710. std::free(user);
  711. user = strdup(optarg);
  712. flags |= User;
  713. flags &= ~Pid;
  714. }
  715. }
  716. return flags;
  717. }
  718. int main(int argc, char *argv[])
  719. {
  720. pid_t hpid(0), pid(0);
  721. char *user(NULL);
  722. uint16_t flags(options(argc, argv, hpid, pid, user));
  723. if (flags & Version)
  724. {
  725. std::printf(DTPSTREE_PROGRAM " " DTPSTREE_VERSION "\n");
  726. return 0;
  727. }
  728. uid_t uid(0);
  729. if (flags & User)
  730. {
  731. errno = 0;
  732. passwd *us3r(getpwnam(user));
  733. if (!us3r)
  734. errno ? err(1, NULL) : errx(1, "Unknown user: \"%s\"", user);
  735. uid = us3r->pw_uid;
  736. }
  737. char error[_POSIX2_LINE_MAX];
  738. kvm_t *kd(kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, error));
  739. if (!kd)
  740. errx(1, "%s", error);
  741. typedef kinfo_proc *InfoProc;
  742. int count;
  743. InfoProc procs(kvm_getprocs(kd, KERN_PROC_PROC, 0, &count));
  744. if (!procs)
  745. errx(1, "%s", kvm_geterr(kd));
  746. PidMap pids;
  747. _forall (InfoProc, proc, procs, procs + count)
  748. if (flags & ShowKernel || proc->ki_ppid != 0 || proc->ki_pid == 1)
  749. pids[proc->ki_pid] = new Proc(flags, kd, proc);
  750. enum { PidSort, NameSort } sort(flags & NumericSort ? PidSort : NameSort);
  751. _foreach (PidMap, pid, pids)
  752. {
  753. Proc *proc(pid->second);
  754. PidMap::iterator parent(pids.find(proc->parent()));
  755. if (parent != pids.end())
  756. parent->second->child(proc);
  757. }
  758. if (flags & Highlight)
  759. {
  760. PidMap::iterator pid(pids.find(hpid));
  761. if (pid != pids.end())
  762. pid->second->highlight();
  763. }
  764. Tree tree(flags);
  765. if (flags & Pid)
  766. {
  767. PidMap::iterator p1d(pids.find(pid));
  768. if (p1d != pids.end())
  769. {
  770. Proc *proc(p1d->second);
  771. if (!(flags & NoCompact))
  772. proc->compact();
  773. switch (sort)
  774. {
  775. case PidSort:
  776. proc->printByPid(tree);
  777. break;
  778. case NameSort:
  779. proc->printByName(tree);
  780. }
  781. }
  782. }
  783. else
  784. {
  785. NameMap names;
  786. _foreach (PidMap, pid, pids)
  787. {
  788. Proc *proc(pid->second);
  789. if (proc->root(uid))
  790. names.insert(NameMap::value_type(proc->name(), proc));
  791. }
  792. if (!(flags & NoCompact))
  793. Proc::compact(names);
  794. switch (sort)
  795. {
  796. case PidSort:
  797. _foreach (PidMap, pid, pids)
  798. {
  799. Proc *proc(pid->second);
  800. if (proc->root(uid))
  801. proc->printByPid(tree);
  802. }
  803. break;
  804. case NameSort:
  805. _foreach (NameMap, name, names)
  806. name->second->printByName(tree);
  807. }
  808. }
  809. _foreach (PidMap, pid, pids)
  810. delete pid->second;
  811. return 0;
  812. }
  813. // display a tree of processes