dtpstree.cpp 14 KB

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