dtpstree.cpp 26 KB

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