dtpstree.cpp 25 KB

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