dtpstree.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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 <kvm.h>
  16. #include <libgen.h>
  17. #include <paths.h>
  18. #include <popt.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. int main(int argc, char *argv[])
  154. {
  155. int flags(0);
  156. pid_t hpid, pid;
  157. char *user(NULL);
  158. {
  159. poptOption options[] = {
  160. { "arguments", 'a', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Arguments, "show command line arguments", NULL },
  161. { "ascii", 'A', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Ascii, "use ASCII line drawing characters", NULL },
  162. { "compact", 'c', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Compact, "don't compact identical subtrees", NULL },
  163. { "highlight-all", 'h', POPT_ARG_NONE, NULL, 'h', "highlight current process and its ancestors", NULL },
  164. { "highlight-pid", 'H', POPT_ARG_INT, &hpid, 'H', "highlight this process and its ancestors", "PID" },
  165. { "vt100", 'G', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Vt100, "use VT100 line drawing characters", NULL },
  166. { "show-kernel", 'k', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowKernel, "show kernel processes", NULL },
  167. { "long", 'l', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Long, "don't truncate long lines", NULL },
  168. { "numeric-sort", 'n', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, NumericSort, "sort output by PID", NULL },
  169. { "show-pids", 'p', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowPids, "show PIDs; implies -c", NULL },
  170. { "show-titles", 't', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowTitles, "show process titles", NULL },
  171. { "uid-changes", 'u', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, UidChanges, "show uid transitions", NULL },
  172. { "unicode", 'U', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Unicode, "use Unicode line drawing characters", NULL },
  173. { "version", 'V', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Version, "display version information", NULL },
  174. { "pid", '\0', POPT_ARG_INT, &pid, 'p', "start at this PID", "PID" },
  175. { "user", '\0', POPT_ARG_STRING, &user, 'u', "show only trees rooted at processes of this user", "USER" },
  176. POPT_AUTOHELP
  177. POPT_TABLEEND
  178. };
  179. poptContext context(poptGetContext(NULL, argc, const_cast<const char **>(argv), options, 0));
  180. int versionArgc;
  181. const char **versionArgv;
  182. poptParseArgvString("-V", &versionArgc, &versionArgv);
  183. poptAlias versionAlias = { NULL, 'v', versionArgc, versionArgv };
  184. poptAddAlias(context, versionAlias, 0);
  185. poptSetOtherOptionHelp(context, "[OPTION...] [PID|USER]");
  186. int option;
  187. while ((option = poptGetNextOpt(context)) >= 0)
  188. switch (option)
  189. {
  190. case 'h':
  191. hpid = getpid();
  192. case 'H':
  193. flags |= Highlight;
  194. break;
  195. case 'p':
  196. flags |= Pid;
  197. flags &= ~User;
  198. break;
  199. case 'u':
  200. flags |= User;
  201. flags &= ~Pid;
  202. }
  203. if (option != -1)
  204. errx(1, "%s: %s", poptStrerror(option), poptBadOption(context, 0));
  205. for (const char **arg(poptGetArgs(context)); arg && *arg; ++arg)
  206. {
  207. char *end;
  208. long value(std::strtol(*arg, &end, 0));
  209. if (*arg == end || *end != '\0')
  210. {
  211. std::free(user);
  212. user = strdup(*arg);
  213. flags |= User;
  214. flags &= ~Pid;
  215. }
  216. else if (value > INT_MAX || value < INT_MIN)
  217. errx(1, "%s: %s", poptStrerror(POPT_ERROR_OVERFLOW), *arg);
  218. else
  219. {
  220. pid = value;
  221. flags |= Pid;
  222. flags &= ~User;
  223. }
  224. }
  225. poptFreeContext(context);
  226. }
  227. if (flags & Version)
  228. {
  229. std::cout << DTPSTREE_PROGRAM " " DTPSTREE_VERSION << std::endl;
  230. return 0;
  231. }
  232. char error[_POSIX2_LINE_MAX];
  233. kvm_t *kd(kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, error));
  234. if (!kd)
  235. errx(1, "%s", error);
  236. typedef kinfo_proc *InfoProc;
  237. int count;
  238. InfoProc procs(kvm_getprocs(kd, KERN_PROC_PROC, 0, &count));
  239. if (!procs)
  240. errx(1, "%s", kvm_geterr(kd));
  241. PidMap pids;
  242. _forall (InfoProc, proc, procs, procs + count)
  243. if (flags & ShowKernel || proc->ki_ppid != 0 || proc->ki_pid == 1)
  244. pids[proc->ki_pid] = new Proc(flags, kd, proc);
  245. enum { PidSort, NameSort } sort(flags & NumericSort ? PidSort : NameSort);
  246. _foreach (PidMap, pid, pids)
  247. {
  248. Proc *proc(pid->second);
  249. PidMap::iterator parent(pids.find(proc->parent()));
  250. if (parent != pids.end())
  251. parent->second->child(proc);
  252. }
  253. if (flags & Highlight)
  254. {
  255. PidMap::iterator pid(pids.find(hpid));
  256. if (pid != pids.end())
  257. pid->second->highlight();
  258. }
  259. NameMap names;
  260. _foreach (PidMap, pid, pids)
  261. {
  262. Proc *proc(pid->second);
  263. if (proc->root())
  264. switch (sort)
  265. {
  266. case PidSort:
  267. proc->printByPid();
  268. break;
  269. case NameSort:
  270. names.insert(NameMap::value_type(proc->name(), proc));
  271. }
  272. }
  273. switch (sort)
  274. {
  275. case NameSort:
  276. _foreach (NameMap, name, names)
  277. name->second->printByName();
  278. default:
  279. break;
  280. }
  281. std::setlocale(LC_ALL, "");
  282. char line[MB_LEN_MAX];
  283. int size(std::wctomb(line, L'└'));
  284. if (size != -1)
  285. std::cout << std::string(line, size) << std::endl;
  286. return 0;
  287. }
  288. // display a tree of processes