dtpstree.cpp 22 KB

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