dtpstree.cpp 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168
  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 <cerrno>
  21. #include <climits>
  22. #include <cstdarg>
  23. #include <cstdio>
  24. #include <cstdlib>
  25. #include <cstring>
  26. #include <iostream>
  27. #include <map>
  28. #include <set>
  29. #include <sstream>
  30. #include <string>
  31. #include <vector>
  32. #ifdef __GLIBC__
  33. #include <bsd/stdlib.h>
  34. #else
  35. #include <libgen.h>
  36. #endif
  37. #ifdef __NetBSD__
  38. //#include <ncurses/curses.h>
  39. //#include <ncurses/term.h>
  40. #include <termcap.h>
  41. #else
  42. #include <curses.h>
  43. #include <term.h>
  44. #endif
  45. #include <err.h>
  46. #include <fcntl.h>
  47. #include <getopt.h>
  48. #include <kvm.h>
  49. #include <paths.h>
  50. #include <pwd.h>
  51. #include <sys/param.h>
  52. #include <sys/sysctl.h>
  53. #include <sys/user.h>
  54. #include <vis.h>
  55. #include "foreach.hpp"
  56. #define DTPSTREE_PROGRAM "dtpstree"
  57. #define DTPSTREE_VERSION "1.0.2"
  58. namespace kvm
  59. {
  60. template <typename Type>
  61. inline Type *getprocs(kvm_t *kd, int &count);
  62. template <typename Type>
  63. inline char **getargv(kvm_t *kd, const Type *proc);
  64. template <typename Type>
  65. inline pid_t pid(Type *proc);
  66. template <typename Type>
  67. inline pid_t ppid(Type *proc);
  68. template <typename Type>
  69. inline uid_t ruid(Type *proc);
  70. template <typename Type>
  71. inline char *comm(Type *proc);
  72. #if !defined(__NetBSD__) && !defined(__OpenBSD__)
  73. typedef kinfo_proc Proc;
  74. const int Flags(O_RDONLY);
  75. #ifndef KERN_PROC_PROC
  76. #define KERN_PROC_PROC KERN_PROC_ALL
  77. #endif
  78. template <>
  79. inline kinfo_proc *getprocs(kvm_t *kd, int &count)
  80. {
  81. return kvm_getprocs(kd, KERN_PROC_PROC, 0, &count);
  82. }
  83. template <>
  84. inline char **getargv(kvm_t *kd, const kinfo_proc *proc)
  85. {
  86. return kvm_getargv(kd, proc, 0);
  87. }
  88. template <>
  89. inline pid_t pid(kinfo_proc *proc)
  90. {
  91. return proc->ki_pid;
  92. }
  93. template <>
  94. inline pid_t ppid(kinfo_proc *proc)
  95. {
  96. return proc->ki_ppid;
  97. }
  98. template <>
  99. inline uid_t ruid(kinfo_proc *proc)
  100. {
  101. return proc->ki_ruid;
  102. }
  103. template <>
  104. inline char *comm(kinfo_proc *proc)
  105. {
  106. return proc->ki_comm;
  107. }
  108. #else
  109. typedef kinfo_proc2 Proc;
  110. const int Flags(KVM_NO_FILES);
  111. template <>
  112. inline kinfo_proc2 *getprocs(kvm_t *kd, int &count)
  113. {
  114. return kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof (kinfo_proc2), &count);
  115. }
  116. template <>
  117. inline char **getargv(kvm_t *kd, const kinfo_proc2 *proc)
  118. {
  119. return kvm_getargv2(kd, proc, 0);
  120. }
  121. template <>
  122. inline pid_t pid(kinfo_proc2 *proc)
  123. {
  124. return proc->p_pid;
  125. }
  126. template <>
  127. inline pid_t ppid(kinfo_proc2 *proc)
  128. {
  129. return proc->p_ppid;
  130. }
  131. template <>
  132. inline uid_t ruid(kinfo_proc2 *proc)
  133. {
  134. return proc->p_ruid;
  135. }
  136. template <>
  137. inline char *comm(kinfo_proc2 *proc)
  138. {
  139. return proc->p_comm;
  140. }
  141. #endif
  142. }
  143. enum Flags
  144. {
  145. Arguments = 0x0001,
  146. Ascii = 0x0002,
  147. NoCompact = 0x0004,
  148. Highlight = 0x0008,
  149. Vt100 = 0x0010,
  150. ShowKernel = 0x0020,
  151. Long = 0x0040,
  152. NumericSort = 0x0080,
  153. ShowPids = 0x0100,
  154. ShowTitles = 0x0200,
  155. UidChanges = 0x0400,
  156. Unicode = 0x0800,
  157. Version = 0x1000,
  158. Pid = 0x2000,
  159. User = 0x4000
  160. };
  161. enum Escape { None, BoxDrawing, Bright };
  162. struct Segment
  163. {
  164. size_t width_;
  165. Escape escape_;
  166. char *string_;
  167. inline Segment(size_t width, Escape escape, char *string) : width_(width), escape_(escape), string_(string) {}
  168. };
  169. struct Branch
  170. {
  171. std::string indentation_;
  172. bool done_;
  173. inline Branch(size_t indentation) : indentation_(indentation, ' '), done_(false) {}
  174. };
  175. class Tree
  176. {
  177. const uint16_t &flags_;
  178. bool vt100_;
  179. wchar_t horizontal_, vertical_, upAndRight_, verticalAndRight_, downAndHorizontal_;
  180. size_t maxWidth_, width_;
  181. bool max_, suppress_;
  182. std::vector<Segment> segments_;
  183. std::vector<Branch> branches_;
  184. bool first_, last_;
  185. size_t duplicate_;
  186. public:
  187. Tree(const uint16_t &flags) : flags_(flags), vt100_(false), maxWidth_(0), width_(0), max_(false), suppress_(false), duplicate_(0)
  188. {
  189. bool tty(isatty(1));
  190. if (flags & Ascii)
  191. {
  192. ascii:
  193. horizontal_ = L'-';
  194. vertical_ = L'|';
  195. upAndRight_ = L'`';
  196. verticalAndRight_ = L'|';
  197. downAndHorizontal_ = L'+';
  198. }
  199. else if (flags & Unicode)
  200. {
  201. unicode:
  202. if (!std::setlocale(LC_CTYPE, ""))
  203. goto vt100;
  204. horizontal_ = L'\x2500';
  205. vertical_ = L'\x2502';
  206. upAndRight_ = L'\x2514';
  207. verticalAndRight_ = L'\x251c';
  208. downAndHorizontal_ = L'\x252c';
  209. char *test;
  210. if (asprintf(&test, "%lc%lc%lc%lc%lc", horizontal_, vertical_, upAndRight_, verticalAndRight_, downAndHorizontal_) == -1)
  211. goto vt100;
  212. std::free(test);
  213. }
  214. else if (flags & Vt100)
  215. {
  216. vt100:
  217. vt100_ = true;
  218. horizontal_ = L'\x71';
  219. vertical_ = L'\x78';
  220. upAndRight_ = L'\x6d';
  221. verticalAndRight_ = L'\x74';
  222. downAndHorizontal_ = L'\x77';
  223. }
  224. else if (tty)
  225. goto unicode;
  226. else
  227. goto ascii;
  228. if (!(flags & Long) && tty)
  229. {
  230. # ifndef __NetBSD__
  231. int code;
  232. if (setupterm(NULL, 1, &code) == OK)
  233. {
  234. maxWidth_ = tigetnum(const_cast<char *>("cols"));
  235. if (tigetflag(const_cast<char *>("am")) && !tigetflag(const_cast<char *>("xenl")))
  236. suppress_ = true;
  237. }
  238. # else
  239. char buffer[1024], *term(std::getenv("TERM"));
  240. if (term != NULL && tgetent(buffer, term) == 1)
  241. {
  242. maxWidth_ = tgetnum("co");
  243. if (tgetflag("am") && !tgetflag("xn"))
  244. suppress_ = true;
  245. }
  246. # endif
  247. else
  248. maxWidth_ = 80;
  249. }
  250. }
  251. void print(const std::string &string, bool highlight, size_t duplicate)
  252. {
  253. Escape escape(vt100_ ? BoxDrawing : None);
  254. if (!first_ || flags_ & Arguments)
  255. {
  256. size_t last(branches_.size() - 1);
  257. _foreach (std::vector<Branch>, branch, branches_)
  258. {
  259. size_t width(branch->indentation_.size() + 2);
  260. if (_index == last)
  261. {
  262. wchar_t line;
  263. if (last_)
  264. {
  265. branch->done_ = true;
  266. line = upAndRight_;
  267. }
  268. else
  269. line = verticalAndRight_;
  270. print(width, escape, "%s%lc%lc", branch->indentation_.c_str(), line, horizontal_);
  271. }
  272. else
  273. print(width, escape, "%s%lc ", branch->indentation_.c_str(), branch->done_ ? ' ' : vertical_);
  274. }
  275. }
  276. else if (branches_.size())
  277. {
  278. wchar_t line;
  279. if (last_)
  280. {
  281. branches_.back().done_ = true;
  282. line = horizontal_;
  283. }
  284. else
  285. line = downAndHorizontal_;
  286. print(3, escape, "%lc%lc%lc", horizontal_, line, horizontal_);
  287. }
  288. size_t size(0);
  289. if (duplicate)
  290. {
  291. std::ostringstream string;
  292. string << duplicate << "*[";
  293. size = string.str().size();
  294. print(size, None, "%s", string.str().c_str());
  295. ++duplicate_;
  296. }
  297. print(string.size(), highlight ? Bright : None, "%s", string.c_str());
  298. branches_.push_back(Branch(!(flags_ & Arguments) ? size + string.size() + 1 : 2));
  299. }
  300. inline void printArg(const std::string &arg, bool last)
  301. {
  302. if (max_)
  303. return;
  304. size_t width(arg.size() + 1);
  305. width_ += width;
  306. char *string;
  307. if (maxWidth_ && !(flags_ & Long))
  308. if (width_ > maxWidth_ || !last && width_ + 3 >= maxWidth_)
  309. {
  310. width -= width_ - maxWidth_;
  311. width_ = maxWidth_;
  312. max_ = true;
  313. ssize_t size(static_cast<ssize_t>(width) - 4);
  314. asprintf(&string, " %s...", size > 0 ? arg.substr(0, size).c_str() : "");
  315. }
  316. else
  317. goto print;
  318. else
  319. print:
  320. asprintf(&string, " %s", arg.c_str());
  321. segments_.push_back(Segment(width, None, string));
  322. }
  323. inline void pop(bool children)
  324. {
  325. branches_.pop_back();
  326. if (!(flags_ & Arguments) && !children)
  327. done();
  328. }
  329. void done()
  330. {
  331. if (duplicate_)
  332. {
  333. print(duplicate_, None, "%s", std::string(duplicate_, ']').c_str());
  334. duplicate_ = 0;
  335. }
  336. size_t last(segments_.size() - 1);
  337. _foreach (std::vector<Segment>, segment, segments_)
  338. {
  339. const char *begin, *end;
  340. switch (segment->escape_)
  341. {
  342. case BoxDrawing:
  343. begin = !_index || (segment - 1)->escape_ != BoxDrawing ? "\033(0\017" : "";
  344. end = _index == last || (segment + 1)->escape_ != BoxDrawing ? "\033(B\017" : "";
  345. break;
  346. case Bright:
  347. begin = "\033[1m";
  348. end = "\033[22m";
  349. break;
  350. default:
  351. begin = end = ""; break;
  352. }
  353. std::printf("%s%s%s", begin, segment->string_, end);
  354. std::free(segment->string_);
  355. }
  356. segments_.clear();
  357. if (suppress_ && width_ == maxWidth_)
  358. std::fflush(stdout);
  359. else
  360. std::printf("\n");
  361. width_ = 0;
  362. max_ = false;
  363. }
  364. inline Tree &operator()(bool first, bool last)
  365. {
  366. first_ = first;
  367. last_ = last;
  368. return *this;
  369. }
  370. private:
  371. void print(size_t width, Escape escape, const char * format, ...)
  372. {
  373. if (max_)
  374. return;
  375. std::va_list args;
  376. va_start(args, format);
  377. char *string;
  378. vasprintf(&string, format, args);
  379. va_end(args);
  380. width_ += width;
  381. if (maxWidth_ && !(flags_ & Long))
  382. if (width_ > maxWidth_)
  383. {
  384. width -= width_ - maxWidth_;
  385. width_ = maxWidth_;
  386. max_ = true;
  387. bool previous = !width;
  388. if (previous)
  389. {
  390. std::free(string);
  391. const Segment &segment(segments_.back());
  392. width = segment.width_;
  393. string = segment.string_;
  394. }
  395. std::wstring wide(width - 1, '\0');
  396. std::mbstowcs(const_cast<wchar_t *>(wide.data()), string, wide.size());
  397. std::free(string);
  398. asprintf(&string, "%ls+", wide.c_str());
  399. if (previous)
  400. {
  401. segments_.back().string_ = string;
  402. return;
  403. }
  404. }
  405. segments_.push_back(Segment(width, escape, string));
  406. }
  407. };
  408. template <typename Type>
  409. struct Proc
  410. {
  411. typedef std::map<pid_t, Proc<Type> *> PidMap;
  412. typedef std::multimap<std::string, Proc<Type> *> NameMap;
  413. private:
  414. const uint16_t &flags_;
  415. kvm_t *kd_;
  416. Type *proc_;
  417. mutable std::string name_, print_;
  418. Proc<Type> *parent_;
  419. PidMap childrenByPid_;
  420. NameMap childrenByName_;
  421. bool highlight_, root_;
  422. int8_t compact_;
  423. size_t duplicate_;
  424. public:
  425. inline Proc(const uint16_t &flags, kvm_t *kd, Type *proc) : flags_(flags), kd_(kd), proc_(proc), parent_(NULL), highlight_(false), root_(false), compact_(-1), duplicate_(0) {}
  426. inline const std::string &name() const
  427. {
  428. if (name_.empty())
  429. name_ = visual(kvm::comm(proc_));
  430. return name_;
  431. }
  432. inline pid_t parent() const { return kvm::ppid(proc_); }
  433. inline pid_t pid() const { return kvm::pid(proc_); }
  434. inline void child(Proc *proc)
  435. {
  436. if (proc == this)
  437. return;
  438. proc->parent_ = this;
  439. childrenByPid_[proc->pid()] = proc;
  440. childrenByName_.insert(typename NameMap::value_type(proc->name(), proc));
  441. }
  442. inline void highlight()
  443. {
  444. highlight_ = true;
  445. if (parent_)
  446. parent_->highlight();
  447. }
  448. inline bool compact()
  449. {
  450. if (compact_ == -1)
  451. compact_ = compact(childrenByName_);
  452. return compact_;
  453. }
  454. bool root(uid_t uid)
  455. {
  456. if (flags_ & User)
  457. {
  458. if (uid == this->uid())
  459. {
  460. Proc *parent(parent_);
  461. while (parent)
  462. {
  463. if (parent->uid() == uid)
  464. return false;
  465. parent = parent->parent_;
  466. }
  467. return root_ = true;
  468. }
  469. return false;
  470. }
  471. return root_ = !parent_;
  472. }
  473. inline void printByPid(Tree &tree) const
  474. {
  475. print(tree, childrenByPid_);
  476. }
  477. inline void printByName(Tree &tree) const
  478. {
  479. print(tree, childrenByName_);
  480. }
  481. static bool compact(NameMap &names)
  482. {
  483. Proc *previous(NULL);
  484. bool compact(true);
  485. _tforeach (NameMap, name, names)
  486. {
  487. Proc *proc(name->second);
  488. if (proc->duplicate_)
  489. continue;
  490. size_t duplicate(proc->compact());
  491. if (compact && duplicate && (!previous || proc->print() == previous->print()))
  492. previous = proc;
  493. else
  494. compact = false;
  495. size_t count(names.count(name->first));
  496. if (!duplicate || count == 1)
  497. continue;
  498. _forall(typename NameMap::iterator, n4me, (++name)--, names.upper_bound(name->first))
  499. {
  500. Proc *pr0c(n4me->second);
  501. if (pr0c->compact() && Proc::compact(proc, pr0c))
  502. duplicate += ++pr0c->duplicate_;
  503. }
  504. if (duplicate != 1)
  505. proc->duplicate_ = duplicate;
  506. }
  507. return compact;
  508. }
  509. private:
  510. inline std::string visual(const char *string) const
  511. {
  512. std::string visual(std::strlen(string) * 4 + 1, '\0');
  513. visual.resize(strvis(const_cast<char *>(visual.data()), string, VIS_TAB | VIS_NL | VIS_NOSLASH));
  514. return visual;
  515. }
  516. template <typename Map>
  517. void print(Tree &tree, const Map &children) const
  518. {
  519. if (duplicate_ == 1)
  520. return;
  521. print(tree);
  522. size_t size(children.size()), last(size - 1);
  523. _tforeach (const Map, child, children)
  524. {
  525. Proc<Type> *proc(child->second);
  526. bool l4st(_index + (proc->duplicate_ ? proc->duplicate_ - 1 : 0) == last);
  527. if (!l4st)
  528. {
  529. l4st = true;
  530. for (++child; child != _end; ++child)
  531. if (child->second->duplicate_ != 1)
  532. {
  533. l4st = false;
  534. break;
  535. }
  536. --child;
  537. }
  538. proc->print(tree(!_index, l4st), proc->template children<Map>());
  539. if (l4st)
  540. break;
  541. }
  542. tree.pop(size);
  543. }
  544. void print(Tree &tree) const
  545. {
  546. tree.print(print(), highlight_, duplicate_);
  547. if (flags_ & Arguments)
  548. {
  549. char **argv(kvm::getargv(kd_, proc_));
  550. if (argv && *argv)
  551. for (++argv; *argv; ++argv)
  552. tree.printArg(visual(*argv), !*(argv + 1));
  553. tree.done();
  554. }
  555. }
  556. const std::string &print() const
  557. {
  558. if (print_.empty())
  559. {
  560. std::ostringstream print;
  561. if (flags_ & ShowTitles)
  562. {
  563. char **argv(kvm::getargv(kd_, proc_));
  564. if (argv)
  565. print << visual(*argv);
  566. else
  567. print << name();
  568. }
  569. else
  570. print << name();
  571. bool p1d(flags_ & ShowPids), args(flags_ & Arguments);
  572. bool change(flags_ & UidChanges && (root_ ? !(flags_ & User) && uid() : parent_ && uid() != parent_->uid()));
  573. bool parens((p1d || change) && !args);
  574. if (parens)
  575. print << '(';
  576. if (p1d)
  577. {
  578. if (!parens)
  579. print << ',';
  580. print << pid();
  581. }
  582. if (change)
  583. {
  584. if (!parens || p1d)
  585. print << ',';
  586. passwd *user(getpwuid(uid()));
  587. print << user->pw_name;
  588. }
  589. if (parens)
  590. print << ')';
  591. print_ = print.str();
  592. }
  593. return print_;
  594. }
  595. inline uid_t uid() const { return kvm::ruid(proc_); }
  596. template <typename Map>
  597. inline const Map &children() const;
  598. inline bool hasChildren() const { return childrenByName_.size(); }
  599. inline Proc<Type> *child() const { return childrenByName_.begin()->second; }
  600. inline static bool compact(Proc<Type> *one, Proc<Type> *two)
  601. {
  602. if (one->print() != two->print())
  603. return false;
  604. if (one->hasChildren() != two->hasChildren())
  605. return false;
  606. if (one->hasChildren() && !compact(one->child(), two->child()))
  607. return false;
  608. if (two->highlight_)
  609. one->highlight_ = true;
  610. return true;
  611. }
  612. };
  613. template <> template <>
  614. inline const Proc<kvm::Proc>::PidMap &Proc<kvm::Proc>::children() const
  615. {
  616. return childrenByPid_;
  617. }
  618. template <> template <>
  619. inline const Proc<kvm::Proc>::NameMap &Proc<kvm::Proc>::children() const
  620. {
  621. return childrenByName_;
  622. }
  623. static void help(char *program, option options[], int code = 0)
  624. {
  625. std::printf("Usage: %s [options] [PID|USER]\n\nOptions:\n", basename(program));
  626. for (option *option(options); option->name; ++option)
  627. {
  628. std::string name(option->name);
  629. std::ostringstream arguments;
  630. switch (option->val)
  631. {
  632. case 'H':
  633. if (name != "highlight")
  634. continue;
  635. arguments << "-H[PID], --highlight[=PID]"; break;
  636. case 0:
  637. if (name == "pid")
  638. arguments << "PID, --pid=PID";
  639. else if (name == "user")
  640. arguments << "USER, --user=USER";
  641. else
  642. goto argument;
  643. break;
  644. case 'c':
  645. if (name != "no-compact")
  646. continue;
  647. default:
  648. arguments << '-' << static_cast<char>(option->val) << ", ";
  649. argument:
  650. arguments << "--" << name;
  651. }
  652. const char *description("");
  653. switch (option->val)
  654. {
  655. case 'a':
  656. description = "show command line arguments"; break;
  657. case 'A':
  658. description = "use ASCII line drawing characters"; break;
  659. case 'c':
  660. description = "don't compact identical subtrees"; break;
  661. case 'h':
  662. description = "show this help message and exit"; break;
  663. case 'H':
  664. description = "highlight the current process (or PID) and its\n ancestors"; break;
  665. case 'G':
  666. description = "use VT100 line drawing characters"; break;
  667. case 'k':
  668. description = "show kernel processes"; break;
  669. case 'l':
  670. description = "don't truncate long lines"; break;
  671. case 'n':
  672. description = "sort output by PID"; break;
  673. case 'p':
  674. description = "show PIDs; implies -c"; break;
  675. case 't':
  676. description = "show process titles"; break;
  677. case 'u':
  678. description = "show uid transitions"; break;
  679. case 'U':
  680. description = "use Unicode line drawing characters"; break;
  681. case 'V':
  682. description = "show version information and exit"; break;
  683. case 0:
  684. if (name == "pid")
  685. description = "show only the tree rooted at the process PID";
  686. else if (name == "user")
  687. description = "show only trees rooted at processes of USER";
  688. }
  689. std::printf(" %-27s %s\n", arguments.str().c_str(), description);
  690. }
  691. std::exit(code);
  692. }
  693. template <typename Type, long minimum, long maximum>
  694. static Type value(char *program, option options[], bool *success = NULL)
  695. {
  696. char *end;
  697. long value(std::strtol(optarg, &end, 0));
  698. errno = 0;
  699. if (optarg == end || *end != '\0')
  700. if (success)
  701. *success = false;
  702. else
  703. {
  704. warnx("Number is invalid: \"%s\"", optarg);
  705. help(program, options, 1);
  706. }
  707. else if (value < minimum || value == LONG_MIN && errno == ERANGE)
  708. {
  709. warnx("Number is too small: \"%s\"", optarg);
  710. help(program, options, 1);
  711. }
  712. else if (value > maximum || value == LONG_MAX && errno == ERANGE)
  713. {
  714. warnx("Number is too large: \"%s\"", optarg);
  715. help(program, options, 1);
  716. }
  717. else if (success)
  718. *success = true;
  719. return value;
  720. }
  721. static uint16_t options(int argc, char *argv[], pid_t &hpid, pid_t &pid, char *&user)
  722. {
  723. option options[] = {
  724. { "arguments", no_argument, NULL, 'a' },
  725. { "ascii", no_argument, NULL, 'A' },
  726. { "compact", no_argument, NULL, 'c' },
  727. { "no-compact", no_argument, NULL, 'c' },
  728. { "help", no_argument, NULL, 'h' },
  729. { "highlight", optional_argument, NULL, 'H' },
  730. { "highlight-all", no_argument, NULL, 'H' },
  731. { "highlight-pid", required_argument, NULL, 'H' },
  732. { "vt100", no_argument, NULL, 'G' },
  733. { "show-kernel", no_argument, NULL, 'k' },
  734. { "long", no_argument, NULL, 'l' },
  735. { "numeric-sort", no_argument, NULL, 'n' },
  736. { "show-pids", no_argument, NULL, 'p' },
  737. { "show-titles", no_argument, NULL, 't' },
  738. { "uid-changes", no_argument, NULL, 'u' },
  739. { "unicode", no_argument, NULL, 'U' },
  740. { "version", no_argument, NULL, 'V' },
  741. { "pid", required_argument, NULL, 0 },
  742. { "user", required_argument, NULL, 0 },
  743. { NULL, 0, NULL, 0 }
  744. };
  745. int option, index;
  746. uint16_t flags(0);
  747. char *program(argv[0]);
  748. while ((option = getopt_long(argc, argv, "aAchH::GklnptuUV", options, &index)) != -1)
  749. switch (option)
  750. {
  751. case 'a':
  752. flags |= Arguments | NoCompact; break;
  753. case 'A':
  754. flags |= Ascii;
  755. flags &= ~Vt100;
  756. flags &= ~Unicode;
  757. break;
  758. case 'c':
  759. flags |= NoCompact; break;
  760. case 'h':
  761. help(program, options);
  762. case 'H':
  763. hpid = optarg ? value<pid_t, 0, INT_MAX>(program, options) : getpid();
  764. flags |= Highlight;
  765. break;
  766. case 'G':
  767. flags |= Vt100;
  768. flags &= ~Ascii;
  769. flags &= ~Unicode;
  770. break;
  771. case 'k':
  772. flags |= ShowKernel; break;
  773. case 'l':
  774. flags |= Long; break;
  775. case 'n':
  776. flags |= NumericSort; break;
  777. case 'p':
  778. flags |= NoCompact | ShowPids; break;
  779. case 't':
  780. flags |= ShowTitles; break;
  781. case 'u':
  782. flags |= UidChanges; break;
  783. case 'U':
  784. flags |= Unicode;
  785. flags &= ~Ascii;
  786. flags &= ~Vt100;
  787. break;
  788. case 'V':
  789. flags |= Version; break;
  790. case 0:
  791. {
  792. std::string option(options[index].name);
  793. if (option == "pid")
  794. {
  795. pid = value<pid_t, 0, INT_MAX>(program, options);
  796. flags |= Pid;
  797. flags &= ~User;
  798. }
  799. else if (option == "user")
  800. {
  801. std::free(user);
  802. user = strdup(optarg);
  803. flags |= User;
  804. flags &= ~Pid;
  805. }
  806. }
  807. break;
  808. case '?':
  809. help(program, options, 1);
  810. }
  811. _forall (int, index, optind, argc)
  812. {
  813. bool success(false);
  814. optarg = argv[index];
  815. pid = value<pid_t, 0, INT_MAX>(program, options, &success);
  816. if (success)
  817. {
  818. flags |= Pid;
  819. flags &= ~User;
  820. }
  821. else
  822. {
  823. std::free(user);
  824. user = strdup(optarg);
  825. flags |= User;
  826. flags &= ~Pid;
  827. }
  828. }
  829. return flags;
  830. }
  831. template <typename Type, int Flags>
  832. static void tree(pid_t hpid, pid_t pid, uint16_t flags, uid_t uid)
  833. {
  834. char error[_POSIX2_LINE_MAX];
  835. kvm_t *kd(kvm_openfiles(NULL, _PATH_DEVNULL, NULL, Flags, error));
  836. if (!kd)
  837. errx(1, "%s", error);
  838. int count;
  839. Type *procs(kvm::getprocs<Type>(kd, count));
  840. if (!procs)
  841. errx(1, "%s", kvm_geterr(kd));
  842. typedef Type *Pointer;
  843. typename Proc<Type>::PidMap pids;
  844. _forall (Pointer, proc, procs, procs + count)
  845. if (flags & ShowKernel || kvm::ppid(proc) != 0 || kvm::pid(proc) == 1)
  846. pids[kvm::pid(proc)] = new Proc<Type>(flags, kd, proc);
  847. enum { PidSort, NameSort } sort(flags & NumericSort ? PidSort : NameSort);
  848. _tforeach (typename Proc<Type>::PidMap, pid, pids)
  849. {
  850. Proc<Type> *proc(pid->second);
  851. typename Proc<Type>::PidMap::iterator parent(pids.find(proc->parent()));
  852. if (parent != pids.end())
  853. parent->second->child(proc);
  854. }
  855. if (flags & Highlight)
  856. {
  857. typename Proc<Type>::PidMap::iterator pid(pids.find(hpid));
  858. if (pid != pids.end())
  859. pid->second->highlight();
  860. }
  861. Tree tree(flags);
  862. if (flags & Pid)
  863. {
  864. typename Proc<Type>::PidMap::iterator p1d(pids.find(pid));
  865. if (p1d != pids.end())
  866. {
  867. Proc<Type> *proc(p1d->second);
  868. if (!(flags & NoCompact))
  869. proc->compact();
  870. switch (sort)
  871. {
  872. case PidSort:
  873. proc->printByPid(tree);
  874. break;
  875. case NameSort:
  876. proc->printByName(tree);
  877. }
  878. }
  879. }
  880. else
  881. {
  882. typename Proc<Type>::NameMap names;
  883. _tforeach (typename Proc<Type>::PidMap, pid, pids)
  884. {
  885. Proc<Type> *proc(pid->second);
  886. if (proc->root(uid))
  887. names.insert(typename Proc<Type>::NameMap::value_type(proc->name(), proc));
  888. }
  889. if (!(flags & NoCompact))
  890. Proc<Type>::compact(names);
  891. switch (sort)
  892. {
  893. case PidSort:
  894. _tforeach (typename Proc<Type>::PidMap, pid, pids)
  895. {
  896. Proc<Type> *proc(pid->second);
  897. if (proc->root(uid))
  898. proc->printByPid(tree);
  899. }
  900. break;
  901. case NameSort:
  902. _tforeach (typename Proc<Type>::NameMap, name, names)
  903. name->second->printByName(tree);
  904. }
  905. }
  906. _tforeach (typename Proc<Type>::PidMap, pid, pids)
  907. delete pid->second;
  908. }
  909. int main(int argc, char *argv[])
  910. {
  911. pid_t hpid(0), pid(0);
  912. char *user(NULL);
  913. uint16_t flags(options(argc, argv, hpid, pid, user));
  914. if (flags & Version)
  915. {
  916. std::printf(DTPSTREE_PROGRAM " " DTPSTREE_VERSION "\n");
  917. return 0;
  918. }
  919. uid_t uid(0);
  920. if (flags & User)
  921. {
  922. errno = 0;
  923. passwd *us3r(getpwnam(user));
  924. if (!us3r)
  925. errno ? err(1, NULL) : errx(1, "Unknown user: \"%s\"", user);
  926. uid = us3r->pw_uid;
  927. }
  928. tree<kvm::Proc, kvm::Flags>(hpid, pid, flags, uid);
  929. return 0;
  930. }
  931. // display a tree of processes