dtpstree.cpp 14 KB

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