dtpstree.cpp 14 KB

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