dtpstree.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // DT PS Tree
  2. //
  3. // Douglas Thrift
  4. //
  5. // $Id$
  6. #include <climits>
  7. #include <cstdio>
  8. #include <iostream>
  9. #include <map>
  10. #include <set>
  11. #include <sstream>
  12. #include <string>
  13. #include <err.h>
  14. #include <fcntl.h>
  15. #include <getopt.h>
  16. #include <kvm.h>
  17. #include <libgen.h>
  18. #include <paths.h>
  19. #include <pwd.h>
  20. #include <sys/param.h>
  21. #include <sys/sysctl.h>
  22. #include <sys/user.h>
  23. #include "foreach.hpp"
  24. #define DTPSTREE_PROGRAM "dtpstree"
  25. #define DTPSTREE_VERSION "1.0.1"
  26. class Proc;
  27. typedef std::map<pid_t, Proc *> PidMap;
  28. typedef std::multimap<std::string, Proc *> NameMap;
  29. enum Flags
  30. {
  31. Arguments = 0x0001,
  32. Ascii = 0x0002,
  33. Compact = 0x0004,
  34. Highlight = 0x0008,
  35. Vt100 = 0x0010,
  36. ShowKernel = 0x0020,
  37. Long = 0x0040,
  38. NumericSort = 0x0080,
  39. ShowPids = 0x0104,
  40. ShowTitles = 0x0200,
  41. UidChanges = 0x0400,
  42. Unicode = 0x0800,
  43. Version = 0x1000,
  44. Pid = 0x2000,
  45. User = 0x4000
  46. };
  47. class Proc
  48. {
  49. const int &flags_;
  50. kvm_t *kd_;
  51. kinfo_proc *proc_;
  52. Proc *parent_;
  53. PidMap childrenByPid_;
  54. NameMap childrenByName_;
  55. bool highlight_;
  56. public:
  57. Proc(const int &flags, kvm_t *kd, kinfo_proc *proc) : flags_(flags), kd_(kd), proc_(proc) {}
  58. inline std::string name() const { return proc_->ki_comm; }
  59. inline pid_t parent() const { return proc_->ki_ppid; }
  60. inline pid_t pid() const { return proc_->ki_pid; }
  61. void child(Proc *proc)
  62. {
  63. if (proc == this)
  64. return;
  65. proc->parent_ = this;
  66. childrenByPid_[proc->pid()] = proc;
  67. childrenByName_.insert(NameMap::value_type(proc->name(), proc));
  68. }
  69. void highlight()
  70. {
  71. highlight_ = true;
  72. if (parent_)
  73. parent_->highlight();
  74. }
  75. inline bool root() const { return !parent_; }
  76. void printByPid(const std::string &indent = "") const
  77. {
  78. std::cout << indent << print(indent.size()) << std::endl;
  79. _foreach (const PidMap, child, childrenByPid_)
  80. child->second->printByPid(indent + " ");
  81. }
  82. void printByName(const std::string &indent = "") const
  83. {
  84. std::cout << indent << print(indent.size()) << std::endl;
  85. _foreach (const NameMap, child, childrenByName_)
  86. child->second->printByName(indent + " ");
  87. }
  88. private:
  89. std::string print(std::string::size_type indent) const
  90. {
  91. std::ostringstream print;
  92. if (highlight_)
  93. print << "\033[1m";
  94. if (flags_ & ShowTitles)
  95. {
  96. char **argv(kvm_getargv(kd_, proc_, 0));
  97. if (argv)
  98. print << *argv;
  99. else
  100. print << name();
  101. }
  102. else
  103. print << name();
  104. bool _pid(flags_ & ShowPids), _args(flags_ & Arguments);
  105. bool change(flags_ & UidChanges && parent_ && uid() != parent_->uid());
  106. bool parens((_pid || change) && !_args);
  107. if (parens)
  108. print << '(';
  109. if (_pid)
  110. {
  111. if (!parens)
  112. print << ',';
  113. print << pid();
  114. }
  115. if (change)
  116. {
  117. if (!parens || _pid)
  118. print << ',';
  119. print << user();
  120. }
  121. if (parens)
  122. print << ')';
  123. if (highlight_)
  124. print << "\033[22m";
  125. if (_args)
  126. print << args(indent + print.str().size());
  127. return print.str();
  128. }
  129. inline bool children() const { return childrenByPid_.size(); }
  130. inline uid_t uid() const { return proc_->ki_ruid; }
  131. std::string user() const
  132. {
  133. passwd *user(getpwuid(uid()));
  134. return user->pw_name;
  135. }
  136. std::string args(std::string::size_type indent) const
  137. {
  138. char **argv(kvm_getargv(kd_, proc_, 0));
  139. std::ostringstream args;
  140. if (argv && *argv)
  141. for (++argv; *argv; ++argv)
  142. {
  143. if (!(flags_ & Long) && (indent += 1 + std::strlen(*argv)) > 75)
  144. {
  145. args << " ...";
  146. break;
  147. }
  148. args << ' ' << *argv;
  149. }
  150. return args.str();
  151. }
  152. };
  153. static void help(const char *program, option options[], int code = 0)
  154. {
  155. std::cout << "Usage: " << basename(program) << " [options] [PID|USER]" << std::endl << std::endl << "Options:" << std::endl;
  156. for (option *option(options); option->name; ++option)
  157. {
  158. std::string name(option->name);
  159. if (name == "highlight")
  160. continue;
  161. std::ostringstream arguments;
  162. switch (option->val)
  163. {
  164. case 'h':
  165. arguments << "-h, --highlight"; break;
  166. case 'H':
  167. arguments << "-H PID, --highlight=PID"; break;
  168. case 0:
  169. if (name == "pid")
  170. arguments << "PID, --pid=PID";
  171. else if (name == "user")
  172. arguments << "USER, --user=USER";
  173. else
  174. goto argument;
  175. break;
  176. default:
  177. arguments << '-' << static_cast<char>(option->val) << ", ";
  178. argument:
  179. arguments << "--" << name;
  180. }
  181. const char *description("");
  182. switch (option->val)
  183. {
  184. case 'a':
  185. description = "show command line arguments"; break;
  186. case 'A':
  187. description = "use ASCII line drawing characters"; break;
  188. case 'c':
  189. description = "don't compact identical subtrees"; break;
  190. case 'h':
  191. description = "highlight the current process and its ancestors"; break;
  192. case 'H':
  193. description = "highlight the process PID and its ancestors"; break;
  194. case 'G':
  195. description = "use VT100 line drawing characters"; break;
  196. case 'k':
  197. description = "show kernel processes"; break;
  198. case 'l':
  199. description = "don't truncate long lines"; break;
  200. case 'n':
  201. description = "sort output by PID"; break;
  202. case 'p':
  203. description = "show PIDs; implies -c"; break;
  204. case 't':
  205. description = "show process titles"; break;
  206. case 'u':
  207. description = "show uid transitions"; break;
  208. case 'U':
  209. description = "use Unicode line drawing characters"; break;
  210. case 'V':
  211. description = "show version information and exit"; break;
  212. case 0:
  213. if (name == "help")
  214. description = "show this help message and exit";
  215. else if (name == "pid")
  216. description = "show only the tree roted at the process PID";
  217. else if (name == "user")
  218. description = "show only trees rooted at processes of USER";
  219. }
  220. std::printf(" %-25s %s\n", arguments.str().c_str(), description);
  221. }
  222. std::exit(code);
  223. }
  224. template <typename Type, long minimum, long maximum>
  225. static Type value(char *program, option options[], bool *success = NULL)
  226. {
  227. char *end;
  228. long value(std::strtol(optarg, &end, 0));
  229. if (optarg == end || *end != '\0')
  230. if (success)
  231. *success = false;
  232. else
  233. {
  234. warnx("invalid numeric value: \"%s\"", optarg);
  235. help(program, options, 1);
  236. }
  237. else if (value < minimum)
  238. {
  239. warnx("number too small: %s", optarg);
  240. help(program, options, 1);
  241. }
  242. else if (value > maximum)
  243. {
  244. warnx("number too large: %s", optarg);
  245. help(program, options, 1);
  246. }
  247. else if (success)
  248. *success = true;
  249. return value;
  250. }
  251. static int options(int argc, char *argv[], pid_t &hpid, pid_t &pid, char *&user)
  252. {
  253. option options[] = {
  254. { "arguments", no_argument, NULL, 'a' },
  255. { "ascii", no_argument, NULL, 'A' },
  256. { "compact", no_argument, NULL, 'c' },
  257. { "highlight", optional_argument, NULL, 0 },
  258. { "highlight-all", no_argument, NULL, 'h' },
  259. { "highlight-pid", required_argument, NULL, 'H' },
  260. { "vt100", no_argument, NULL, 'G' },
  261. { "show-kernel", no_argument, NULL, 'k' },
  262. { "long", no_argument, NULL, 'l' },
  263. { "numeric-sort", no_argument, NULL, 'n' },
  264. { "show-pids", no_argument, NULL, 'p' },
  265. { "show-titles", no_argument, NULL, 't' },
  266. { "uid-changes", no_argument, NULL, 'u' },
  267. { "unicode", no_argument, NULL, 'U' },
  268. { "version", no_argument, NULL, 'V' },
  269. { "help", no_argument, NULL, 0 },
  270. { "pid", required_argument, NULL, 0 },
  271. { "user", required_argument, NULL, 0 },
  272. { NULL, 0, NULL, 0 }
  273. };
  274. int option, index, flags(0);
  275. char *program(argv[0]);
  276. while ((option = getopt_long(argc, argv, "aAchH:GklnptuUV", options, &index)) != -1)
  277. switch (option)
  278. {
  279. case 'a':
  280. flags |= Arguments; break;
  281. case 'A':
  282. flags |= Ascii;
  283. flags &= ~Vt100;
  284. flags &= ~Unicode;
  285. break;
  286. case 'c':
  287. flags |= Compact; break;
  288. case 'h':
  289. hpid = getpid();
  290. flags |= Highlight;
  291. break;
  292. case 'H':
  293. hpid = value<pid_t, 0, INT_MAX>(program, options);
  294. flags |= Highlight;
  295. break;
  296. case 'G':
  297. flags |= Vt100;
  298. flags &= ~Ascii;
  299. flags &= ~Unicode;
  300. break;
  301. case 'k':
  302. flags |= ShowKernel; break;
  303. case 'l':
  304. flags |= Long; break;
  305. case 'n':
  306. flags |= NumericSort; break;
  307. case 'p':
  308. flags |= ShowPids; break;
  309. case 't':
  310. flags |= ShowTitles; break;
  311. case 'u':
  312. flags |= UidChanges; break;
  313. case 'U':
  314. flags |= Unicode;
  315. flags &= ~Ascii;
  316. flags &= ~Vt100;
  317. break;
  318. case 'V':
  319. flags |= Version; break;
  320. case 0:
  321. {
  322. std::string option(options[index].name);
  323. if (option == "highlight")
  324. {
  325. hpid = optarg ? value<pid_t, 0, INT_MAX>(program, options) : getpid();
  326. flags |= Highlight;
  327. }
  328. else if (option == "pid")
  329. {
  330. pid = value<pid_t, 0, INT_MAX>(program, options);
  331. flags |= Pid;
  332. flags &= ~User;
  333. }
  334. else if (option == "user")
  335. {
  336. std::free(user);
  337. user = strdup(optarg);
  338. flags |= User;
  339. flags &= ~Pid;
  340. }
  341. else
  342. help(program, options);
  343. }
  344. break;
  345. case '?':
  346. help(program, options, 1);
  347. }
  348. _forall (int, index, optind, argc)
  349. {
  350. bool success;
  351. optarg = argv[index];
  352. pid = value<pid_t, 0, INT_MAX>(program, options, &success);
  353. if (success)
  354. {
  355. std::free(user);
  356. user = strdup(optarg);
  357. flags |= User;
  358. flags &= ~Pid;
  359. }
  360. else
  361. {
  362. flags |= Pid;
  363. flags &= ~User;
  364. }
  365. }
  366. return flags;
  367. }
  368. int main(int argc, char *argv[])
  369. {
  370. pid_t hpid, pid;
  371. char *user(NULL);
  372. int flags(options(argc, argv, hpid, pid, user));
  373. if (flags & Version)
  374. {
  375. std::cout << DTPSTREE_PROGRAM " " DTPSTREE_VERSION << std::endl;
  376. return 0;
  377. }
  378. char error[_POSIX2_LINE_MAX];
  379. kvm_t *kd(kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, error));
  380. if (!kd)
  381. errx(1, "%s", error);
  382. typedef kinfo_proc *InfoProc;
  383. int count;
  384. InfoProc procs(kvm_getprocs(kd, KERN_PROC_PROC, 0, &count));
  385. if (!procs)
  386. errx(1, "%s", kvm_geterr(kd));
  387. PidMap pids;
  388. _forall (InfoProc, proc, procs, procs + count)
  389. if (flags & ShowKernel || proc->ki_ppid != 0 || proc->ki_pid == 1)
  390. pids[proc->ki_pid] = new Proc(flags, kd, proc);
  391. enum { PidSort, NameSort } sort(flags & NumericSort ? PidSort : NameSort);
  392. _foreach (PidMap, pid, pids)
  393. {
  394. Proc *proc(pid->second);
  395. PidMap::iterator parent(pids.find(proc->parent()));
  396. if (parent != pids.end())
  397. parent->second->child(proc);
  398. }
  399. if (flags & Highlight)
  400. {
  401. PidMap::iterator pid(pids.find(hpid));
  402. if (pid != pids.end())
  403. pid->second->highlight();
  404. }
  405. NameMap names;
  406. _foreach (PidMap, pid, pids)
  407. {
  408. Proc *proc(pid->second);
  409. if (proc->root())
  410. switch (sort)
  411. {
  412. case PidSort:
  413. proc->printByPid();
  414. break;
  415. case NameSort:
  416. names.insert(NameMap::value_type(proc->name(), proc));
  417. }
  418. }
  419. switch (sort)
  420. {
  421. case NameSort:
  422. _foreach (NameMap, name, names)
  423. name->second->printByName();
  424. default:
  425. break;
  426. }
  427. std::setlocale(LC_ALL, "");
  428. char line[MB_LEN_MAX];
  429. int size(std::wctomb(line, L'└'));
  430. if (size != -1)
  431. std::cout << std::string(line, size) << std::endl;
  432. return 0;
  433. }
  434. // display a tree of processes