dtpstree.cpp 24 KB

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