dtpstree.cpp 26 KB

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