dtpstree.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. None = 0x000,
  32. Arguments = 0x001,
  33. Ascii = 0x002,
  34. Compact = 0x004,
  35. Vt100 = 0x008,
  36. ShowKernel = 0x010,
  37. Long = 0x020,
  38. NumericSort = 0x040,
  39. ShowPids = 0x084,
  40. UidChanges = 0x100,
  41. Unicode = 0x200,
  42. Version = 0x400
  43. };
  44. class Proc
  45. {
  46. kvm_t *kd_;
  47. kinfo_proc *proc_;
  48. Proc *parent_;
  49. PidMap childrenByPid_;
  50. NameMap childrenByName_;
  51. bool highlight_;
  52. public:
  53. Proc() {}
  54. Proc(kvm_t *kd, kinfo_proc *proc) : kd_(kd), proc_(proc) {}
  55. inline std::string name() const { return proc_->ki_comm; }
  56. inline pid_t parent() const { return proc_->ki_ppid; }
  57. inline pid_t pid() const { return proc_->ki_pid; }
  58. void child(Proc *proc)
  59. {
  60. proc->parent_ = this;
  61. childrenByPid_[proc->pid()] = proc;
  62. childrenByName_.insert(NameMap::value_type(proc->name(), proc));
  63. }
  64. void highlight()
  65. {
  66. highlight_ = true;
  67. if (parent_)
  68. parent_->highlight();
  69. }
  70. inline bool root() const { return !parent_; }
  71. void printByPid(const std::string &indent = "") const
  72. {
  73. std::cout << indent << print(indent.size()) << std::endl;
  74. _foreach (const PidMap, child, childrenByPid_)
  75. child->second->printByPid(indent + " ");
  76. }
  77. void printByName(const std::string &indent = "") const
  78. {
  79. std::cout << indent << print(indent.size()) << std::endl;
  80. _foreach (const NameMap, child, childrenByName_)
  81. child->second->printByName(indent + " ");
  82. }
  83. private:
  84. std::string print(std::string::size_type indent) const
  85. {
  86. std::ostringstream print;
  87. if (highlight_)
  88. print << "\033[1m";
  89. print << name();
  90. bool _pid(true), _args(true);
  91. bool change(true && parent_ && uid() != parent_->uid());
  92. bool parens((_pid || change) && !_args);
  93. if (parens)
  94. print << '(';
  95. if (_pid)
  96. {
  97. if (!parens)
  98. print << ',';
  99. print << pid();
  100. }
  101. if (change)
  102. {
  103. if (!parens || _pid)
  104. print << ',';
  105. print << user();
  106. }
  107. if (parens)
  108. print << ')';
  109. if (highlight_)
  110. print << "\033[22m";
  111. if (_args)
  112. print << args(indent + print.str().size());
  113. return print.str();
  114. }
  115. inline bool children() const { return childrenByPid_.size(); }
  116. inline uid_t uid() const { return proc_->ki_ruid; }
  117. std::string user() const
  118. {
  119. passwd *user(getpwuid(uid()));
  120. return user->pw_name;
  121. }
  122. std::string args(std::string::size_type indent) const
  123. {
  124. char **argv(kvm_getargv(kd_, proc_, 0));
  125. std::ostringstream args;
  126. if (argv && *argv)
  127. for (++argv; *argv; ++argv)
  128. {
  129. if (true && (indent += 1 + std::strlen(*argv)) > 75)
  130. {
  131. args << " ...";
  132. break;
  133. }
  134. args << ' ' << *argv;
  135. }
  136. return args.str();
  137. }
  138. };
  139. typedef kinfo_proc *InfoProc;
  140. int main(int argc, char *argv[])
  141. {
  142. int flags(0);
  143. pid_t hpid(-2);
  144. poptOption options[] = {
  145. { "arguments", 'a', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Arguments, "show command line arguments", NULL },
  146. { "ascii", 'A', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Ascii, "use ASCII line drawing characters", NULL },
  147. { "compact", 'c', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Compact, "don't compact identical subtrees", NULL },
  148. { "highlight-all", 'h', POPT_ARG_VAL, &hpid, -1, "highlight current process and its ancestors", NULL },
  149. { "highlight-pid", 'H', POPT_ARG_INT, &hpid, 0, "highlight this process and its ancestors", "PID" },
  150. { "vt100", 'G', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Vt100, "use VT100 line drawing characters", NULL },
  151. { "show-kernel", 'k', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowKernel, "show kernel processes", NULL },
  152. { "long", 'l', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Long, "don't truncate long lines", NULL },
  153. { "numeric-sort", 'n', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, NumericSort, "sort output by PID", NULL },
  154. { "show-pids", 'p', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, ShowPids, "show PIDs; implies -c", NULL },
  155. { "uid-changes", 'u', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, UidChanges, "show uid transitions", NULL },
  156. { "unicode", 'U', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Unicode, "use Unicode line drawing characters", NULL },
  157. { "version", 'V', POPT_ARG_VAL | POPT_ARGFLAG_OR, &flags, Version, "display version information", NULL },
  158. { "pid", '\0', POPT_ARG_INT, NULL, 0, "start at this PID", "PID" },
  159. { "user", '\0', POPT_ARG_STRING, NULL, 0, "show only trees rooted at processes of this user", "USER" },
  160. POPT_AUTOHELP
  161. POPT_TABLEEND
  162. };
  163. poptContext context(poptGetContext(NULL, argc, const_cast<const char **>(argv), options, 0));
  164. poptSetOtherOptionHelp(context, "[OPTION...] [PID|USER]");
  165. int count;
  166. if ((count = poptGetNextOpt(context)) < -1)
  167. {
  168. std::cerr << basename(argv[0]) << ": " << poptStrerror(count) << ": " << poptBadOption(context, 0) << std::endl;
  169. return 1;
  170. }
  171. poptFreeContext(context);
  172. if (flags & Version)
  173. {
  174. std::cout << DTPSTREE_PROGRAM " " DTPSTREE_VERSION << std::endl;
  175. return 0;
  176. }
  177. char error[_POSIX2_LINE_MAX];
  178. kvm_t *kd(kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, error));
  179. if (!kd)
  180. errx(1, "%s", error);
  181. InfoProc procs(kvm_getprocs(kd, KERN_PROC_PROC, 0, &count));
  182. if (!procs)
  183. errx(1, "%s", kvm_geterr(kd));
  184. PidMap pids;
  185. _forall (InfoProc, proc, procs, procs + count)
  186. if (proc->ki_ppid != 0 || proc->ki_pid == 1)
  187. pids[proc->ki_pid] = new Proc(kd, proc);
  188. enum { Pid, Name } sort(flags & NumericSort ? Pid : Name);
  189. _foreach (PidMap, pid, pids)
  190. {
  191. Proc *proc(pid->second);
  192. PidMap::iterator parent(pids.find(proc->parent()));
  193. if (parent != pids.end())
  194. parent->second->child(proc);
  195. }
  196. if (hpid != -2)
  197. {
  198. PidMap::iterator pid(pids.find(hpid != -1 ? hpid : getpid()));
  199. if (pid != pids.end())
  200. pid->second->highlight();
  201. }
  202. NameMap names;
  203. _foreach (PidMap, pid, pids)
  204. {
  205. Proc *proc(pid->second);
  206. if (proc->root())
  207. switch (sort)
  208. {
  209. case Pid:
  210. proc->printByPid();
  211. break;
  212. case Name:
  213. names.insert(NameMap::value_type(proc->name(), proc));
  214. }
  215. }
  216. switch (sort)
  217. {
  218. case Name:
  219. _foreach (NameMap, name, names)
  220. name->second->printByName();
  221. default:
  222. break;
  223. }
  224. std::setlocale(LC_ALL, "");
  225. char line[MB_LEN_MAX];
  226. int size(std::wctomb(line, L'└'));
  227. if (size != -1)
  228. std::cout << std::string(line, size) << std::endl;
  229. return 0;
  230. }
  231. // display a tree of processes