dtpstree.cpp 11 KB

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