dtpstree.cpp 24 KB

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