dtpstree.cpp 26 KB

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