From a486d28ab84bf75365fc428bbce3f3272437cbe3 Mon Sep 17 00:00:00 2001 From: Hasan Wan Date: Fri, 25 May 2012 16:47:13 +0800 Subject: [PATCH] Updated with Tizen:Base source codes --- AUTHORS | 50 + BUGS | 74 + COPYING | 340 ++ COPYING.LIB | 481 +++ CodingStyle | 101 + Makefile | 261 ++ NEWS | 368 +++ README | 72 + README.top | 545 ++++ TODO | 151 + dummy.c | 31 + free.1 | 47 + free.c | 122 + kill.1 | 118 + minimal.c | 670 ++++ packaging/FAQ | 82 + packaging/MD5SUM | 1 + packaging/fix-makefile-error-with-as-needed.patch | 13 + packaging/procps-3.1.15-misc.patch | 35 + packaging/procps-3.2.1-selinux-workaround.patch | 13 + packaging/procps-3.2.3-FAQ.patch | 46 + packaging/procps-3.2.3-noproc.patch | 18 + packaging/procps-3.2.3-pseudo.patch | 18 + packaging/procps-3.2.4-0x9b.patch | 27 + packaging/procps-3.2.5-sysctl-writeonly.patch | 55 + packaging/procps-3.2.5-top-rc.patch | 14 + packaging/procps-3.2.6-libselinux.patch | 59 + packaging/procps-3.2.6-top-env-cpuloop.patch | 81 + packaging/procps-3.2.6-top-env-vmsize.patch | 53 + packaging/procps-3.2.7-free-hlmem.patch | 23 + packaging/procps-3.2.7-longcmd.patch | 22 + packaging/procps-3.2.7-ps-eip64.patch | 48 + packaging/procps-3.2.7-ps-libselinux.patch | 11 + packaging/procps-3.2.7-ps-man-fmt.patch | 12 + packaging/procps-3.2.7-ps-stime.patch | 71 + packaging/procps-3.2.7-psman.patch | 22 + packaging/procps-3.2.7-selinux.patch | 23 + packaging/procps-3.2.7-slabtop-once.patch | 113 + packaging/procps-3.2.7-sysctl-ignore.patch | 87 + packaging/procps-3.2.7-top-clrscr.patch | 19 + packaging/procps-3.2.7-top-cpu0.patch | 20 + packaging/procps-3.2.7-top-cpuint.patch | 12 + packaging/procps-3.2.7-top-manpage.patch | 24 + packaging/procps-3.2.7-top-remcpu.patch | 98 + packaging/procps-3.2.7-top-sorthigh.path | 37 + packaging/procps-3.2.7-vmstat-cpusteal.patch | 52 + packaging/procps-3.2.7-vmstat-header.patch | 12 + packaging/procps-3.2.7-vmstat-partstats-long.patch | 95 + .../procps-3.2.7-vmstat-partstats-reqwrites.patch | 12 + packaging/procps-3.2.7-vmstat-pgpg.patch | 235 ++ .../procps-3.2.7-vmstat-timestamp-manpage.patch | 22 + packaging/procps-3.2.7-vmstat-timestamp.patch | 117 + packaging/procps-3.2.7-w-best.patch | 16 + packaging/procps-3.2.7-watch-unicode.patch | 148 + packaging/procps-enable-core.patch | 11 + packaging/procps.changes | 474 +++ packaging/procps.spec | 188 ++ packaging/unknown-hz-value-fix.patch | 63 + pgrep.1 | 162 + pgrep.c | 732 +++++ pkill.1 | 1 + pmap.1 | 39 + pmap.c | 372 +++ proc/COPYING | 481 +++ proc/alloc.c | 49 + proc/alloc.h | 14 + proc/devname.c | 329 ++ proc/devname.h | 17 + proc/escape.c | 216 ++ proc/escape.h | 22 + proc/ksym.c | 631 ++++ proc/library.map | 24 + proc/module.mk | 130 + proc/procps.h | 112 + proc/pwcache.c | 77 + proc/pwcache.h | 17 + proc/readproc.c | 1054 ++++++ proc/readproc.h | 253 ++ proc/sig.c | 240 ++ proc/sig.h | 30 + proc/slab.c | 337 ++ proc/slab.h | 39 + proc/smaps.c | 171 + proc/sysinfo.c | 912 ++++++ proc/sysinfo.h | 135 + proc/version.c | 49 + proc/version.h | 31 + proc/wchan.h | 16 + proc/whattime.c | 87 + proc/whattime.h | 13 + procps.lsm | 15 + procps.spec | 52 + ps/COPYING | 481 +++ ps/HACKING | 53 + ps/TRANSLATION | 39 + ps/common.h | 337 ++ ps/display.c | 594 ++++ ps/global.c | 498 +++ ps/help.c | 48 + ps/it | 35 + ps/module.mk | 40 + ps/output.c | 1969 +++++++++++ ps/p | 6 + ps/parser.c | 1246 +++++++ ps/ps.1 | 1521 +++++++++ ps/regression | 26 + ps/select.c | 146 + ps/sortformat.c | 955 ++++++ pwdx.1 | 37 + pwdx.c | 95 + skill.1 | 123 + skill.c | 585 ++++ slabtop.1 | 124 + slabtop.c | 397 +++ snice.1 | 1 + sysctl.8 | 85 + sysctl.c | 516 +++ sysctl.conf | 60 + sysctl.conf.5 | 51 + t | 6 + tload.1 | 50 + tload.c | 156 + top.1 | 1617 ++++++++++ top.c | 3411 ++++++++++++++++++++ top.h | 567 ++++ uptime.1 | 34 + uptime.c | 17 + v | 6 + vmstat.8 | 198 ++ vmstat.c | 689 ++++ w.1 | 84 + w.c | 307 ++ watch.1 | 87 + watch.c | 326 ++ 134 files changed, 30515 insertions(+) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 COPYING.LIB create mode 100644 CodingStyle create mode 100644 Makefile create mode 100644 NEWS create mode 100644 README create mode 100644 README.top create mode 100644 TODO create mode 100644 dummy.c create mode 100644 free.1 create mode 100644 free.c create mode 100644 kill.1 create mode 100644 minimal.c create mode 100644 packaging/FAQ create mode 100644 packaging/MD5SUM create mode 100644 packaging/fix-makefile-error-with-as-needed.patch create mode 100644 packaging/procps-3.1.15-misc.patch create mode 100644 packaging/procps-3.2.1-selinux-workaround.patch create mode 100644 packaging/procps-3.2.3-FAQ.patch create mode 100644 packaging/procps-3.2.3-noproc.patch create mode 100644 packaging/procps-3.2.3-pseudo.patch create mode 100644 packaging/procps-3.2.4-0x9b.patch create mode 100644 packaging/procps-3.2.5-sysctl-writeonly.patch create mode 100644 packaging/procps-3.2.5-top-rc.patch create mode 100644 packaging/procps-3.2.6-libselinux.patch create mode 100644 packaging/procps-3.2.6-top-env-cpuloop.patch create mode 100644 packaging/procps-3.2.6-top-env-vmsize.patch create mode 100644 packaging/procps-3.2.7-free-hlmem.patch create mode 100644 packaging/procps-3.2.7-longcmd.patch create mode 100644 packaging/procps-3.2.7-ps-eip64.patch create mode 100644 packaging/procps-3.2.7-ps-libselinux.patch create mode 100644 packaging/procps-3.2.7-ps-man-fmt.patch create mode 100644 packaging/procps-3.2.7-ps-stime.patch create mode 100644 packaging/procps-3.2.7-psman.patch create mode 100644 packaging/procps-3.2.7-selinux.patch create mode 100644 packaging/procps-3.2.7-slabtop-once.patch create mode 100644 packaging/procps-3.2.7-sysctl-ignore.patch create mode 100644 packaging/procps-3.2.7-top-clrscr.patch create mode 100644 packaging/procps-3.2.7-top-cpu0.patch create mode 100644 packaging/procps-3.2.7-top-cpuint.patch create mode 100644 packaging/procps-3.2.7-top-manpage.patch create mode 100644 packaging/procps-3.2.7-top-remcpu.patch create mode 100644 packaging/procps-3.2.7-top-sorthigh.path create mode 100644 packaging/procps-3.2.7-vmstat-cpusteal.patch create mode 100644 packaging/procps-3.2.7-vmstat-header.patch create mode 100644 packaging/procps-3.2.7-vmstat-partstats-long.patch create mode 100644 packaging/procps-3.2.7-vmstat-partstats-reqwrites.patch create mode 100644 packaging/procps-3.2.7-vmstat-pgpg.patch create mode 100644 packaging/procps-3.2.7-vmstat-timestamp-manpage.patch create mode 100644 packaging/procps-3.2.7-vmstat-timestamp.patch create mode 100644 packaging/procps-3.2.7-w-best.patch create mode 100644 packaging/procps-3.2.7-watch-unicode.patch create mode 100644 packaging/procps-enable-core.patch create mode 100644 packaging/procps.changes create mode 100644 packaging/procps.spec create mode 100644 packaging/unknown-hz-value-fix.patch create mode 100644 pgrep.1 create mode 100644 pgrep.c create mode 100644 pkill.1 create mode 100644 pmap.1 create mode 100644 pmap.c create mode 100644 proc/COPYING create mode 100644 proc/alloc.c create mode 100644 proc/alloc.h create mode 100644 proc/devname.c create mode 100644 proc/devname.h create mode 100644 proc/escape.c create mode 100644 proc/escape.h create mode 100644 proc/ksym.c create mode 100644 proc/library.map create mode 100644 proc/module.mk create mode 100644 proc/procps.h create mode 100644 proc/pwcache.c create mode 100644 proc/pwcache.h create mode 100644 proc/readproc.c create mode 100644 proc/readproc.h create mode 100644 proc/sig.c create mode 100644 proc/sig.h create mode 100644 proc/slab.c create mode 100644 proc/slab.h create mode 100644 proc/smaps.c create mode 100644 proc/sysinfo.c create mode 100644 proc/sysinfo.h create mode 100644 proc/version.c create mode 100644 proc/version.h create mode 100644 proc/wchan.h create mode 100644 proc/whattime.c create mode 100644 proc/whattime.h create mode 100644 procps.lsm create mode 100644 procps.spec create mode 100644 ps/COPYING create mode 100644 ps/HACKING create mode 100644 ps/TRANSLATION create mode 100644 ps/common.h create mode 100644 ps/display.c create mode 100644 ps/global.c create mode 100644 ps/help.c create mode 100644 ps/it create mode 100755 ps/module.mk create mode 100644 ps/output.c create mode 100755 ps/p create mode 100644 ps/parser.c create mode 100644 ps/ps.1 create mode 100644 ps/regression create mode 100644 ps/select.c create mode 100644 ps/sortformat.c create mode 100644 pwdx.1 create mode 100644 pwdx.c create mode 100644 skill.1 create mode 100644 skill.c create mode 100644 slabtop.1 create mode 100644 slabtop.c create mode 100644 snice.1 create mode 100644 sysctl.8 create mode 100644 sysctl.c create mode 100644 sysctl.conf create mode 100644 sysctl.conf.5 create mode 100755 t create mode 100644 tload.1 create mode 100644 tload.c create mode 100644 top.1 create mode 100644 top.c create mode 100644 top.h create mode 100644 uptime.1 create mode 100644 uptime.c create mode 100755 v create mode 100644 vmstat.8 create mode 100644 vmstat.c create mode 100644 w.1 create mode 100644 w.c create mode 100644 watch.1 create mode 100644 watch.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0860b24 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,50 @@ +free: +Brian Edmonds + +oldps: +Branko Lankester +Michael K. Johnson +Michael Shields +Charles Blake +David Mossberger-Tang + +ps: +Albert Cahalan + +skill/kill/snice: +Albert Cahalan + +tload: +Branko Lankester +David Engel +Michael K. Johnson + +top: +Jim Warner + +oldtop: +Branko Lankester +Roger Binns +Robert Nation +Michael K. Johnson +Michael Shields +Tim Janik +Helmut Geyer +George Bonser + +uptime: +Larry Greenfield +Michael K. Johnson + +vmstat: +Henry Ware . + +w: +Larry Greenfield +Michael K. Johnson +Charles Blake + +watch: +Tony Rems +Mike Coleman + diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..ec45bde --- /dev/null +++ b/BUGS @@ -0,0 +1,74 @@ +BUG REPORTS + +Please read this file before sending in a bug report or patch. + +Also, PLEASE read the documentation first. 90% of the mail I get +complaining about procps is due to the sender not having read the +documentation! + + +Where to send +============= +Send comments, bug reports, patches, etc., to albert@users.sf.net + + +What to send +============ +It is much more useful to me if a program really crashes to recompile it +with make "CFLAGS=-ggdb -O", run it with "gdb prog" and "run" and send +me a stack trace ('bt' command). That said, any bug report is still +better than none. + +strace and ltrace output are very helpful: + + strace -o output-file ps --blah + bzip2 output-file + +I also like "ps --info" output, even if there isn't a ps problem. + +Kernel-Dependent Patches +======================== +If you send me patches which are specific to *running* with a particular +kernel version of /proc, please condition them with the runtime determined +variable 'linux_version_code' from libproc/version.c. It is the same +number as the macro LINUX_VERSION_CODE for which the kernel /proc fs +code was compiled. + +A macro is provide in libproc/version.h to construct the code from its +components, e.g. + if (linux_version_code < LINUX_VERSION(2,5,41)) + /* blah blah blah */ +A startup call to set_linux_version may also be necessary. + +Of course, if a bug is due to a change in kernel file formats, it would +be best to first try to generalize the parsing, since the code is then +more resilient against future change. + +Also unified diffs (diff -u) are my preference, context diffs (diff -c ) +are kind of usable, and standard diffs (diff) are more useless than a +generic text description of what you did. Just use + diff -Naurd oldfile newfile +or + diff -Naurd old-procps-dir new-procps-dir +to create your diffs and you will make me happy. Also make sure to +include a description of what the diff is for or I'm likely to ignore +it because of general lack of time... + +It might be nice to get rid of miscellaneous compiler warnings, but +don't bend over backwards to do it. + + +Code Structure +============== + +A goal is to encapsulate *all* parsing dependent on /proc +file formats into the libproc library. If the API is general enough +it can hopefully stabilize and then /proc changes might only require +updating libproc.so. Beyond that having the set of utilities be simple +command lines parsers and output formatters and encapsulating all kernel +divergence in libproc is the way to go. + +Hence if you are submitting a new program or are fixing an old one, keep +in mind that adding files to libproc which encapsulate such things is +more desirable than patching the actual driver program. (well, except +to move it toward the API of the library). diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..92b8903 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/CodingStyle b/CodingStyle new file mode 100644 index 0000000..962207d --- /dev/null +++ b/CodingStyle @@ -0,0 +1,101 @@ +If you start a new file, you get to choose the style. +If you change an existing file, follow the existing style. + +Hard tabs are OK, as long as you consider the tab stops to +be every 8 characters. You can also use 2, 3, or 4 spaces. +Tabs are kind of yucky, since cut-and-paste mangles them +sometimes and they make "diff -Naurd old new" output less +readable. + +Spaces within a line don't matter much, and won't be +considered part of the style. Just make it readable: + +if(x){ // OK +if( x ){ // OK +if (x) { // OK +if(x==y && a==b){ // OK +if(x == y && a == b){ // poor +if(x==y&&a==b){ // poor + +This is evil: + +szWinStallman +FoulCodingStyle (int iInsanity) + { + if (iInsanity) + { + GoHackEmacs () ; + } + else + { + SeekHelpForYourLisp () ; + } + } + + +Curly braces belong at the end of a line. If you must, go ahead +and make function bodies an exception to that rule. (as Linus does) + +Big fprintf() calls and similar go like this: + +fprintf(fd, "%d %d %d %d %d %d\n", + sdfsdf_sdfsdf + sdfs_iii, // not an example of good names! + iijjij, + kjfkkj_sdfssss_sfff, + sdflkjfdskj + sdf - sfds, + jksss, + sfssss + wwwwfwfw +); + +Keep these distinct: NULL, '\0', 0, 0.0 + +Command-line parsers need to be bomb-proof. It is not acceptable +to crash due to a messed up command-line. For an option "-x" that +takes an argument, accept both "-x arg" and "-xarg". Remember to +support "--" and "--version". + +Be extremely careful when handling data from other users. +For example, it is a security hole if /proc/123/cmdline can +overflow an array. It is often a security hole if you allow +non-ASCII characters to be printed. Assuming the console is +not in UTF-8 mode, all of these are bad: "\b\e\f\n\r\t\v\x9b". +(the "\x9b" is valid in UTF-8 mode, but equivalent to "\e[" +when not in UTF-8 mode -- which gives control of terminal +settings) It's best if you consider user-supplied data to +be unsafe, since this makes for less work in case the code +ends up needing to run setuid. Termcap data is user-supplied. +Except for the above security issues, don't bother to check +for something you can't handle... like printf() failing. +It is expected that /dev exists and so on. + +Remember that a read() may return early, with partial data +or with -1 and errno set to EINTR. You then must try again. + +char: may be signed or unsigned by default +int: always 32-bit +long long: always 64-bit +pointer: either 32-bit or 64-bit +long: same size as a pointer +KLONG: same size as a pointer or long IN THE KERNEL + +Functions used in just one file must be marked static. +Use the "const" and "restrict" keywords wherever you can. + +Put main() at the bottom of a file so you don't need all +those ugly forward declarations. + +Avoid the strcat() function. It is slow. For some odd +reason, snprintf() is faster than sprintf(). + +Reuse memory allocations when you can. When using realloc(), +do your increments by more than one. 25% is a nice amount. + +Avoid compile-time choices. They make documentation difficult, +and they are not friendly to binary distribution. + +Write programs that can handle a million processes without +getting hopelessly slow. Allow for /proc/123/cmdline to +be at least 128 kB. + +The LGPL license is strongly preferred. This allows use of +the code in the library. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09fb3ed --- /dev/null +++ b/Makefile @@ -0,0 +1,261 @@ +# procps Makefile +# Albert Cahalan, 2002-2004 +# +# Recursive make is considered harmful: +# http://google.com/search?q=%22recursive+make+considered+harmful%22 +# +# For now this Makefile uses explicit dependencies. The project +# hasn't grown big enough to need something complicated, and the +# dependency tracking files are an ugly annoyance. +# +# This file includes */module.mk files which add on to variables: +# FOO += bar/baz +# +# +# Set (or uncomment) SKIP if you wish to avoid something. +# For example, you may prefer the /bin/kill from util-linux or bsdutils. + + +VERSION := 3 +SUBVERSION := 2 +MINORVERSION := 8 +TARVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION) + +############ vars + +# so you can disable them or choose alternates +ldconfig := ldconfig +ln_f := ln -f +ln_sf := ln -sf +install := install -D --owner 0 --group 0 + +# Lame x86-64 /lib64 and /usr/lib64 abomination: +lib64 := lib$(shell [ -d /lib64 ] && echo 64) + +usr/bin := $(DESTDIR)/usr/bin/ +bin := $(DESTDIR)/bin/ +sbin := $(DESTDIR)/sbin/ +usr/proc/bin := $(DESTDIR)/usr/bin/ +man1 := $(DESTDIR)/usr/share/man/man1/ +man5 := $(DESTDIR)/usr/share/man/man5/ +man8 := $(DESTDIR)/usr/share/man/man8/ +lib := $(DESTDIR)/$(lib64)/ +usr/lib := $(DESTDIR)/usr/$(lib64)/ +usr/include := $(DESTDIR)/usr/include/ + +#SKIP := $(bin)kill $(man1)kill.1 + +BINFILES := $(usr/bin)uptime $(usr/bin)tload $(usr/bin)free $(usr/bin)w \ + $(usr/bin)top $(usr/bin)vmstat $(usr/bin)watch $(usr/bin)skill \ + $(usr/bin)snice $(bin)kill $(sbin)sysctl $(usr/bin)pmap \ + $(usr/proc/bin)pgrep $(usr/proc/bin)pkill $(usr/bin)slabtop \ + $(usr/proc/bin)pwdx + +MANFILES := $(man1)uptime.1 $(man1)tload.1 $(man1)free.1 $(man1)w.1 \ + $(man1)top.1 $(man1)watch.1 $(man1)skill.1 $(man1)kill.1 \ + $(man1)snice.1 $(man1)pgrep.1 $(man1)pkill.1 $(man1)pmap.1 \ + $(man5)sysctl.conf.5 $(man8)vmstat.8 $(man8)sysctl.8 \ + $(man1)slabtop.1 $(man1)pwdx.1 + +TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \ + Makefile procps.lsm procps.spec v t README.top CodingStyle \ + sysctl.conf minimal.c $(notdir $(MANFILES)) dummy.c \ + uptime.c tload.c free.c w.c top.c vmstat.c watch.c skill.c \ + sysctl.c pgrep.c top.h pmap.c slabtop.c pwdx.c + +# Stuff (tests, temporary hacks, etc.) left out of the standard tarball +# plus the top-level Makefile to make it work stand-alone. +_TARFILES := Makefile + +CURSES := -lncurses + +# This seems about right for the dynamic library stuff. +# Something like this is probably needed to make the SE Linux +# library loading not conflict with embedded systems stuff. +# +#ifeq ($(SHARED),1) +#ldl := -ldl +#LIBTYPE := -DSHAREDLIB +#else +#LIBTYPE := -DSTATICLIB +#endif + +# Preprocessor flags. +PKG_CPPFLAGS := -D_GNU_SOURCE -I proc +CPPFLAGS := -I/usr/include/ncurses +ALL_CPPFLAGS := $(PKG_CPPFLAGS) $(CPPFLAGS) + +# Left out -Wconversion due to noise in glibc headers. +# Left out -Wunreachable-code and -Wdisabled-optimization +# because gcc spews many useless warnings with them. +# +# Since none of the PKG_CFLAGS things are truly required +# to compile procps, they might best be moved to CFLAGS. +# On the other hand, they aren't normal -O -g things either. +# +# Note that -O2 includes -fomit-frame-pointer only if the arch +# doesn't lose some debugging ability. +# +PKG_CFLAGS := -fno-common -ffast-math \ + -W -Wall -Wshadow -Wcast-align -Wredundant-decls \ + -Wbad-function-cast -Wcast-qual -Wwrite-strings -Waggregate-return \ + -Wstrict-prototypes -Wmissing-prototypes +# Note that some stuff below is conditional on CFLAGS containing +# an option that starts with "-g". (-g, -g2, -g3, -ggdb, etc.) +CFLAGS := -O2 -s +ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS) + +PKG_LDFLAGS := -Wl,-warn-common +LDFLAGS := +ALL_LDFLAGS := $(PKG_LDFLAGS) $(LDFLAGS) + +############ Add some extra flags if gcc allows + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),tar) +ifneq ($(MAKECMDGOALS),extratar) +ifneq ($(MAKECMDGOALS),beta) + +# Unlike the kernel one, this check_gcc goes all the way to +# producing an executable. There might be a -m64 that works +# until you go looking for a 64-bit curses library. +check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o /dev/null $(CURSES) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) + +# Be 64-bit if at all possible. In a cross-compiling situation, one may +# do "make m64=-m32 lib64=lib" to produce 32-bit executables. DO NOT +# attempt to use a 32-bit executable on a 64-bit kernel. Packagers MUST +# produce separate executables for ppc and ppc64, s390 and s390x, +# i386 and x86-64, mips and mips64, sparc and sparc64, and so on. +# Failure to do so will cause data corruption. +m64 := $(call check_gcc,-m64,$(call check_gcc,-mabi=64,)) +ALL_CFLAGS += $(m64) + +ALL_CFLAGS += $(call check_gcc,-Wdeclaration-after-statement,) +ALL_CFLAGS += $(call check_gcc,-Wpadded,) +ALL_CFLAGS += $(call check_gcc,-Wstrict-aliasing,) + +# Adding -fno-gcse might be good for those files which +# use computed goto. +#ALL_CFLAGS += $(call check_gcc,-fno-gcse,) + +# if not debugging, enable things that could confuse gdb +ifeq (,$(findstring -g,$(filter -g%,$(CFLAGS)))) +ALL_CFLAGS += $(call check_gcc,-fweb,) +ALL_CFLAGS += $(call check_gcc,-frename-registers,) +ALL_CFLAGS += $(call check_gcc,-fomit-frame-pointer,) +endif + +# in case -O3 is enabled, avoid bloat +ALL_CFLAGS += $(call check_gcc,-fno-inline-functions,) + +endif +endif +endif +endif + +############ misc. + +# free.c pmap.c sysctl.c uptime.c vmstat.c watch.c pgrep.c skill.c tload.c top.c w.c +# utmp.c oldtop.c tmp-junk.c minimal.c + +.SUFFIXES: +.SUFFIXES: .a .o .c .s .h + +.PHONY: all clean do_all install tar extratar beta + +ALL := $(notdir $(BINFILES)) + +CLEAN := $(notdir $(BINFILES)) + +DIRS := + +INSTALL := $(BINFILES) $(MANFILES) + +# want this rule first, use := on ALL, and ALL not filled in yet +all: do_all + +-include */module.mk + +do_all: $(ALL) + +junk := DEADJOE *~ *.o core gmon.out + +# Remove $(junk) from all $(DIRS) +CLEAN += $(junk) $(foreach dir,$(DIRS),$(addprefix $(dir), $(junk))) + +########## +# not maintained because it isn't really needed: +# +#SRC := +#OBJ := $(patsubst %.c,%.o, $(filter %.c,$(SRC))) +# +#ifneq ($(MAKECMDGOALS),clean) +#-include $(OBJ:.o=.d) +#endif +# +#%.d: %.c +# depend.sh $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@ +############ + +# don't want to type "make procps-$(TARVERSION).tar.gz" +tar: $(TARFILES) + mkdir procps-$(TARVERSION) + (tar cf - $(TARFILES)) | (cd procps-$(TARVERSION) && tar xf -) + tar cf procps-$(TARVERSION).tar procps-$(TARVERSION) + gzip -9 procps-$(TARVERSION).tar + +extratar: $(_TARFILES) + mkdir procps-$(TARVERSION) + (tar cf - $(_TARFILES)) | (cd procps-$(TARVERSION) && tar xf -) + tar cf extra-$(TARVERSION).tar procps-$(TARVERSION) + gzip -9 extra-$(TARVERSION).tar + +beta: $(TARFILES) $(_TARFILES) + mkdir beta-$(TARVERSION) + (tar cf - $(TARFILES) $(_TARFILES)) | (cd beta-$(TARVERSION) && tar xf -) + tar cf beta-$(TARVERSION).tar beta-$(TARVERSION) + gzip -9 beta-$(TARVERSION).tar + +clean: + rm -f $(CLEAN) + +###### install + +$(BINFILES) : all + $(install) --mode a=rx $(notdir $@) $@ + +$(MANFILES) : all + $(install) --mode a=r $(notdir $@) $@ + +install: $(filter-out $(SKIP) $(addprefix $(DESTDIR),$(SKIP)),$(INSTALL)) + cd $(usr/bin) && $(ln_f) skill snice + cd $(usr/proc/bin) && $(ln_f) pgrep pkill + +############ prog.c --> prog.o + +top.o : top.h + +%.o : %.c + $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c -o $@ $< + +w.o: w.c + $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(W_SHOWFROM) -c $< + +############ prog.o --> prog + +pmap w uptime tload free sysctl vmstat utmp pgrep skill pwdx: % : %.o $(LIBPROC) + $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ + +slabtop top: % : %.o $(LIBPROC) + $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES) + +watch: % : %.o + $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES) + +############ progX --> progY + +snice kill: skill + $(ln_f) skill $@ + +pkill: pgrep + $(ln_f) pgrep pkill diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..60acc88 --- /dev/null +++ b/NEWS @@ -0,0 +1,368 @@ +procps-3.2.7 --> procps-3.2.8 + +ps: allow "+" in sort specifications, as in man page rh208217 +ps: recognize SCHED_ISO and SCHED_IDLE +ps: document SCHED_BATCH and add a "see also" for stime +ps: man page less ambiguous +top: normal exit code should be 0 #341272 #354255 rh199174 +top: misc fixes +pgrep: usage error should exit with 2 #413383 +vmstat: use EXIT_FAILURE -- thanks Yoshio Nakamura #425492 +sysctl: fix crash -- thanks Steinar Gunderson #423704 +watch: tolerate umlauts #207103 +pmap: range limits with -A low,high +update /dev/tty* info to May 2009 devices.txt +don't read off end of string const rh469495 rh498182 + +procps-3.2.6 --> procps-3.2.7 + +top: document H option -- thanks Tony Ernst +top: terabytes -- thanks Tony Ernst +top: CPU usage column width fixes -- thanks Tony Ernst +top: *roff change #326517 +ps: SCHED_BATCH is B +ps: fix s format (signals) output with thread display +watch: avoid integer overflow for the time delay +pwdx: buffer overflow fixed -- thanks Ulf Harnhammar +procps.spec needed a slash -- thanks Jesse Brandeburg +w: stale utmp entries snuck in via uninitialized var -- thanks Robert A Basch +pgrep/pkill: fix some realloc-related crashes #353894 +pgrep/pkill: g is criteria (PLD Linux, patch found in locked filing cabinet) +sysctl: use - for stdin (PLD Linux: beware of the leopard) +top: show CPU time stolen from a virtual machine + +procps-3.2.5 --> procps-3.2.6 + +vmstat: /proc/stat buffer big enough for 1024 CPUs +dietlibc needs termios.h for struct winsize -- thanks Thomas Ogrisegg +top: can do per-task display -- thanks John Blackwood rh114012 +more MIPS crud -- thanks Jim Gifford and Ryan Oliver +begin prep for setuid +top: fix %CPU max on 2..9 CPU SMP -- thanks Ga*tan LEURENT rh110555 +ps: fix crash related to realloc -- thanks David Houlder +ps: man page more detailed #334682 +spelling fixes #300333 #334684 #334685 +top: crash on resize fixed -- thanks Michal Maruska #320289 +vmstat: -p handles /dev/ and does not overflow #319523 #330969 + +procps-3.2.4 --> procps-3.2.5 + +display problem on 64-bit systems fixed #287947 +top: variable-width PID and PPID +top: variable-width %CPU rh110555 +sysctl: better error messages +ps: security labels can contain any printable ASCII +top: help and version message on stdout, with exit(0) #283541 +ps: SIGTSTP and SIGTTOU shouldn't print bug email address #246123 +slabtop: compile with glibc 2.2.17 (and older, likely) +slabtop: fix overflow on huge NUMA boxes #264640 +slabtop: accept any slabinfo 2.x format g77301 #287691 rh145369 rh145906 +ps: alignment after WCHAN fixed ub5385 +pmap: when no -x or -d option, show full path rh142751 + +procps-3.2.3 --> procps-3.2.4 + +support 64-bit MIPS with n32 binary +sparc32 optimized for sparc32 again +pwdx: new command -- thanks Nicholas Miell +ps: UTF-8 username + command -- thanks Karel Zak rh134763,rh112518,rh134780 +ps: more room for some columns +ps: tolerate SubDomain security module CONTEXT/LABEL data #277401 +watch: passes COLUMNS and LINES in environment +top: in batch mode, tolerate unknown $TERM -- thanks Daniel Walsh +pkill: quiet about processes that die before kill() + +procps-3.2.2 --> procps-3.2.3 + +avoid truncating long usernames +avoid warning about -lncurses when not linking (more) +new names for shared libraries (packagers: watch out!) +"make install" no longer rebuilds everything +wchan now '*' for multi-threaded processes +ps: new man page -- thanks Nicolas Francois +ps: STAT shows l for multi-threaded processes +vmstat: some overflow problems fixed -- thanks Holger Kiehl +sysctl: man page fix + +procps-3.2.1 --> procps-3.2.2 + +new packager (downstream maintainer) guidelines in README +move striping from install command to CFLAGS +new gcc options in use: -fweb, -frename-registers, etc. +avoid warning about -lncurses when not linking -- thanks FLWM +tolerate IA-64 headers without PAGE_SIZE +ps: k option, same as --sort +ps: personality-specific -x support (HP-UX and SVR4-MP) +pgrep: man page SEE ALSO updated #226817 +sysctl: -q and -N options +sysctl: better error handling of failed writes +top: tolerate sparse CPU numbering +top: try to handle terminals lacking rmam and smam #235003 +top: xterm dislikes clear-to-eol at eol (char lost) +vmstat: fixed -d +watch: allow sub-second intervals -- thanks Thomas Stewart + +procps-3.2.0 --> procps-3.2.1 + +build even w/ curses in an odd location -- thanks to Segher Boessenkool +ps: STAT flags for fg process group and session leader +ps: STAT flags for swapped out process dropped (was broken) +ps: new -M and Z options for security data (SE Linux, etc.) +slabtop: detect broken /proc/slabinfo -- thanks to Fabian Frederick +slabtop: ensure that error messages show up on the screen -- FF again + +procps-3.1.15 --> procps-3.2.0 + +build on IA-64 again #227933 +pmap: output like Solaris 9, not Solaris 7 +ps: also handle SELinux on the 2.4.xx kernels +top: during a ^Z, the terminal was messed up #228822 +future-proof the tty handling (thanks to Zhou Wei) +slabtop (Chris Rivera and Robert Love) #226778 +pmap: detect the primary stack +pmap: -d format +free: report high and low memory + +procps-3.1.14 --> procps-3.1.15 + +install to /lib64 if it exists +hide kernel PID bug (Linux 2.4.13-pre1 to 2.4.MAX) #217278 #219730 #217525 #224470 +ps: faster threaded display +top: auto-margin problem #217559 +ps: support NSA SELinux, all builds, Linux 2.6+ #193648 +sysctl: tweak man page for ESR's broken parser + +procps-3.1.13 --> procps-3.1.14 + +top: displays on more genuine serial terminals +handle 32-bit dev_t of Linux 2.6 +ps: finally, m and -m satisfy the original design +ps: distinct per-thread and whole-process pending signals + +procps-3.1.12 --> procps-3.1.13 + +ps: can display NPTL threads w/ kernel patch +no seLinux for now (new kernel interface) + +procps-3.1.11 --> procps-3.1.12 + +ps: explicit width ("ps -o pid,wchan:42,args") +ps: $PS_FORMAT works properly #201575 +top: new Linux 2.6.0-test4 CPU stats shown +top: multiple -p options work again +top: fixed 4 GB wrap-around +ps: has a set of tests to ensure correctness +man page: /var/run/utmp, not /etc/utmp #206583 +required flags moved out of CFLAGS #205429 +RPM generation handles /lib64 +WCHAN skips leading '.' +vmstat: numerous new features + +procps-3.1.10 --> procps-3.1.11 + +compile with gcc 2.95 again (C99 issue) + +procps-3.1.9 --> procps-3.1.10 + +handle GPLONLY_ symbols #143549 #188374 +kill: better man page +skill: better man page +ps: PID-like columns change width as needed +top: COMMAND instead of Command +vmstat: -m displays slabinfo +vmstat: -d displays disk stats + +procps-3.1.8 --> procps-3.1.9 + +memory sizes fixed for 64-bit w/ gcc 3.x #194376 #191933 +ps: detect broken OS install w/o /proc mounted #172735 +top: fix suspend/resume behavior +top: ditch warning until a GOOD interface is found #188271 +kill: more info in the man page #182414 +ps: document the -o, o, -O, and O options #169301 +vmstat: choose units you like: 1000, 1024, 1000000... + +procps-3.1.7 --> procps-3.1.8 + +top: fix keyboard handling (help screen, etc.) + +procps-3.1.6 --> procps-3.1.7 + +Makefile: made SKIP feature easier to use +watch: --help now explains -t, --no-title #182246 +ps: warning directs users to the FAQ +top: batch mode can refresh by fractional seconds +top: faster start-up +top: do not refresh like crazy +ps: better crash message + +procps-3.1.5 --> procps-3.1.6 + +handle the 2.5.61 kernel +top: memory leak fixed +ps: new --ppid option selects by PPID +watch: new --no-title option #179862 +handle SPARC Linux badness +rare crash fixed +compile with gcc 2.91.xx again +more informative "ps --info" +README update +ps: compare more with "ps -C verylongname" #178127 + +procps-3.1.4 --> procps-3.1.5 + +ancient (2.x.xx era) data corruption fixed +serious hidden-process problem (3.1.3+) fixed +w: escape sequence vulnerability fixed + +procps-3.1.3 --> procps-3.1.4 + +top: was trashing every "3" in a command name +top: when killing a process, the PID was cut at a "3" +top: more reliable %CPU +update copyright dates (GPL & LGPL require this) +RPM generation works now + +procps-3.1.2 --> procps-3.1.3 + +uses /proc/*/wchan files when available +top: user selection +sysctl: add -e for Red Hat 8.0 boot scripts +sysctl: the obvious --help, -V, and --version +sysctl: some command line error checking +w: stdout, not stderr -- thanks to Sander van Malssen + +procps-3.1.1 --> procps-3.1.2 + +better RPM generation +use C99 features +some seLinux fixes +now count Inact_laundry as needed #172163 +ps: fewer globals +ps: hardware-enforced buffer protection +ps: 1 kB smaller +top: B command added (for bold on/off) +top: handle old (and future) config files +top: man page tweak +top: old sort keys #167249 +top: out-of-bounds RT as "RT" +top: several times faster +top: t command fixed +vmstat: -f +vmstat: -s +w: much faster +watch: don't drop empty lines #171005 +watch: re-indented + +procps-3.1.0 --> procps-3.1.1 + +vmstat faster on 2.5.xx kernels +vmstat header fixed +vmstat -a re-fixed + +procps-3.0.5 --> procps-3.1.0 + +vmstat displays IO-wait time instead of bogus "w" +can build w/o shared library (set SHARED=0) +when IO-wait hidden, count as idle, not as sys +pmap command added (like Sun has) +do not crash GNU make 3.79 +top slightly faster + +procps-3.0.4 --> procps-3.0.5 + +top tolerates super-wide displays +better (?) RPM generation +XConsole and top.desktop removed +old build system removed +code cleanup +pgrep and pkill get "-o" (oldest matching process) +had vmstat "bi" and "bo" output interchanged on 2.5.xx +fix man page tbl directives +top man page cleaned up + +procps-3.0.3 --> procps-3.0.4 + +make top go faster +Linux 2.2.xx ELF note warning removed +only show IO-wait on recent kernels +fix top's SMP stats +fix top for "dumb" and "vt510" terminals +in top, limit the priority values to -99 ... 99 + +procps-3.0.2 --> procps-3.0.3 + +more "make install" fixes +lib CFLAGS working again +top.1 codes fixed +bad (int*) cast in top removed +top runs faster +libproc memory corruption fixed +rant moved out of top.1 man page +ability to SKIP installing things +fixed ps --sort crash + +procps-3.0.1 --> procps-3.0.2 + +top defaults to the old layout +top defaults to sorting by %CPU +fix top for non-SMP 2.2.xx and 2.0.xx +new "make install" fixed +vmstat -a fixed +vmstat compiles with latest gcc-3.x +vmstat does 64-bit time + +procps-3.0.0 --> procps-3.0.1 + +sysctl handles net/ipv4/conf/eth1.0123/tag (VLAN interface) +sysctl handles net.ipv4.conf.eth1/0123.tag (VLAN interface) +"ps" is now about 2x faster than in procps-2.x.x +"ps -F" now documented +w works in KOI8-R locale +vmstat documentation update +"skill -n blah blah blah" lets you test options +simple "make && make install" now + +procps-2.x.x --> procps-3.0.0 + +designed to support Linux 2.0 through 2.5.41 and beyond +new top, with optional: color, windowing, SMP stats +runs faster +more "it crashes" bugs fixed +top shows IO-wait time +vmstat can show active/inactive memory stats +real-time info supported in ps +correct "ps -o size" and "ps --sort size" +new maintainers +reduced memory usage for ps +allow large PIDs to be specified +SELINUX support is just a recompile away +the "F" column shrank, so "ps -l" has more command name room +64-bit time reduces the overflow problem +support S/390, IA-64 emulator, and user-mode Linux +oldps is gone +configure script -- use "make -f Makefile.noam" as a backup +"w" program better at determining what a user is doing +more stable +code at http://procps.sf.net/ now (SourceForge) + +Earlier changes, for those not using Debian already: + +more stable +runs faster +-F format option +better error reporting in ps for unknown format specifiers +BSD's sysctl options -b and -X +top displays well on large-memory systems +old BSD-style select-by-PID ("ps l$$") +15-character user names +ps 'f' ASCII art forest fixed +add SIGSYS on i386 +top reports real RSS value +large-memory systems work +minimal ps program for embedded systems (minimal.c) +BSD personality process selection fixed +support locale (French) with ',' and '.' mixed up +pgrep program +includes the "kill" and "nice" programs +don't chop non-tty ps output at 80 columns diff --git a/README b/README new file mode 100644 index 0000000..fd76134 --- /dev/null +++ b/README @@ -0,0 +1,72 @@ +COMPATIBILITY + + This code is intended for use with Linux 2.2.xx, 2.4.xx, + 2.6.xx, and hopefully all future kernels. You should be + running a system with libc 6, but libc 5 might work too. + +INSTALLATION + + make + make install + + Only the second ("make install") is needed if you just + want to build and install procps in the normal way. + + If you wish to test before installing, use the scripts + named t, v, and p to ensure that the correct libproc + (the new one) is used during your testing. + + You may set SKIP to avoid building or installing things. + For example: + + make SKIP='/bin/kill /usr/share/man/man1/kill.1' install + + Use SHARED=0 to build procps without shared libraries. + This may be useful for installing in your home directory. + + make SHARED=0 DESTDIR=$HOME install + + Suppose you wanted to install stuff in strange places. + You might do something like this: + + make usr/bin=/tmp/Q/i/ DESTDIR=/tmp/Q install="install -D" ldconfig=echo install + + If cross-compiling, you might need to set lib64 to + either "lib" or "lib64". You might need to set m64 to + -m64, -m32, or nothing at all. Some examples: + + make lib64=lib m64=-m32 # for a bi-arch gcc + make lib64=lib64 CC=x86_64-gcc + make lib64=lib CC=alpha-gcc + +PACKAGING + + If you are a downstream maintainer (packager) for a Linux distribution, + please avoid causing troubles. This section applies to you. + + Send patches in regularly. Many patches made by vendors have been buggy, + some quite severely so. Sending in a patch will at least get it reviewed, + if not included. There is a procps test suite that must be passed. + Forward all bug reports. If your bug database is public and busy enough + to bother with, please make this known. Follow Debian's lead in making + the bug database easy to comment on via email w/o need for an account. + + Do not change the user interface. Many of the programs are intended to be + compatible with Solaris, FreeBSD, AIX, IRIX, Tru64, and the UNIX standard. + Your nice new command options WILL BE BROKEN as needed to ensure that + procps remains compatible with the rest of the world. Sysadmins hate to + deal with incompatible behavior. If you need a new option, ask for it. + + For normal packages, ensure that you do not add debugging flags + to the CFLAGS variable. If debugging flags are present, the Makefile + will avoid adding several optimizations that would interfere with gdb. + + There should be no need to modify the Makefile. You can set variables + on the "make" command line or use "make -e" to pass variables from + the environment. + +BUG REPORTS + + Debian users should use the Debian bug tracking system. + Email to albert@users.sf.net or csmall@debian.org or + procps-feedback@lists.sf.net will also work. diff --git a/README.top b/README.top new file mode 100644 index 0000000..3f225d5 --- /dev/null +++ b/README.top @@ -0,0 +1,545 @@ +Credit for this belongs to: +Jim / James C. Warner, + +---------------------------------- + +Ok, ok, I yield -- most of what follows has been removed from the manual page +and packaged separately as this README (hey, it was only TEMPORARY insanity). + +Of course, that means that now absolutely nobody will ever read it. + +This is probably a good thing... + + +## Table of Contents ---------------------------------------------------## + # the only darn thing that wasn't in the man page + CUSTOMIZING the Sources + # the following carry their original topic numbers + DIFFERENCES / New Features + Interface Etiquette + Expanded Configurable Display Support + Enhanced Field/Column Management + Customization Flexibility + NOTES and Rantings + The top Binary + Comparing Performance + Cost of Stuff + The top Sources + EXAMPLES of Windows + The 'A' Mode Command Toggle + STACKIN' & WHACKIN' Windows + ALL TOGETHER Now, Window(s) + + +## CUSTOMIZING the Sources ---------------------------------------------## + +Listed below are the conditionals available should you wish to recompile +this top. The author's favorite is: PRETEND4CPUS. + +That's the #define allowing you to simulate an SMP environment, and +(perhaps) impress your friends. It's currently set to display four +separate CPUs, but could easily be changed. + + Caution: do NOT use this provision in an effort to impress someone + who truly possesses such a machine! The fact that all 4 + CPUs show the same dynamic results will likely have the + opposite effect. + + +//#define ATEOJ_REPORT /* report a bunch of stuff, at end-of-job */ +//#define CASEUP_HEXES /* show any hex values in upper case */ +//#define CASEUP_SCALE /* show scaled time/num suffix upper case */ +//#define CASEUP_SUMMK /* show memory summary kilobytes with 'K' */ +//#define POSIX_CMDLIN /* use '[ ]' for kernel threads, not '( )' */ +//#define PRETEND2_5_X /* pretend we're linux 2.5.x (for IO-wait) */ +//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */ +//#define PRETENDNOCAP /* use a terminal without essential caps */ +//#define SORT_SUPRESS /* *attempt* to reduce qsort overhead */ +//#define STDOUT_IOLBF /* disable our own stdout _IOFBF override */ +//#define USE_LIB_STA3 /* use lib status (3 ch) vs. proc_t (1 ch) */ +//#define WARN_NOT_SMP /* restrict '1' & 'I' commands to true smp */ + + +## 6. DIFFERENCES / New Features ---------------------------------------## + The following summarizes differences between this top and your + former top. It was originally based on procps-2.0.7. However, + except for the separate/summary CPU toggle, all of these differ- + ences also apply through procps-2.0.10. + + 6a. Interface Etiquette + -*- Input and output are far more carefully implemented in + this top. You won't be subjected to 4 - 5 'Unknown command' + messages should you press the wrong key. + + -*- You need suffer a confirmation message only when the results + of a command are not obvious by their effects on the display. + + -*- The Help screen will no longer overflow, even when running + with a 24 row xterm (vt100). + + -*- The fields selection/ordering screens do not carelessly + destroy important information through unintended line wraps. + + -*- Should you narrow a xterm window to less than 80 columns + while this top is running, you will not be left with an + utterly worthless, embarrassing display. + + 6b. Expanded Configurable Display Support + -*- In an SMP environment, you can choose between a summary dis- + play or you may show each cpu separately. No longer must + this choice be irrevocably made at startup. + + -*- There are new fields and with this top, any field is + selectable for sorting. Plus, your sorted column can be + instantly reversed with just a single keystroke. + + -*- You may optionally apply 2 distinct types of highlighting to + running tasks and/or sorted columns. With this top, you'll + be able to instantly spot running tasks and always know the + current sort field. + + -*- While you could continue to use the more familiar (and + boring) monochrome display, you might want to try this top's + new color display. You can even create your own unique col- + ors used in summaries, messages, headings and tasks, each of + which can be made persistent until you choose to change them. + + -*- Up to four separate windows can be displayed simultaneously, + giving you four separate ways to sort and view the tasks cur- + rently cluttering up your system. You could have one view by + pids, another by cpu usage, yet another showing memory con- + sumption. You get the idea... + + -*- Each window comes with pre-configured (but user configurable) + fields and you can size each window individually. + + -*- Virtually every one of this top's options (summaries, fields, + colors, sorted column, etc.) is separately configurable for + each of those four windows. + + Heck, you can even change a window's name, if you don't care + for top's choices. Your changes will be reflected not only + when you're in what top calls alternate-display mode but also + on his special new 'Windows' help screen. + + -*- And, [ ** Drum-Roll + Ta-Da ** ] with just one keystroke you + can quickly switch between full-screen and multiple window + modes! Or, with a different keystroke, toggle a single win- + dow Off for now, then On again later!! + + 6c. Enhanced Field/Column Management + -*- Many Field/Column names have been changed to make them more + intuitive, more self-descriptive. And with this top you + won't be fooled with field choices that are "not yet imple- + mented". + + -*- Task memory statistics are more meaningful and more accurate. + + -*- You'll finally have complete display integrity regardless of + field selections, their order or screen width. And that + means the command column no longer need be kept as the right- + most field, lest your screen turn to when all the + following columns get misaligned. + + 6d. Customization Flexibility + -*- You have complete program naming freedom with no internal + ties to a specific personal configuration file. Symbolic + links could be used to establish different configuration + files reflecting the different personalities of your cus- + tomized "tops", under whatever aliases you've used. + + Thus, you could have an alias for running top in 'Batch + mode', another for when you work from the Linux console and + maybe a third used with X-Windows. All of that, yet still + just a single binary image! + + -*- All of your configuration choices can be preserved in a per- + sonal configuration file, including any changes made on a + per-window basis. Thus, once you personalize things they + remain personalized until you decide to change them again. + This top has been completely cured of: + i-cant-remember-so-please-do-that-all-over-again + ( and again, and again ... ) + + The bottom line is this: if you save your configuration + before quitting top, upon restart the display will appear + exactly as you left it. And that means you no longer have to + keep top running until-the-end-of-time (ok, a long time + anyway), lest your customizations go bye-bye. + + +## 7. NOTES and Rantings -----------------------------------------------## + 7a. The top Binary + To whom it may (should) concern: this top, even with its vastly + expanded capabilities, is only slightly larger than the old top. + Were it not for extensive help text and additional sort callbacks, + it would be smaller. + Throw source carelessly at objectives, it will + produce equally careless machine instructions. + example: (num_pages - an_address)/1024 == duh? + kicker: document result as broken, due to elf! + ---------------------------------------------- + I know you're out there, are you getting this? + + Now, as for all those new capabilities like colors and windows and + highlighting, you'd expect this top to be the "mother of all pigs" + compared to old top -- right? + + Yea, with this top expect following piglets: + . A smaller virtual image and resident footprint + . Slightly fewer major page faults + . A large reduction in minor page faults for SMP + . The same or better response time + . The same or even less CPU costs + + Ideally any comparison of the old and new top should be against + the same libproc format (32-bit or 64-bit tics) and run in a true + or simulated SMP environment (producing separate CPU stats). This + latter requirement will coax old top into handling his own + '/proc/stat' access -- something this top always does, but with + less cost. + + 7b. Comparing Performance + Even with equivalent libraries and '/proc/stat' access, it's dif- + ficult to accurately compare tops using their own displays. + Results for these cpu-intensive programs (who frequently exceed + their time-slice) generally show a wide disparity in %CPU. This + is due to differing call patterns, kernel preemptions and the tim- + ing of process snapshots. For slightly better results, start each + program with the following commands: + ./old-top -d 0.5 + nice -n-10 ./new-top -d 0.4 + + While actually putting this top at a performance disadvantage, the + higher scheduling priority and staggered timing will periodically + yield a somewhat truer picture. You could even reverse those + roles and get similar results. + + The most consistent performance results will be obtained 'off- + line', using your shell's time pipe or the time program itself. + And even in a single processor environment or without equivalent + libraries, total cpu costs (user time + system time) are similar. + + However, this top's cpu costs ARE influenced by the capabilities + you choose to exploit, even if they don't SEEM to be reflected in + such timings. So let's examine some... + + 7c. Cost of Stuff + Colors Cost -- Nada (almost). + Once the terminfo strings are built (at and during a user's + behest) they are SAVED with each window's stuff. And while + there will be extra tty escape sequences transmitted because of + colors, it makes no difference which 'char *' is actually used. + + Highlighting Cost -- Nada (maybe), or blame it on Rio. + On second thought, let's blame it on the user. + + For row highlighting, there is only the cost of those extra tty + escape sequences (same as for colors). For column highlight- + ing, there is a fairly significant cost associated with column + transition management combined with even more tty output. + These increased costs are incurred on every task display row. + + Sooo... hey USER -- do NOT highlight COLUMNS. You shouldn't + need a constant visual reminder of your chosen sort field. + However, if you forget which field top is sorting it can serve + as a quick visual reminder. + + Windows Cost -- Nada (if just 1 window). + If more than 1 window, almost certainly NOT Nada so blame it on + reality. Colors are not an issue, but those sort fields are. + + If we could trust the user to always select the same 'c' state, + 'S' state and sort field (hey, why ya got multiple windows then + user, huh?) AND if we can trust someone to recompile top with a + #define enabled, then we could achieve 'Nada'. + + Ok, not likely, so we're gonna' be doing multiple sorts. BUT, + it may not be as bad as it sounds. Those sorts involve point- + ers only. And, that's as good as it gets ! (right Mr. N?) + + 7d. The top Sources + top.h + Unlike his predecessor, this top has a proper header file. It + contains ONLY declarations, NOT definitions. And there are + several conditionals present to help with further customiza- + tions and experimentation. All are Off by default. + + top.c + Hopefully proves that source code needn't be a disorganized, + misaligned MESS. And, WHO says a source listing shouldn't + occasionally make you SMILE? Why, top.c even does a darn good + job of following the suggestions in a document hardly anybody + seems to observe. + + the Linus Torvalds CodingStyle guidelines ... + -*- -*- -*- on indentation + etc. -*- -*- -*- + well almost all, except for those stinkin'... + + I suppose even Linus Torvalds is entitled to err now and again. + How so you say? Tabs, me' bucko, stinkin' tabs! That, plus the + simplistic position regarding indentation espoused in that other- + wise excellent document. + + -*- Rant On, and on -*- + Let's compare two approaches to the tab/indentation issue with a + small code sample using tabs then spaces. This snippet happens to + be the key to top's use of dynamic colors on many static screens, + while also ensuring screen width isn't exceeded so as to avoid + line wraps. We'll view just the first 40 columns, assuming one + wishes to occasionally provide comments to the right of actual + code (you do, don't you?). + + Then YOU decide which approach makes the most SENSE! + + Stinkin' Tabs versus Spaces: the Linus way + Hey, where'd my +----+----1----+----2----+----3----+----4+ + many code lines | while (*sub_beg) { : + up-and-gone-to? | switch (*sub_end: + | case 0: : + Gosh, wonder if | \ Tabs Induced / : + Linus expects a | case 1: : + fellow to stick | + WASTE-Lands! + case 5: : + his comments on | : + the left side?! | + Not a Living + : + | : + Ever see source | + line-of-code + : + with not enough | : + whitespace; and | / To Be Found! \ : + this is better? | default:: + | : + Oh lookie here, \ } : + there's just a hint of REAL code! ----> if (0 >= room) b: + / } /* end: while 'subtrin: + +----------------------------------------+ + + Spaces versus Stinkin' Tabs: the other way + +----+----1----+----2----+----3----+----4+ + Wow, now this is | while (*sub_beg) { : + Visible hackin'! | switch (*sub_end) { : + | case 0: : + Hmmm, wonder how | *(sub_end + 1) = '\0'; : + many programmers | case 1: case 2: case 3: case: + read those lines | case 5: case 6: case 7: case: + from the LEFT to | cap = Curwin->captab[(int: + the RIGHT? This | *sub_end = '\0'; : + "innovation" may | PUTP("%s%.*s%s", cap, roo: + possibly benefit | room -= (sub_end - sub_be: + those particular | sub_beg = ++sub_end; : + kinds of people, | break; : + you agree? Duh! | default: : + | ++sub_end; : + AND, there might | } : + even be room for | if (0 >= room) break; : + unseen comments! | } /* end: while 'subtrings' */ : + +----------------------------------------+ + + Gosh, I just don't KNOW -- it's such a TOUGH choice... + + Oh you Stinkin' Tabs: correspondence, Who-Cares; documentation, + Oh-Alright; even scripts, Well-If-You-Must. But you have NO place + within the code-space of MY C-source listing! So be gone + already!! + + In Summation... + - If you want to use tabs to the right of the code, go-for-it. + But PLEASE, not ever in the C-source code-space, thank-you- + kindly. Just use three little ol' spaces (exactly 3, no-more, + no-less) where you WOULD have stuck a stinkin' tab. + + We'll get far more READABLE files, much less WAISTED precious + horizontal space, more consistent CURSORS and on, and ON, AND + ON! Plus, without those awful *the-devil's-own-handiwork*, the + aforementioned document need NEVER speak of their EVILS again. + + - Lastly, since SPACES (not stinkin' tabs) are SO beneficial, + maybe we should use just a few more of 'em. Some of those C- + thingies are VERY sensitive -- they don't like being TOUCHED + by any other syntax element! Which ones? Why these guys: + + braces, reserved words and binary operators + ( it's the TRUTH, they told me themselves ) + + It's so EASY to keep 'em HAPPY! And lo-and-behold, the combi- + nation of thingy turns out to be a darn effective bug + repellent, too. So much so, one can actually code while + TOTALLY NUDE yet still avoid them ol' bug-bytes (sic-sic)! + step + down_from + me_punctilious + soap-box_once_again + [1 +5 +5 +5 = huh?] + + +## 4c. EXAMPLES of Windows ---------------------------------------------## + + -*- The 'A' Mode Command Toggle -*- + Here's what you'll see when you first invoke the alternate-display + mode interactive command. + + This particular display was produce on a VT100 xterm, with only 24 + rows. All four task displays are visible, but they could not be sized + the same. Available lines are parceled out in the fairest way possi- + ble so the last two task displays have an extra line each. + + Notice the 'current' window name in the summary area -- it's been + emphasized because the associated task display is visible. Since + 1:Def has a task area, the full range of interactive commands would be + at your disposal. But remember, many of those commands will apply + only to window 1:Def. + + +--------------------------------------+ + 1:Def name is bold, |1:Def - 15:46:37 up 16:25, 9 users, : + thus all commands |Tasks: 76 total, 1 running, 75 sle: + will be available. |Cpu(s): 0.7% user, 1.3% system, : + |Mem: 126588k total, 123688k used,: + |Swap: 265032k total, 8232k used,: + |______________________________________: + Tough luck windows |1__PID_USER______PR__NI_%CPU____TIME+_: + #1 & 2 - you lost | 7343 jtwm 16 0 0.9 0:00.59: + one line each -- | 7339 jtwm 9 0 0.0 0:00.02: + guess you'll just |__7337_root_______9___0__0.0___0:01.30: + have to learn how |2__PID__PPID_Command____________TIME+_: + to live with it. | 997 952 kdeinit 17:59.59: + | 1115 952 kdeinit 2:16.47: + |__1803__1116_led_______________1:55.30: + |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: + The #3 & #4 windows | 4634 12.3 15620 0 15m 860 14m : + better not gloat | 7337 11.3 14396 92 13m 36 13m : + over 1 extra line. | 923 10.6 30524 16m 13m 1120 12m : + That user could yet |___991__7.2__9492__316_9176___12_9164_: + sock 'em with the |4_UID_USER_____GROUP____TTY________PID: + 'n' command and | 43 xfs xfs ? 806: + take those lines, | 0 ykde users pts/7 5561: + plus others, away! | 0 wgnome users pts/7 5560: + | 0 root root pts/7 5325: + +--------------------------------------+ + + So, what say we start applying some of those "full range of interac- + tive commands"? + + Onward + Downward... + + -*- STACKIN' & WHACKIN' Windows -*- + Whoa, hold on mate. Someone has already whacked these windows. See, + there are no task areas for windows 1:Def and 4:Usr. Well, we can at + least retrace their steps... + + Here's what was done, after issuing the 'A' command and entering + alternate-display mode. + 1) When #1 was the 'current' window, '-' was pressed, + toggling Off the associated task display + ( if 'l t m' had been applied to its summary, too ) + ( then there'll be only a msg line when 'current' ) + 2) Then the 'w' key was struck to cycle backward, + making 4:Usr the 'current' window + (could have used 'a a a', if one likes to type) + 3) Then step #1 was repeated, and bye-bye window #4 + 4) Finally, window #2 was made the 'current' window + ( Q. how many keystrokes were used? ) + ( A. minimum of 2: 'a a' or 'w w'. ) + + +--------------------------------------+ + No 'l','t','m','1' |2:Top - 15:48:35 up 16:27, 9 users, : + commands have been |Tasks: 75 total, 1 running, 74 sle: + issued here, |Cpu(s): 2.0% user, 0.7% system, : + but... |Mem: 126588k total, 123712k used,: + |Swap: 265032k total, 8232k used,: + |______________________________________: + #2's been changed; |2__PID__PPID_Command____________TIME+_: + user applied a 'c' | 997 952 kdeinit: konsol 18:00.70: + command (when it | 1115 952 kdeinit: konsol 2:16.47: + was current) - now | 1803 1116 led tiptop.HELP 1:55.30: + shows cmd lines vs. | 923 922 X :0 1:09.60: + program names; | 973 1 klaptopdaemon 0:59.63: + still seems to be | 981 952 /usr/bin/artsd 0:48.63: + sorted on TIME+ | 987 1 kdeinit: kdeskt 0:24.34: + though |___991_____1_kdeinit:_kicker___0:04.59: + |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: + This #3 guy appears | 4634 12.3 15620 0 15m 860 14m : + to still be running | 7337 11.3 14396 92 13m 36 13m : + with the supplied | 923 10.6 30544 16m 13m 1120 12m : + defaults, but no | 991 7.2 9492 316 9176 12 9164 : + telling what damage | 7329 7.0 9036 140 8896 36 8860 : + might have been | 1115 6.9 8956 160 8796 36 8760 : + done to it's | 987 6.4 8668 524 8144 20 8124 : + summary info stuff | 1131 6.4 8268 144 8124 36 8088 : + +--------------------------------------+ + + And that's what brought us to this current state. No, wait. Oh + lordy, will you look at that -- someone has changed the name of win- + dow #2 from 'Job' to 'Top'! + + How'd they do that? Well, they just issued the 'g' interactive com- + mand, of course. That command is available whenever alternate-display + mode is active and always impacts just the 'current' window. Gosh, + you can even issue the 'g' command when 'l' has toggled Off the very + summary area line containing the window name! + + Almost Done... + + -*- ALL TOGETHER Now, Window(s) -*- + Here, the window 1:Def task display has been toggled Off but it + remains the 'current' window. Since there is no task area, many com- + mands will be restricted. However, the commands ('l', 't', 'm', '1') + affecting the summary area, as well as some other global commands + ('k', 'Z', etc.), would still be active. + + Notice that the Mem and Swap lines are not shown. This means that the + loser (oops, user) has, in fact, issued the 'm' command! Now, if you + were to cycle the 'current' window with the 'a' or 'w' commands, the + task display would remain the same (except possibly growing/shrinking + slightly) but the summary area would change periodically. + + The comments to the left of the image provide additional insights into + how things came to be. Note especially the comments for window 4:Usr + -- the one with some empty rows... + + 1:Def no highlight, +--------------------------------------+ + thus disabled cmds: |1:Def - 15:50:32 up 16:29, 9 users, : + b,i,n,u,x,y, etc. |Tasks: 75 total, 2 running, 73 sle: + & m = lost Mem/Swap |Cpu(s): 10.6% user, 0.0% system, : + |______________________________________: + 2:Job was very busy: |2__PID__PPID_Command____________TIME+_: + 'n' cmd, w/ 7 tasks | 80 1 ( khubd ) 0:00.00: + 'c' cmd, cmd line | 6 0 ( kreclaimd ) 0:00.00: + 'O' cmd, sort cmd | 9 1 ( mdrecoveryd ) 0:00.00: + 'R' cmd, sort bkwd | 11358 1 /bin/bash/ /usr 0:00.00: + 'x' cmd, hi column | 1297 1 /sbin/mingetty 0:00.00: + (when 2 WAS current) | 683 1 xinetd -stayali 0:00.00: + |___836_____1_login_--_root_____0:00.00: + 3:Mem has altered |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: + some std defaults: | 4634 12.3 15620 0 15m 860 14m : + 'y' turned Off | 7337 11.3 14396 92 13m 36 13m : + 'x' turned On | 923 10.6 30544 16m 13m 1120 12m : + (when 3 WAS current) | 991 7.2 9492 316 9176 12 9164 : + |__7329__7.0__9036__140_8896___36_8860_: + Huh? 4:Usr has some |4_UID_USER_____GROUP____TTY________PID: + blank rows! ? ? ? ? | 0 jtwm root pts/2 5561: + Aha, the 'i' command | 0 root root ? 5560: + applied (when 4 WAS | : + current); could be | : + reversed with '=', | : + when 4 IS current! +--------------------------------------+ + + Ok now, how about that 'current' window 1:Def and its unseen tasks? + At any time, you can quickly retrieve lost tasks in a number of ways: + 1) Press '-', toggling just the 'current' window + 2) Press '_', toggling all visible/invisible windows + ( 1:Def is the only window currently not shown ) + ( afterward, it'll be the only window showing! ) + * 3) Press '+', forcing all task displays to become visible + 4) Press 'A' to return to full-screen mode, + with only 1:Def tasks shown and without a window name + + Now that should be enough ways of getting a task area visible again to + satisfy almost any user, don't ya think? + + Note: Use #3 above when you've messed up your screen beyond + redemption. The four task displays will reappear, nice and even. + They will also have retained any customizations you had previously + applied, except for the 'i' (idle tasks) and 'n' (max tasks) com- + mands. + + That's It ! Piece of Cake !! Enjoy them there windows !!! + diff --git a/TODO b/TODO new file mode 100644 index 0000000..7831ea0 --- /dev/null +++ b/TODO @@ -0,0 +1,151 @@ +-------------------------- general ------------------------ + +Consider using glibc obstacks for memory allocation. + +Implement /usr/proc/bin tools like Solaris has. +The prstat command is interesting, like top in batch mode. +SCO has a pstat command. + +Don't these really belong in the procps package? + killall pstree fuser lsof who +(they are maintained elsewhere, which causes version problems) + +OpenBSD has a pfind command. + +Cache results of dev_to_tty. + +---------------------- kernel ------------------------- + +Add an "adopted child" flag to mark processes that are not +natural children of init. This can make --forest work better. + +Supply better data for top's CPU state display. Currently top has +to subtract old numbers from new numbers and divide that result by +the number of processors. The kernel won't even supply the number +of processors in a portable way. + +Supply data for the ADDR and JOBC fields. + +Support & supply data for SL and RE. + +Add a /proc/*/tty symlink to eliminate guessing when /proc/*/fd is +not accessable. + +Add /proc/*/.bindata files to avoid string parsing. It should be an array +of 64-bit values on all machines. New entries go on the end and obsolete +ones get filled in with something logical -- entries must never be deleted! + +Add all the stuff Solaris has. This would also replace ptrace. + +---------------------- watch -------------------------- + +Tolerate UTF-8. + +Tolerate color, bold, underline, etc. #129334 + +Tolerate stderr. #420377 #155227 #225549 + +Tolerate VT100 line-drawing characters. Maybe translate them. + +---------------------- w -------------------------- + +The LOGIN@ column sometimes has a space in it. This makes correct +scripting difficult. + +Time formats are demented. + +---------------------- vmstat -------------------------- + +Extract /proc/diskstats parsing from vmstat into libproc somewhere. + +--------------------- libproc ---------------------- + +Stop storing fields with duplicate info (often different +units: kB and pages, seconds and jiffies) in the proc_t struct. + +Use own readdir code (assembly language) because glibc sucks ass. + +---------------------- top ------------------------- + +Share more stuff with ps. + +don't truncate long usernames + +have a --config option + +---------------- ps for now, maybe move to libproc ------------------ + +With forest output and a tty named /dev/this_is_my_tty, the position +of the command name gets messed up. (we print too many spaces) (fixed?) + +Fix missing stuff for these formats: FB_j FB_l FB_v HP_f HP_l HP_fl JFMT OL_m +(jobc,cpu,sl,re,cpu,prmgrp,m_swap,m_share,vm_lib,m_dt) +Note that "cpu" has two meanings. + +Add Beowulf support. This is ugly, since the current patches use a +daemon to collect info and add a HOST field after the PID field. + +Query optimizer, put cheap/required process selection first. + +Avoid reading both /proc/*/status and /proc/*/stat. + +Support printing the client hostname (the FROM that w(1) uses) in place +of a pty. + +Disambiguate narrow tty info. (/dev/tty7 == /dev/pts/7 now) +1------8 1--4 +ttyS2 S2 +ttyI31 I31 +pts/7 7 Short form could be /999. +pts/9999 9999 Short form could just be trunctuated to /999. +tty7 7 Short form could be vc-7. +tty63 63 Short form could be vc63. + +Internationalization, as specified by XPG3, Volume 1, Commands and Utilities. +(and suggested by Unix98) LC_TIME affects date format. + +----------------------- ps ----------------------- + +Add an option to select all processes that a user can kill. +(related to RUID, EUID, tty, etc. -- but maybe ignore root power) + +Add a nice display option for killing things. +ruser,euser,ppid,pid,pmem,stime,args + +For RT stuff: +pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm + +For job control: +stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm + +Make the column alignment algorithm support this: + FOO BAR + 8 44444 + 453 45 + 45 2989 + 63666 0 + 34 333 +(useful for the UNIX tty and time values, since the time might look +like 100-10:40:32 for old processes and the tty might have extra room) + +Improve long sort/format specifiers documentation and fill in the missing +code as much as the kernel can support. Make sure that memory amounts are in +pages when they should be and in kB when they should be, not backwards. + +output encoding: UTF8 --nul --null + +Make BSD formats use non-standard BSD time format, at least when it +doesn't violate the "no whitespace" rule. + +Better unmangling of '?' as a tty. The shell destroys '?' when there +is a filename that matches. If the argument seems like garbage, +check for a file that might have screwed up the '?'. + +If the 'O' option is given something already implied by 'O', +assume the user wanted a sorting option. + +Conflict: +Digital THREAD is user,pcpu,pri,scnt,wchan,usertime,systime +AIX THREAD is uname,pid,ppid,tid,S,C,PRI,scount,WCHAN,F,tty,bnd,comm +AIX looks like this: + USER PID PPID TID S C PRI SC WCHAN FLAG TTY BND CMD diff --git a/dummy.c b/dummy.c new file mode 100644 index 0000000..95e7824 --- /dev/null +++ b/dummy.c @@ -0,0 +1,31 @@ +// This is to test the compiler. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Foul POS defines all sorts of stuff... +#include +#undef tab + +#include +#include +#include +#include + +int main(int argc, char *argv[]){ + (void)argc; + (void)argv; + return 0; +} diff --git a/free.1 b/free.1 new file mode 100644 index 0000000..954c8d9 --- /dev/null +++ b/free.1 @@ -0,0 +1,47 @@ +.\" -*-Nroff-*- +.\" This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu. +.\" Freely distributable under the terms of the GPL +.TH FREE 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual" +.SH NAME +free \- Display amount of free and used memory in the system +.SH SYNOPSIS +.BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s" +.I delay +.RB "] [" "\-t" "] [" "\-V" ] +.SH DESCRIPTION +\fBfree\fP displays the total amount of free and used physical and swap +memory in the system, as well as the buffers used by the kernel. +The shared memory column should be ignored; it is obsolete. +.SS Options +The \fB-b\fP switch displays the amount of memory in bytes; the +\fB-k\fP switch (set by default) displays it in kilobytes; the \fB-m\fP +switch displays it in megabytes. +.PP +The \fB-t\fP switch displays a line containing the totals. +.PP +The \fB-o\fP switch disables the display of a "buffer adjusted" line. +If the -o option is not specified, \fBfree\fP subtracts buffer memory +from the used memory and adds it to the free memory reported. +.PP +The \fB-s\fP switch activates continuous polling \fIdelay\fP seconds apart. You +may actually specify any floating point number for \fIdelay\fP, +.BR usleep (3) +is used for microsecond resolution delay times. +.PP +The \fB\-V\fP displays version information. +.SH FILES +.ta +.IR /proc/meminfo "\-\- memory information" +.fi + +.SH "SEE ALSO" +.BR ps (1), +.BR slabtop (1), +.BR vmstat (8), +.BR top(1) + +.SH AUTHORS +Written by Brian Edmonds. + +Send bug reports to + diff --git a/free.c b/free.c new file mode 100644 index 0000000..bd78f02 --- /dev/null +++ b/free.c @@ -0,0 +1,122 @@ +// free.c - free(1) +// procps utility to display free memory information +// +// All new, Robert Love 18 Nov 2002 +// Original by Brian Edmonds and Rafal Maszkowski 14 Dec 1992 +// +// This program is licensed under the GNU Library General Public License, v2 +// +// Copyright 2003 Robert Love +// Copyright 2004 Albert Cahalan + +#include "proc/sysinfo.h" +#include "proc/version.h" +//#include +#include +#include +#include +#include +#include +#include + +#define S(X) ( ((unsigned long long)(X) << 10) >> shift) + +const char help_message[] = +"usage: free [-b|-k|-m|-g] [-l] [-o] [-t] [-s delay] [-c count] [-V]\n" +" -b,-k,-m,-g show output in bytes, KB, MB, or GB\n" +" -l show detailed low and high memory statistics\n" +" -o use old format (no -/+buffers/cache line)\n" +" -t display total for RAM + swap\n" +" -s update every [delay] seconds\n" +" -c update [count] times\n" +" -V display version information and exit\n" +; + +int main(int argc, char *argv[]){ + int i; + int count = 0; + int shift = 10; + int pause_length = 0; + int show_high = 0; + int show_total = 0; + int old_fmt = 0; + + /* check startup flags */ + while( (i = getopt(argc, argv, "bkmglotc:s:V") ) != -1 ) + switch (i) { + case 'b': shift = 0; break; + case 'k': shift = 10; break; + case 'm': shift = 20; break; + case 'g': shift = 30; break; + case 'l': show_high = 1; break; + case 'o': old_fmt = 1; break; + case 't': show_total = 1; break; + case 's': pause_length = 1000000 * atof(optarg); break; + case 'c': count = strtoul(optarg, NULL, 10); break; + case 'V': display_version(); exit(0); + default: + fwrite(help_message,1,strlen(help_message),stderr); + return 1; + } + + do { + meminfo(); + printf(" total used free shared buffers cached\n"); + printf( + "%-7s %10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n", "Mem:", + S(kb_main_total), + S(kb_main_used), + S(kb_main_free), + S(kb_main_shared), + S(kb_main_buffers), + S(kb_main_cached) + ); + // Print low vs. high information, if the user requested it. + // Note we check if low_total==0: if so, then this kernel does + // not export the low and high stats. Note we still want to + // print the high info, even if it is zero. + if (show_high) { + printf( + "%-7s %10Lu %10Lu %10Lu\n", "Low:", + S(kb_low_total), + S(kb_low_total - kb_low_free), + S(kb_low_free) + ); + printf( + "%-7s %10Lu %10Lu %10Lu\n", "High:", + S(kb_high_total), + S(kb_high_total - kb_high_free), + S(kb_high_free) + ); + } + if(!old_fmt){ + unsigned KLONG buffers_plus_cached = kb_main_buffers + kb_main_cached; + printf( + "-/+ buffers/cache: %10Lu %10Lu\n", + S(kb_main_used - buffers_plus_cached), + S(kb_main_free + buffers_plus_cached) + ); + } + printf( + "%-7s %10Lu %10Lu %10Lu\n", "Swap:", + S(kb_swap_total), + S(kb_swap_used), + S(kb_swap_free) + ); + if(show_total){ + printf( + "%-7s %10Lu %10Lu %10Lu\n", "Total:", + S(kb_main_total + kb_swap_total), + S(kb_main_used + kb_swap_used), + S(kb_main_free + kb_swap_free) + ); + } + if(pause_length){ + fputc('\n', stdout); + fflush(stdout); + if (count != 1) usleep(pause_length); + } + } while(pause_length && --count); + + return 0; +} diff --git a/kill.1 b/kill.1 new file mode 100644 index 0000000..aef9dbf --- /dev/null +++ b/kill.1 @@ -0,0 +1,118 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for kill. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan; converted to a man page by +.\" Michael K. Johnson +.TH KILL 1 "November 21, 1999" "Linux" "Linux User's Manual" +.SH NAME +kill \- send a signal to a process + +.SH SYNOPSIS +.TS +l l. +kill pid ... Send SIGTERM to every process listed. +kill -signal pid ... Send a signal to every process listed. +kill -s signal pid ... Send a signal to every process listed. +kill -l List all signal names. +kill -L List all signal names in a nice table. +kill -l signal Convert a signal number into a name. +kill -V,--version Show version of program +.TE + +.SH DESCRIPTION +The default signal for kill is TERM. Use -l or -L to list available signals. +Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0. +Alternate signals may be specified in three ways: -9 -SIGKILL -KILL. +Negative PID values may be used to choose whole process groups; see the +PGID column in ps command output. A PID of -1 is special; it indicates +all processes except the kill process itself and init. + +.SH SIGNALS +The signals listed below may be available for use with kill. +When known constant, numbers and default behavior are shown. + +.TS +lB rB lB lB +lfCW r l l. +Name Num Action Description +.TH +0 0 n/a exit code indicates if a signal may be sent +ALRM 14 exit +HUP 1 exit +INT 2 exit +KILL 9 exit this signal may not be blocked +PIPE 13 exit +POLL exit +PROF exit +TERM 15 exit +USR1 exit +USR2 exit +VTALRM exit +STKFLT exit may not be implemented +PWR ignore may exit on some systems +WINCH ignore +CHLD ignore +URG ignore +TSTP stop may interact with the shell +TTIN stop may interact with the shell +TTOU stop may interact with the shell +STOP stop this signal may not be blocked +CONT restart continue if stopped, otherwise ignore +ABRT 6 core +FPE 8 core +ILL 4 core +QUIT 3 core +SEGV 11 core +TRAP 5 core +SYS core may not be implemented +EMT core may not be implemented +BUS core core dump may fail +XCPU core core dump may fail +XFSZ core core dump may fail +.TE + +.SH NOTES +Your shell (command line interpreter) may have a built-in kill command. +You may need to run the command described here as /bin/kill to solve +the conflict. + +.SH EXAMPLES + +.SS +.B "kill -9 -1" +.nf +Kill all processes you can kill. +.fi +.PP +.SS +.B "kill -l 11" +.nf +Translate number 11 into a signal name. +.fi +.PP +.SS +.B "kill -L" +.nf +List the available signal choices in a nice table. +.fi +.PP +.SS +.B "kill 123 543 2341 3453" +.nf +Send the default signal, SIGTERM, to all those processes. +.fi +.PP +.SH "SEE ALSO" +pkill(1) skill(1) kill(2) renice(1) nice(1) signal(7) killall(1) + +.SH STANDARDS +This command meets appropriate standards. The -L flag is Linux-specific. + +.SH AUTHOR +Albert Cahalan wrote kill in 1999 to replace a +bsdutils one that was not standards compliant. The util-linux one might +also work correctly. + +Please send bug reports to diff --git a/minimal.c b/minimal.c new file mode 100644 index 0000000..a38e4c7 --- /dev/null +++ b/minimal.c @@ -0,0 +1,670 @@ +/* + * Copyright 1998,2004 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* This is a minimal /bin/ps, designed to be smaller than the old ps + * while still supporting some of the more important features of the + * new ps. (for total size, note that this ps does not need libproc) + * It is suitable for Linux-on-a-floppy systems only. + * + * Maintainers: do not compile or install for normal systems. + * Anyone needing this will want to tweak their compiler anyway. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEV_ENCODE(M,m) ( \ + ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \ +) + +/////////////////////////////////////////////////////// +#ifdef __sun__ +#include +#define _STRUCTURED_PROC 1 +#include +#define NO_TTY_VALUE DEV_ENCODE(-1,-1) +#define HZ 1 // only bother with seconds +#endif + +/////////////////////////////////////////////////////// +#ifdef __FreeBSD__ +#include +#include +#include +#include +#include +#define NO_TTY_VALUE DEV_ENCODE(-1,-1) +#define HZ 1 // only bother with seconds +#endif + +/////////////////////////////////////////////////////// +#ifdef __linux__ +#include /* HZ */ +#include /* PAGE_SIZE */ +#define NO_TTY_VALUE DEV_ENCODE(0,0) +#ifndef HZ +#warning HZ not defined, assuming it is 100 +#define HZ 100 +#endif +#endif + +/////////////////////////////////////////////////////////// + +#ifndef PAGE_SIZE +#warning PAGE_SIZE not defined, assuming it is 4096 +#define PAGE_SIZE 4096 +#endif + + + +static char P_tty_text[16]; +static char P_cmd[16]; +static char P_state; +static int P_euid; +static int P_pid; +static int P_ppid, P_pgrp, P_session, P_tty_num, P_tpgid; +static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime; +static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_alarm; +static unsigned long P_start_time, P_vsize; +static long P_rss; +static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip; +static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch; +static unsigned long P_wchan, P_nswap, P_cnswap; + + + +#if 0 +static int screen_cols = 80; +static int w_count; +#endif + +static int want_one_pid; +static const char *want_one_command; +static int select_notty; +static int select_all; + +static int ps_format; +static int old_h_option; + +/* we only pretend to support this */ +static int show_args; /* implicit with -f and all BSD options */ +static int bsd_c_option; /* this option overrides the above */ + +static int ps_argc; /* global argc */ +static char **ps_argv; /* global argv */ +static int thisarg; /* index into ps_argv */ +static char *flagptr; /* current location in ps_argv[thisarg] */ + + + + +static void usage(void){ + fprintf(stderr, + "-C select by command name (minimal ps only accepts one)\n" + "-p select by process ID (minimal ps only accepts one)\n" + "-e all processes (same as ax)\n" + "a all processes w/ tty, including other users\n" + "x processes w/o controlling ttys\n" + "-f full format\n" + "-j,j job control format\n" + "v virtual memory format\n" + "-l,l long format\n" + "u user-oriented format\n" + "-o user-defined format (limited support, only \"ps -o pid=\")\n" + "h no header\n" +/* + "-A all processes (same as ax)\n" + "c true command name\n" + "-w,w wide output\n" +*/ + ); + exit(1); +} + +/* + * Return the next argument, or call the usage function. + * This handles both: -oFOO -o FOO + */ +static const char *get_opt_arg(void){ + const char *ret; + ret = flagptr+1; /* assume argument is part of ps_argv[thisarg] */ + if(*ret) return ret; + if(++thisarg >= ps_argc) usage(); /* there is nothing left */ + /* argument is the new ps_argv[thisarg] */ + ret = ps_argv[thisarg]; + if(!ret || !*ret) usage(); + return ret; +} + + +/* return the PID, or 0 if nothing good */ +static void parse_pid(const char *str){ + char *endp; + int num; + if(!str) goto bad; + num = strtol(str, &endp, 0); + if(*endp != '\0') goto bad; + if(num<1) goto bad; + if(want_one_pid) goto bad; + want_one_pid = num; + return; +bad: + usage(); +} + +/***************** parse SysV options, including Unix98 *****************/ +static void parse_sysv_option(void){ + do{ + switch(*flagptr){ + /**** selection ****/ + case 'C': /* end */ + if(want_one_command) usage(); + want_one_command = get_opt_arg(); + return; /* can't have any more options */ + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + case 'A': + case 'e': + select_all++; + select_notty++; +case 'w': /* here for now, since the real one is not used */ + break; + /**** output format ****/ + case 'f': + show_args = 1; + /* FALL THROUGH */ + case 'j': + case 'l': + if(ps_format) usage(); + ps_format = *flagptr; + break; + case 'o': /* end */ + /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */ + if(strcmp(get_opt_arg(),"pid=")) usage(); + if(ps_format) usage(); + ps_format = 'o'; + old_h_option++; + return; /* can't have any more options */ + /**** other stuff ****/ +#if 0 + case 'w': + w_count++; + break; +#endif + default: + usage(); + } /* switch */ + }while(*++flagptr); +} + +/************************* parse BSD options **********************/ +static void parse_bsd_option(void){ + do{ + switch(*flagptr){ + /**** selection ****/ + case 'a': + select_all++; + break; + case 'x': + select_notty++; + break; + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + /**** output format ****/ + case 'j': + case 'l': + case 'u': + case 'v': + if(ps_format) usage(); + ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */ + break; + /**** other stuff ****/ + case 'c': + bsd_c_option++; +#if 0 + break; +#endif + case 'w': +#if 0 + w_count++; +#endif + break; + case 'h': + old_h_option++; + break; + default: + usage(); + } /* switch */ + }while(*++flagptr); +} + +#if 0 +#include +/* not used yet */ +static void choose_dimensions(void){ + struct winsize ws; + char *columns; + /* screen_cols is 80 by default */ + if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col; + columns = getenv("COLUMNS"); + if(columns && *columns){ + long t; + char *endptr; + t = strtol(columns, &endptr, 0); + if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t; + } + if(w_count && (screen_cols<132)) screen_cols=132; + if(w_count>1) screen_cols=999999999; +} +#endif + +static void arg_parse(int argc, char *argv[]){ + int sel = 0; /* to verify option sanity */ + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + /**** iterate over the args ****/ + while(++thisarg < ps_argc){ + flagptr = ps_argv[thisarg]; + switch(*flagptr){ + case '0' ... '9': + show_args = 1; + parse_pid(flagptr); + break; + case '-': + flagptr++; + parse_sysv_option(); + break; + default: + show_args = 1; + parse_bsd_option(); + break; + } + } + /**** sanity check and clean-up ****/ + if(want_one_pid) sel++; + if(want_one_command) sel++; + if(select_notty || select_all) sel++; + if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage(); + if(bsd_c_option) show_args = 0; +} + +#ifdef __sun__ +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) { + struct psinfo p; // /proc/*/psinfo, struct psinfo, psinfo_t + char buf[32]; + int num; + int fd; + int tty_maj, tty_min; + snprintf(buf, sizeof buf, "/proc/%d/psinfo", pid); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0; + num = read(fd, &p, sizeof p); + close(fd); + if(num != sizeof p) return 0; + + num = PRFNSZ; + if (num >= sizeof P_cmd) num = sizeof P_cmd - 1; + memcpy(P_cmd, p.pr_fname, num); // p.pr_fname or p.pr_lwp.pr_name + P_cmd[num] = '\0'; + + P_pid = p.pr_pid; + P_ppid = p.pr_ppid; + P_pgrp = p.pr_pgid; + P_session = p.pr_sid; + P_euid = p.pr_euid; + P_rss = p.pr_rssize; + P_vsize = p.pr_size; + P_start_time = p.pr_start.tv_sec; + P_wchan = p.pr_lwp.pr_wchan; + P_state = p.pr_lwp.pr_sname; + P_nice = p.pr_lwp.pr_nice; + P_priority = p.pr_lwp.pr_pri; // or pr_oldpri +// P_ruid = p.pr_uid; +// P_rgid = p.pr_gid; +// P_egid = p.pr_egid; + +#if 0 + // don't support these + P_tpgid; P_flags, + P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime; + P_cutime, P_cstime, P_timeout, P_alarm; + P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip; + P_signal, P_blocked, P_sigignore, P_sigcatch; + P_nswap, P_cnswap; +#endif + + // we like it Linux-encoded :-) + tty_maj = major(p.pr_ttydev); + tty_min = minor(p.pr_ttydev); + P_tty_num = DEV_ENCODE(tty_maj,tty_min); + + snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min); +#if 1 + if (tty_maj == 24) snprintf(P_tty_text, sizeof P_tty_text, "pts/%-3u", tty_min); + if (P_tty_num == NO_TTY_VALUE) memcpy(P_tty_text, " ? ", 8); + if (P_tty_num == DEV_ENCODE(0,0)) memcpy(P_tty_text, "console", 8); +#endif + + if(P_pid != pid) return 0; + return 1; +} +#endif + +#ifdef __FreeBSD__ +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) { + char buf[400]; + int num; + int fd; + char* tmp; + int tty_maj, tty_min; + snprintf(buf, 32, "/proc/%d/status", pid); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0; + num = read(fd, buf, sizeof buf - 1); + close(fd); + if(num<43) return 0; + buf[num] = '\0'; + + P_state = '-'; + + // FreeBSD /proc/*/status is seriously fucked. Unlike the Linux + // files, we can't use strrchr to find the end of a command name. + // Spaces in command names do not get escaped. To avoid spoofing, + // one may skip 20 characters and then look _forward_ only to + // find a pattern of entries that are {with,with,without} a comma. + // The entry without a comma is wchan. Then count backwards! + // + // Don't bother for now. FreeBSD isn't worth the trouble. + + tmp = strchr(buf,' '); + num = tmp - buf; + if (num >= sizeof P_cmd) num = sizeof P_cmd - 1; + memcpy(P_cmd,buf,num); + P_cmd[num] = '\0'; + + num = sscanf(tmp+1, + "%d %d %d %d " + "%d,%d " + "%*s " + "%ld,%*d " + "%ld,%*d " + "%ld,%*d " + "%*s " + "%d %d ", + &P_pid, &P_ppid, &P_pgrp, &P_session, + &tty_maj, &tty_min, + /* SKIP funny flags thing */ + &P_start_time, /* SKIP microseconds */ + &P_utime, /* SKIP microseconds */ + &P_stime, /* SKIP microseconds */ + /* SKIP &P_wchan, for now -- it is a string */ + &P_euid, &P_euid // don't know which is which + ); +/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + + snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min); + P_tty_num = DEV_ENCODE(tty_maj,tty_min); +// tty decode is 224 to 256 bytes on i386 +#if 1 + tmp = NULL; + if (tty_maj == 5) tmp = " ttyp%c "; + if (tty_maj == 12) tmp = " ttyv%c "; + if (tty_maj == 28) tmp = " ttyd%c "; + if (P_tty_num == NO_TTY_VALUE) tmp = " ? "; + if (P_tty_num == DEV_ENCODE(0,0)) tmp = "console"; + if (P_tty_num == DEV_ENCODE(12,255)) tmp = "consolectl"; + if (tmp) { + snprintf( + P_tty_text, + sizeof P_tty_text, + tmp, + "0123456789abcdefghijklmnopqrstuvwxyz"[tty_min&31] + ); + } +#endif + + if(num < 9) return 0; + if(P_pid != pid) return 0; + return 1; +} +#endif + +#ifdef __linux__ +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) { + char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */ + int num; + int fd; + char* tmp; + struct stat sb; /* stat() used to get EUID */ + snprintf(buf, 32, "/proc/%d/stat", pid); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0; + num = read(fd, buf, sizeof buf - 1); + fstat(fd, &sb); + P_euid = sb.st_uid; + close(fd); + if(num<80) return 0; + buf[num] = '\0'; + tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ + *tmp = '\0'; /* replace trailing ')' with NUL */ + /* parse these two strings separately, skipping the leading "(". */ + memset(P_cmd, 0, sizeof P_cmd); /* clear */ + sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */ + num = sscanf(tmp + 2, /* skip space after ')' too */ + "%c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %ld %ld " + "%lu %lu " + "%ld " + "%lu %lu %lu %lu %lu %lu " + "%u %u %u %u " /* no use for RT signals */ + "%lu %lu %lu", + &P_state, + &P_ppid, &P_pgrp, &P_session, &P_tty_num, &P_tpgid, + &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime, + &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_alarm, + &P_start_time, &P_vsize, + &P_rss, + &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip, + &P_signal, &P_blocked, &P_sigignore, &P_sigcatch, + &P_wchan, &P_nswap, &P_cnswap + ); +/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + P_vsize /= 1024; + P_rss *= (PAGE_SIZE/1024); + + memcpy(P_tty_text, " ? ", 8); + if (P_tty_num != NO_TTY_VALUE) { + int tty_maj = (P_tty_num>>8)&0xfff; + int tty_min = (P_tty_num&0xff) | ((P_tty_num>>12)&0xfff00); + snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min); + } + + if(num < 30) return 0; + if(P_pid != pid) return 0; + return 1; +} +#endif + +static const char *do_time(unsigned long t){ + int hh,mm,ss; + static char buf[32]; + int cnt = 0; + t /= HZ; + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t); + snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss); + return buf; +} + +static const char *do_user(void){ + static char buf[32]; + static struct passwd *p; + static int lastuid = -1; + if(P_euid != lastuid){ + p = getpwuid(P_euid); + if(p) snprintf(buf, sizeof buf, "%-8.8s", p->pw_name); + else snprintf(buf, sizeof buf, "%5d ", P_euid); + } + return buf; +} + +static const char *do_cpu(int longform){ + static char buf[8]; + strcpy(buf," - "); + if(!longform) buf[2] = '\0'; + return buf; +} + +static const char *do_mem(int longform){ + static char buf[8]; + strcpy(buf," - "); + if(!longform) buf[2] = '\0'; + return buf; +} + +static const char *do_stime(void){ + static char buf[32]; + strcpy(buf," - "); + return buf; +} + +static void print_proc(void){ + switch(ps_format){ + case 0: + printf("%5d %s %s", P_pid, P_tty_text, do_time(P_utime+P_stime)); + break; + case 'o': + printf("%d\n", P_pid); + return; /* don't want the command */ + case 'l': + printf( + "0 %c %5d %5d %5d %s %3d %3d - " + "%5ld %06x %s %s", + P_state, P_euid, P_pid, P_ppid, do_cpu(0), + (int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024), + (unsigned)(P_wchan&0xffffff), P_tty_text, do_time(P_utime+P_stime) + ); + break; + case 'f': + printf( + "%8s %5d %5d %s %s %s %s", + do_user(), P_pid, P_ppid, do_cpu(0), do_stime(), P_tty_text, do_time(P_utime+P_stime) + ); + break; + case 'j': + printf( + "%5d %5d %5d %s %s", + P_pid, P_pgrp, P_session, P_tty_text, do_time(P_utime+P_stime) + ); + break; + case 'u'|0x80: + printf( + "%8s %5d %s %s %5ld %4ld %s %c %s %s", + do_user(), P_pid, do_cpu(1), do_mem(1), P_vsize, P_rss, P_tty_text, P_state, + do_stime(), do_time(P_utime+P_stime) + ); + break; + case 'v'|0x80: + printf( + "%5d %s %c %s %6d - - %5d %s", + P_pid, P_tty_text, P_state, do_time(P_utime+P_stime), (int)P_maj_flt, + (int)P_rss, do_mem(1) + ); + break; + case 'j'|0x80: + printf( + "%5d %5d %5d %5d %s %5d %c %5d %s", + P_ppid, P_pid, P_pgrp, P_session, P_tty_text, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime) + ); + break; + case 'l'|0x80: + printf( + "0 %5d %5d %5d %3d %3d " + "%5ld %4ld %06x %c %s %s", + P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice, + P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, P_tty_text, do_time(P_utime+P_stime) + ); + break; + default: + ; + } + if(show_args) printf(" [%s]\n", P_cmd); + else printf(" %s\n", P_cmd); +} + + +int main(int argc, char *argv[]){ + arg_parse(argc, argv); +#if 0 + choose_dimensions(); +#endif + if(!old_h_option){ + const char *head; + switch(ps_format){ + default: /* can't happen */ + case 0: head = " PID TTY TIME CMD"; break; + case 'l': head = "F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break; + case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break; + case 'j': head = " PID PGID SID TTY TIME CMD"; break; + case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break; + case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break; + case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break; + case 'l'|0x80: head = "F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break; + } + printf("%s\n",head); + } + if(want_one_pid){ + if(stat2proc(want_one_pid)) print_proc(); + else exit(1); + }else{ + struct dirent *ent; /* dirent handle */ + DIR *dir; + int ouruid; + int found_a_proc; + found_a_proc = 0; + ouruid = getuid(); + dir = opendir("/proc"); + while(( ent = readdir(dir) )){ + if(*ent->d_name<'0' || *ent->d_name>'9') continue; + if(!stat2proc(atoi(ent->d_name))) continue; + if(want_one_command){ + if(strcmp(want_one_command,P_cmd)) continue; + }else{ + if(!select_notty && P_tty_num==NO_TTY_VALUE) continue; + if(!select_all && P_euid!=ouruid) continue; + } + found_a_proc++; + print_proc(); + } + closedir(dir); + exit(!found_a_proc); + } + return 0; +} diff --git a/packaging/FAQ b/packaging/FAQ new file mode 100644 index 0000000..26f10dc --- /dev/null +++ b/packaging/FAQ @@ -0,0 +1,82 @@ +Why does "ps -aux" complain about a bogus '-'? + +According to the POSIX and UNIX standards, the above command asks to display +all processes with a TTY (generally the commands users are running) plus all +processes owned by a user named "x". If that user doesn't exist, then ps will +assume you really meant "ps aux". The warning is given to gently break you of a +habit that will cause you trouble if a user named "x" were created. + + +Why don't I see SMP (per-CPU) stats in top? + +You didn't enable it. Press '?' for built-in help or read the man page. Per-CPU +stats are disabled by default because they take up too much space. Some Linux +systems have hundreds of CPUs. + + +Why do long usernames get printed as numbers? + +The UNIX and POSIX standards require that user names and group names be printed +as decimal integers when there is not enough room in the column. Truncating the +names, besides being a violation of the standard, would lead to confusion +between names like MichelleRichards and MichelleRichardson. The UNIX and POSIX +way to change column width is to rename the column: + + ps -o pid,user=CumbersomeUserNames -o comm + +The easy way is to directly specify the desired width: + + ps -o pid,user:19,comm + + +Why is %CPU underreported for multi-threaded (Java, etc.) apps? + +You need to upgrade to the 2.6.10 kernel at least. Older kernels do not provide +a reasonable way to get this information. + + +Why do ps and top show threads individually? + +The 2.4.xx kernel does not provide proper support for grouping threads by +process. Hacks exist to group them anyway, but such hacks will falsely group +similar tasks and will fail to group tasks due to race conditions. The hacks +are also slow. As none of this is acceptable in a critical system tool, task +grouping is not currently available for the 2.4.xx kernel. The 2.6.xx kernel +allows for proper thread grouping and reporting. To take advantage of this, +your programs must use a threading library that features the CLONE_THREAD flag. +The NPTL pthreads provided by recent glibc releases use CLONE_THREAD. + + +What systems are supported? + +Linux 2.4.xx and 2.6.xx are commonly tested and expected to work well. SMP is +well supported. Multi-node cluster views require a multi-node /proc filesystem; +without that you will see a single-node view. + + +Where to I send bug reports? + +You may use the Debian bug tracking system or send your report to +procps-feedback@lists.sf.net (no subscription required) instead. + + +Why are there so many procps projects? + +The original maintainer seems to have had little time for procps. Whatever his +reasons, the project didn't get maintained. Starting in 1997, Albert Cahalan +wrote a new ps program for the package. For the next few years, Albert quietly +helped the Debian package maintainer fix bugs. In 2001, Rik van Riel decided to +do something about what appeared to be the lack of a maintainer. He picked up +the buggy old code in Red Hat's CVS and started adding patches. Meanwhile, +other people have patched procps in a great many ways. In 2002, Albert moved +procps to this site. This was done to ensure that years of testing and bug +fixes would not be lost. The major version number was changed to 3, partly to +avoid confusing users and partly because the top program has been redone. + + +Why does ps get signal 17? + +No ps release has ever had this problem. Most likely your system has been +broken into. You might want to install a more recent version of the OS. If +you'd rather take your chances, simply upgrade procps. + diff --git a/packaging/MD5SUM b/packaging/MD5SUM new file mode 100644 index 0000000..ef79eeb --- /dev/null +++ b/packaging/MD5SUM @@ -0,0 +1 @@ +9532714b6846013ca9898984ba4cd7e0 procps-3.2.8.tar.gz diff --git a/packaging/fix-makefile-error-with-as-needed.patch b/packaging/fix-makefile-error-with-as-needed.patch new file mode 100644 index 0000000..5282a42 --- /dev/null +++ b/packaging/fix-makefile-error-with-as-needed.patch @@ -0,0 +1,13 @@ +diff --git a/Makefile b/Makefile +index 3f67bbf..e4a1adc 100644 +--- a/Makefile ++++ b/Makefile +@@ -67,7 +67,7 @@ TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \ + # plus the top-level Makefile to make it work stand-alone. + _TARFILES := Makefile + +-CURSES := -lncursesw ++CURSES := -ltinfo -lncursesw + + # This seems about right for the dynamic library stuff. + # Something like this is probably needed to make the SE Linux diff --git a/packaging/procps-3.1.15-misc.patch b/packaging/procps-3.1.15-misc.patch new file mode 100644 index 0000000..047674b --- /dev/null +++ b/packaging/procps-3.1.15-misc.patch @@ -0,0 +1,35 @@ +--- procps-3.2.2/watch.c.misc 2004-07-10 14:58:26.000000000 -0400 ++++ procps-3.2.2/watch.c 2004-07-19 10:54:05.905274300 -0400 +@@ -24,6 +24,9 @@ + #include + #include + #include "proc/procps.h" ++#include ++#include ++#include + + static struct option longopts[] = { + {"differences", optional_argument, 0, 'd'}, +@@ -93,6 +96,7 @@ + main(int argc, char *argv[]) + { + int optc; ++ int fd; + int option_differences = 0, + option_differences_cumulative = 0, + option_help = 0, option_version = 0; +@@ -213,7 +217,13 @@ + mvaddstr(0, width - tsl + 1, ts); + free(header); + } +- ++ close(0); ++ fd=open("/dev/null",O_RDWR); ++ if (fd!=0) { ++ perror("open"); ++ do_exit(2); ++ } ++ + if (!(p = popen(command, "r"))) { + perror("popen"); + do_exit(2); diff --git a/packaging/procps-3.2.1-selinux-workaround.patch b/packaging/procps-3.2.1-selinux-workaround.patch new file mode 100644 index 0000000..dde47e3 --- /dev/null +++ b/packaging/procps-3.2.1-selinux-workaround.patch @@ -0,0 +1,13 @@ +--- procps-3.2.1/w.c.selinux-workaround 2003-09-20 02:42:26.000000000 -0400 ++++ procps-3.2.1/w.c 2004-06-15 10:45:53.000000000 -0400 +@@ -162,6 +162,10 @@ + if(best && tmp->start_time <= best->start_time) continue; + best = tmp; + } ++ /* It is there but SELinux wouldn't allow us to know the detail. Really ++ w should just be given rights */ ++ if(!kill(u->ut_pid, 0) || errno != ESRCH) ++ *found_utpid = 1; + return best ? best : secondbest; + } + diff --git a/packaging/procps-3.2.3-FAQ.patch b/packaging/procps-3.2.3-FAQ.patch new file mode 100644 index 0000000..df8dabb --- /dev/null +++ b/packaging/procps-3.2.3-FAQ.patch @@ -0,0 +1,46 @@ +--- procps-3.2.6/proc/version.h.FAQ 2002-12-15 01:08:32.000000000 +0100 ++++ procps-3.2.6/proc/version.h 2005-10-31 20:29:18.000000000 +0100 +@@ -16,6 +16,7 @@ + + extern void display_version(void); /* display suite version */ + extern const char procps_version[]; /* global buf for suite version */ ++extern const char procps_number_version[]; /* global buf for suite number version */ + + extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE + in /usr/include/linux/version.h */ +--- procps-3.2.6/proc/version.c.FAQ 2003-01-29 02:11:43.000000000 +0100 ++++ procps-3.2.6/proc/version.c 2005-10-31 20:29:18.000000000 +0100 +@@ -16,8 +16,10 @@ + + #ifdef MINORVERSION + const char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION; ++const char procps_number_version[] = VERSION "." SUBVERSION "." MINORVERSION; + #else +-const char procps_version[] = "procps version " VERSION "." SUBVERSION; ++const char procps_version[] = "procps version " VERSION "." SUBVERSION; ++const char procps_number_version[] = VERSION "." SUBVERSION; + #endif + + void display_version(void) { +--- procps-3.2.6/proc/library.map.FAQ 2005-03-14 05:32:40.000000000 +0100 ++++ procps-3.2.6/proc/library.map 2005-10-31 20:32:39.000000000 +0100 +@@ -9,7 +9,7 @@ + escape_str; escape_strlist; + openproc; closeproc; + tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan; +- display_version; procps_version; linux_version_code; ++ display_version; procps_version; procps_number_version; linux_version_code; + Hertz; smp_num_cpus; have_privs; + sprint_uptime; uptime; user_from_uid; print_uptime; loadavg; + pretty_print_signals; print_given_signals; unix_print_signals; signal_name_to_number; signal_number_to_name; +--- procps-3.2.6/ps/parser.c.FAQ 2005-10-31 20:29:18.000000000 +0100 ++++ procps-3.2.6/ps/parser.c 2005-10-31 20:29:18.000000000 +0100 +@@ -1238,7 +1238,7 @@ + // as SysV options... and you're screwed if you've been patching + // out the friendly warning. Cut-over is likely to be in 2005. + if(!(personality & PER_FORCE_BSD)) +- fprintf(stderr, "Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html\n"); ++ fprintf(stderr, "Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-%s/FAQ\n",procps_number_version); + // Remember: contact albert@users.sf.net or procps-feedback@lists.sf.net + // if you should feel tempted. Be damn sure you understand all + // the issues. The same goes for other stuff too, BTW. Please ask. diff --git a/packaging/procps-3.2.3-noproc.patch b/packaging/procps-3.2.3-noproc.patch new file mode 100644 index 0000000..d373473 --- /dev/null +++ b/packaging/procps-3.2.3-noproc.patch @@ -0,0 +1,18 @@ +--- procps-3.2.3/top.c~ 2004-09-28 13:39:31.352488862 -0400 ++++ procps-3.2.3/top.c 2004-09-28 13:42:47.659416849 -0400 +@@ -3201,12 +3201,15 @@ + + int main (int dont_care_argc, char *argv[]) + { ++ struct stat isproc; + (void)dont_care_argc; + before(*argv); + // +-------------+ + windows_stage1(); // top (sic) slice + configs_read(); // > spread etc, < + parse_args(&argv[1]); // > lean stuff, < ++ if (stat("/proc/self", &isproc)==-1) ++ std_err("/proc is not mounted, required for output data"); + whack_terminal(); // > onions etc. < + windows_stage2(); // as bottom slice + // +-------------+ diff --git a/packaging/procps-3.2.3-pseudo.patch b/packaging/procps-3.2.3-pseudo.patch new file mode 100644 index 0000000..4a00b61 --- /dev/null +++ b/packaging/procps-3.2.3-pseudo.patch @@ -0,0 +1,18 @@ +--- procps-3.2.5/top.h.pseudo 2005-03-17 13:32:05.305659080 +0100 ++++ procps-3.2.5/top.h 2005-03-17 13:34:04.837487480 +0100 +@@ -149,8 +149,13 @@ + int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ + if (Batch) _ptr = _str; \ + else { \ +- _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ +- if (memcmp(_ptr, _str, _len)) { \ ++ int _rest; \ ++ _ptr = &Pseudo_scrn[Pseudo_row * Pseudo_cols]; \ ++ _rest = Pseudo_size-(_ptr-Pseudo_scrn); \ ++ Pseudo_row = (Pseudo_row+1) % Screen_rows; \ ++ if (_rest < _len) \ ++ _len = _rest; \ ++ if (memcmp(_ptr, _str, _len)) { \ + memcpy(_ptr, _str, _len); \ + } else { \ + _ptr = "\n"; \ diff --git a/packaging/procps-3.2.4-0x9b.patch b/packaging/procps-3.2.4-0x9b.patch new file mode 100644 index 0000000..6ba79e8 --- /dev/null +++ b/packaging/procps-3.2.4-0x9b.patch @@ -0,0 +1,27 @@ +--- procps-3.2.4beta/proc/escape.c.0x9b 2004-10-23 19:44:28.000000000 +0200 ++++ procps-3.2.4beta/proc/escape.c 2004-11-04 15:22:00.196344424 +0100 +@@ -79,6 +79,7 @@ + // multibyte - printable + // Got space? + if (my_cells+wlen > *maxcells || my_bytes+1+len >= bufsize) break; ++#ifdef OUTPUT_PARANOID + // 0x9b is control byte for some terminals + if (memchr(src, 0x9B, len)) { + // unsafe multibyte +@@ -87,13 +88,16 @@ + my_cells++; + my_bytes++; + } else { ++#endif + // safe multibyte + memcpy(dst, src, len); + my_cells += wlen; + dst += len; + my_bytes += len; + src += len; ++#ifdef OUTPUT_PARANOID + } ++#endif + } + } + //fprintf(stdout, "cells: %d\n", my_cells); diff --git a/packaging/procps-3.2.5-sysctl-writeonly.patch b/packaging/procps-3.2.5-sysctl-writeonly.patch new file mode 100644 index 0000000..3e04ccf --- /dev/null +++ b/packaging/procps-3.2.5-sysctl-writeonly.patch @@ -0,0 +1,55 @@ +--- procps-3.2.6/sysctl.c.writeonly 2005-01-05 22:00:47.000000000 +0100 ++++ procps-3.2.6/sysctl.c 2005-11-08 11:24:19.000000000 +0100 +@@ -127,7 +127,8 @@ + char *restrict tmpname; + char *restrict outname; + char inbuf[1025]; +- FILE *restrict fp; ++ FILE *restrict fp = NULL; ++ struct stat st; + + if (!name || !*name) { + fprintf(stderr, ERR_INVALID_KEY, name); +@@ -144,7 +145,13 @@ + outname = strdup(name); + slashdot(outname,'/','.'); /* change / to . */ + +- fp = fopen(tmpname, "r"); ++ if (stat(tmpname, &st)==0) { ++ if (st.st_mode & (S_IRUSR|S_IROTH|S_IRGRP)) ++ fp = fopen(tmpname, "r"); ++ else ++ /* ignore write-only files */ ++ return -1; ++ } + + if (!fp) { + switch(errno) { +@@ -158,12 +165,16 @@ + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; ++ case EINVAL: /* fopen()/stat() invalid argument */ ++ case ENOTTY: /* inappropriate ioctl for device */ ++ break; + default: + fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname); + rc = -1; + break; + } + } else { ++ errno=0; + if(fgets(inbuf, sizeof inbuf - 1, fp)) { + // this loop is required, see + // /sbin/sysctl -a | egrep -6 dev.cdrom.info +@@ -183,7 +194,9 @@ + } + } + } while(fgets(inbuf, sizeof inbuf - 1, fp)); +- } else { ++ ++ /* fgets() returns NULL and errno without change if the file is empty */ ++ } else if (errno) { + switch(errno) { + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); diff --git a/packaging/procps-3.2.5-top-rc.patch b/packaging/procps-3.2.5-top-rc.patch new file mode 100644 index 0000000..eb179db --- /dev/null +++ b/packaging/procps-3.2.5-top-rc.patch @@ -0,0 +1,14 @@ +--- procps-3.2.5/top.c.rc 2005-06-28 22:36:24.000000000 +0200 ++++ procps-3.2.5/top.c 2005-06-28 22:40:27.000000000 +0200 +@@ -2393,7 +2393,10 @@ + int i; + + for (i = 0; i < GROUPSMAX; i++) { +- win_names(&Winstk[i], Winstk[i].rc.winname); ++ /* Please, never use something like sprintf(x, "%s", x); ... see win_names() */ ++ char buf[WINNAMSIZ]; ++ strncpy(buf, Winstk[i].rc.winname, WINNAMSIZ); ++ win_names(&Winstk[i], buf); + capsmk(&Winstk[i]); + } + // rely on this next guy to force a call (eventually) to reframewins diff --git a/packaging/procps-3.2.6-libselinux.patch b/packaging/procps-3.2.6-libselinux.patch new file mode 100644 index 0000000..7b2c3b7 --- /dev/null +++ b/packaging/procps-3.2.6-libselinux.patch @@ -0,0 +1,59 @@ +--- procps-3.2.6/ps/output.c~ 2005-10-29 23:18:19.000000000 -0400 ++++ procps-3.2.6/ps/output.c 2005-12-13 16:27:15.000000000 -0500 +@@ -1090,7 +1090,7 @@ + return snprintf(outbuf, COLWID, "*"); + } + +- ++#ifdef STATICLIB + /****************** FLASK & seLinux security stuff **********************/ + // move the bulk of this to libproc sometime + +@@ -1122,7 +1122,7 @@ + return 1; + } + +-#if 0 ++#else + // This needs more study, considering: + // 1. the static linking option (maybe disable this in that case) + // 2. the -z and -Z option issue +@@ -1134,7 +1134,7 @@ + char *context; + + if(!ps_getpidcon && !tried_load){ +- void *handle = dlopen("libselinux.so.1", RTLD_NOW); ++ void *handle = dlopen("libselinux.so", RTLD_NOW); + if(handle){ + dlerror(); + ps_getpidcon = dlsym(handle, "getpidcon"); +--- procps-3.2.6/Makefile~ 2005-10-30 01:27:04.000000000 -0500 ++++ procps-3.2.6/Makefile 2005-12-13 16:32:09.000000000 -0500 +@@ -73,12 +73,12 @@ + # Something like this is probably needed to make the SE Linux + # library loading not conflict with embedded systems stuff. + # +-#ifeq ($(SHARED),1) +-#ldl := -ldl +-#LIBTYPE := -DSHAREDLIB +-#else +-#LIBTYPE := -DSTATICLIB +-#endif ++ifeq ($(SHARED),1) ++ldl := -ldl ++LIBTYPE := -DSHAREDLIB ++else ++LIBTYPE := -DSTATICLIB ++endif + + # Preprocessor flags. + PKG_CPPFLAGS := -D_GNU_SOURCE -I proc +@@ -103,7 +103,7 @@ + # Note that some stuff below is conditional on CFLAGS containing + # an option that starts with "-g". (-g, -g2, -g3, -ggdb, etc.) + CFLAGS := -O2 -s +-ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS) ++ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS) $(LIBTYPE) + + PKG_LDFLAGS := -Wl,-warn-common + LDFLAGS := diff --git a/packaging/procps-3.2.6-top-env-cpuloop.patch b/packaging/procps-3.2.6-top-env-cpuloop.patch new file mode 100644 index 0000000..cb819c2 --- /dev/null +++ b/packaging/procps-3.2.6-top-env-cpuloop.patch @@ -0,0 +1,81 @@ +--- procps-3.2.7/top.c.kzak 2006-08-31 10:44:42.000000000 +0200 ++++ procps-3.2.7/top.c 2006-08-31 10:46:15.000000000 +0200 +@@ -121,6 +121,7 @@ + Secure_mode = 0; // set if some functionality restricted + + static int VIRT_vmsize = 0; //use vm_size for VIRT ++static int CPU_loop = 0; // wait for reliable CPU data + + /* Some cap's stuff to reduce runtime calls -- + to accomodate 'Batch' mode, they begin life as empty strings */ +@@ -1679,6 +1680,8 @@ + + if (( getenv("STATSIZE")) && ( atoi(getenv("STATSIZE")) == 1 )) + VIRT_vmsize = 1; ++ if (( getenv("CPULOOP")) && ( atoi(getenv("CPULOOP")) == 1 )) ++ CPU_loop = 1; + } + + +@@ -2978,6 +2981,7 @@ + { + static proc_t **p_table = NULL; + static CPU_t *smpcpu = NULL; ++ static int first = 0; + + // whoa first time, gotta' prime the pump... + if (!p_table) { +@@ -3025,6 +3029,28 @@ + + smpcpu = cpus_refresh(smpcpu); + ++ if (first==0 && CPU_loop) ++ { ++ int i; ++ CPU_t *cpu; ++ for (i = 0; i < Cpu_tot; i++) { ++ cpu = &smpcpu[i]; ++ cpu->u_sav = cpu->u; ++ cpu->s_sav = cpu->s; ++ cpu->n_sav = cpu->n; ++ cpu->i_sav = cpu->i; ++ cpu->w_sav = cpu->w; ++ cpu->x_sav = cpu->x; ++ cpu->y_sav = cpu->y; ++ cpu->z_sav = cpu->z; ++ } ++ tv.tv_sec = Rc.delay_time; ++ tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000; ++ select(0, NULL, NULL, NULL, &tv); ++ ++ smpcpu = cpus_refresh(smpcpu); ++ } ++ + if (CHKw(Curwin, View_CPUSUM)) { + // display just the 1st /proc/stat line + summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):"); +@@ -3050,6 +3076,8 @@ + } + + SETw(Curwin, NEWFRAM_cwo); ++ ++ first = 1; + return p_table; + } + +--- procps-3.2.7/top.1.kzak 2006-08-31 10:44:42.000000000 +0200 ++++ procps-3.2.7/top.1 2006-08-31 10:44:42.000000000 +0200 +@@ -1183,6 +1183,13 @@ + Send bug reports to: + Albert D\. Cahalan, + ++The top command calculates Cpu(s) by looking at the change in CPU time values ++between samples. When you first run it, it has no previous sample to compare ++to, so these initial values are the percentages since boot. It means you need ++at least two loops or you have to ignore summary output from the first loop. ++This is problem for example for batch mode. There is a possible workaround if ++you define the CPULOOP=1 environment variable. The top command will be run one ++extra hidden loop for CPU data before standard output. + + .\" ---------------------------------------------------------------------- + .SH 8. HISTORY Former top diff --git a/packaging/procps-3.2.6-top-env-vmsize.patch b/packaging/procps-3.2.6-top-env-vmsize.patch new file mode 100644 index 0000000..ec46b99 --- /dev/null +++ b/packaging/procps-3.2.6-top-env-vmsize.patch @@ -0,0 +1,53 @@ +--- procps-3.2.6/top.1.env-vmsize 2006-04-19 16:50:37.000000000 +0200 ++++ procps-3.2.6/top.1 2006-04-19 16:59:23.000000000 +0200 +@@ -395,7 +395,8 @@ + o:\fB VIRT\fR \*(EM Virtual Image (kb) + The total amount of \*(MV used by the task. + It includes all code, data and shared libraries plus pages that have been +-swapped out. ++swapped out. (Note: you can define the STATSIZE=1 environment variable and the VIRT ++will be calculated from the /proc/#/state VmSize field.) + + VIRT = SWAP + RES. + +--- procps-3.2.6/top.c.env-vmsize 2006-04-19 16:35:20.000000000 +0200 ++++ procps-3.2.6/top.c 2006-04-19 16:50:25.000000000 +0200 +@@ -120,6 +120,8 @@ + Loops = -1, // number of iterations, -1 loops forever + Secure_mode = 0; // set if some functionality restricted + ++static int VIRT_vmsize = 0; //use vm_size for VIRT ++ + /* Some cap's stuff to reduce runtime calls -- + to accomodate 'Batch' mode, they begin life as empty strings */ + static char Cap_clr_eol [CAPBUFSIZ], +@@ -1667,6 +1669,9 @@ + Fieldstab[P_PPD].fmts = pid_fmt; + Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits; + } ++ ++ if (( getenv("STATSIZE")) && ( atoi(getenv("STATSIZE")) == 1 )) ++ VIRT_vmsize = 1; + } + + +@@ -2194,6 +2199,7 @@ + } + if (!Frames_libflags) Frames_libflags = L_DEFAULT; + if (selection_type=='p') Frames_libflags |= PROC_PID; ++ if (VIRT_vmsize) Frames_libflags |= L_status; + } + + +@@ -3157,7 +3163,10 @@ + MKCOL(p->ruser); + break; + case P_VRT: +- MKCOL(scale_num(PAGES_TO_KB(p->size), w, s)); ++ if (VIRT_vmsize) ++ MKCOL(scale_num(PAGES_TO_KB(p->vm_size), w, s)); ++ else ++ MKCOL(scale_num(PAGES_TO_KB(p->size), w, s)); + break; + case P_WCH: + if (No_ksyms) { diff --git a/packaging/procps-3.2.7-free-hlmem.patch b/packaging/procps-3.2.7-free-hlmem.patch new file mode 100644 index 0000000..c5ef9cc --- /dev/null +++ b/packaging/procps-3.2.7-free-hlmem.patch @@ -0,0 +1,23 @@ +diff -up procps-3.2.7/free.1.hlmem procps-3.2.7/free.1 +--- procps-3.2.7/free.1.hlmem 2009-02-24 10:54:35.000000000 +0100 ++++ procps-3.2.7/free.1 2009-02-24 11:00:26.000000000 +0100 +@@ -7,7 +7,7 @@ free \- Display amount of free and used + .SH SYNOPSIS + .BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s" + .I delay +-.RB "] [" "\-t" "] [" "\-V" ] ++.RB "] [" "\-t" "] [" "\-l" "] [" "\-V" "] + .SH DESCRIPTION + \fBfree\fP displays the total amount of free and used physical and swap + memory in the system, as well as the buffers used by the kernel. +@@ -28,7 +28,9 @@ may actually specify any floating point + .BR usleep (3) + is used for microsecond resolution delay times. + .PP +-The \fB\-V\fP displays version information. ++The \fB-l\fP switch shows detailed low and high memory statistics. ++.PP ++The \fB\-V\fP switch displays version information. + .SH FILES + .ta + .IR /proc/meminfo "\-\- memory information" diff --git a/packaging/procps-3.2.7-longcmd.patch b/packaging/procps-3.2.7-longcmd.patch new file mode 100644 index 0000000..7c4fdca --- /dev/null +++ b/packaging/procps-3.2.7-longcmd.patch @@ -0,0 +1,22 @@ +--- procps-3.2.7/proc/readproc.c.kzak 2006-06-16 10:18:13.000000000 +0200 ++++ procps-3.2.7/proc/readproc.c 2006-09-27 11:25:13.000000000 +0200 +@@ -432,14 +432,17 @@ + if(fd==-1) return NULL; + + /* read whole file into a memory buffer, allocating as we go */ +- while ((n = read(fd, buf, sizeof buf - 1)) > 0) { ++ while ((n = read(fd, buf, sizeof buf - 1)) >= 0) { + if (n < (int)(sizeof buf - 1)) + end_of_file = 1; +- if (n == 0 && rbuf == 0) ++ if (n == 0 && rbuf == 0) { ++ close(fd); + return NULL; /* process died between our open and read */ ++ } + if (n < 0) { + if (rbuf) + free(rbuf); ++ close(fd); + return NULL; /* read error */ + } + if (end_of_file && buf[n-1]) /* last read char not null */ diff --git a/packaging/procps-3.2.7-ps-eip64.patch b/packaging/procps-3.2.7-ps-eip64.patch new file mode 100644 index 0000000..9eb851f --- /dev/null +++ b/packaging/procps-3.2.7-ps-eip64.patch @@ -0,0 +1,48 @@ +--- procps-3.2.7/ps/ps.1.eip64 2007-06-14 11:08:35.000000000 +0200 ++++ procps-3.2.7/ps/ps.1 2007-06-14 11:03:28.000000000 +0200 +@@ -1156,6 +1156,14 @@ + or\ a\ decimal representation otherwise. + T} + ++rip RIP T{ ++64-bit instruction pointer. ++T} ++ ++rsp RSP T{ ++64-bit stack pointer. ++T} ++ + rss RSS T{ + resident set size, the non\-swapped physical memory that + a task has used (in\ kiloBytes). +--- procps-3.2.7/ps/output.c.eip64 2007-06-14 11:08:24.000000000 +0200 ++++ procps-3.2.7/ps/output.c 2007-06-14 10:58:21.000000000 +0200 +@@ -764,10 +764,18 @@ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp)); + } + ++static int pr_rsp(char *restrict const outbuf, const proc_t *restrict const pp){ ++ return snprintf(outbuf, COLWID, "%016lx", (unsigned long)(pp->kstk_esp)); ++} ++ + static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip)); + } + ++static int pr_rip(char *restrict const outbuf, const proc_t *restrict const pp){ ++ return snprintf(outbuf, COLWID, "%016lx", (unsigned long)(pp->kstk_eip)); ++} ++ + /* This function helps print old-style time formats */ + static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) { + if(!t) return snprintf(dst, COLWID, " -"); +@@ -1403,7 +1411,9 @@ + {"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT}, + {"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT}, + {"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */ ++{"rip", "RIP", pr_rip, sr_kstk_eip, 16, 0, LNX, TO|RIGHT}, + {"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, ++{"rsp", "RSP", pr_rsp, sr_kstk_eip, 16, 0, LNX, TO|RIGHT}, + {"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */ + {"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/ + {"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/ diff --git a/packaging/procps-3.2.7-ps-libselinux.patch b/packaging/procps-3.2.7-ps-libselinux.patch new file mode 100644 index 0000000..4721358 --- /dev/null +++ b/packaging/procps-3.2.7-ps-libselinux.patch @@ -0,0 +1,11 @@ +--- procps-3.2.7/ps/output.c.libselinux 2007-08-27 08:34:13.000000000 +0200 ++++ procps-3.2.7/ps/output.c 2007-08-27 08:34:30.000000000 +0200 +@@ -1141,7 +1141,7 @@ + char *context; + + if(!ps_getpidcon && !tried_load){ +- void *handle = dlopen("libselinux.so", RTLD_NOW); ++ void *handle = dlopen("libselinux.so.1", RTLD_NOW); + if(handle){ + dlerror(); + ps_getpidcon = dlsym(handle, "getpidcon"); diff --git a/packaging/procps-3.2.7-ps-man-fmt.patch b/packaging/procps-3.2.7-ps-man-fmt.patch new file mode 100644 index 0000000..f3a80dc --- /dev/null +++ b/packaging/procps-3.2.7-ps-man-fmt.patch @@ -0,0 +1,12 @@ +diff -up procps-3.2.7/ps/ps.1.ps-man-fmt procps-3.2.7/ps/ps.1 +--- procps-3.2.7/ps/ps.1.ps-man-fmt 2008-09-01 11:43:18.000000000 +0200 ++++ procps-3.2.7/ps/ps.1 2008-09-01 11:43:41.000000000 +0200 +@@ -30,7 +30,7 @@ + .\" the space needed for the 1st two columns. + .\" Making it messy: inches, ens, points, scaled points... + .\" +-.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-20u)n) ++.nr ColSize ((\n[.l] - \n[.i]) / 1n - 20) + .\" + .\" This is for command options + .nr OptSize (16u) diff --git a/packaging/procps-3.2.7-ps-stime.patch b/packaging/procps-3.2.7-ps-stime.patch new file mode 100644 index 0000000..d58b77e --- /dev/null +++ b/packaging/procps-3.2.7-ps-stime.patch @@ -0,0 +1,71 @@ +--- procps-3.2.7/ps/output.c.jitter 2007-04-26 13:15:47.000000000 +0200 ++++ procps-3.2.7/ps/output.c 2007-04-26 13:31:24.000000000 +0200 +@@ -77,7 +77,6 @@ + static int wide_signals; /* true if we have room */ + + static unsigned long seconds_since_1970; +-static unsigned long time_of_boot; + static unsigned long page_shift; + + +@@ -1952,7 +1951,6 @@ + // available space: page_size*outbuf_pages-SPACE_AMOUNT + + seconds_since_1970 = time(NULL); +- time_of_boot = seconds_since_1970 - seconds_since_boot; + + meminfo(); + +--- procps-3.2.7/ps/common.h.jitter 2005-01-27 04:43:22.000000000 +0100 ++++ procps-3.2.7/ps/common.h 2007-04-26 12:44:01.000000000 +0200 +@@ -302,6 +302,7 @@ + extern int screen_cols; + extern int screen_rows; + extern unsigned long seconds_since_boot; ++extern unsigned long time_of_boot; + extern selection_node *selection_list; + extern unsigned simple_select; + extern sort_node *sort_list; +--- procps-3.2.7/ps/global.c.jitter 2005-10-30 01:43:34.000000000 +0200 ++++ procps-3.2.7/ps/global.c 2007-04-26 13:26:38.000000000 +0200 +@@ -70,6 +70,7 @@ + int screen_cols = -1; + int screen_rows = -1; + unsigned long seconds_since_boot = -1; ++unsigned long time_of_boot = -1; + selection_node *selection_list = (selection_node *)0xdeadbeef; + unsigned simple_select = 0xffffffff; + sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */ +@@ -361,7 +362,23 @@ + look_up_our_self(&p); + set_screen_size(); + set_personality(); +- ++ int fd; ++ char *buf[BUFFSIZE]; ++ const char *b; ++ ++ /* get boot time from /proc/stat */ ++ fd = open("/proc/stat", O_RDONLY, 0); ++ if (fd != -1) { ++ buf[BUFFSIZE-1] = 0; ++ read(fd, buf, BUFFSIZE-1); ++ b = strstr(buf, "btime "); ++ if (b) { ++ sscanf(b, "btime %lu", &time_of_boot); ++ seconds_since_boot = time(0) - time_of_boot; ++ } ++ close(fd); ++ } ++ + all_processes = 0; + bsd_c_option = 0; + bsd_e_option = 0; +@@ -380,7 +397,6 @@ + negate_selection = 0; + page_size = getpagesize(); + running_only = 0; +- seconds_since_boot = uptime(0,0); + selection_list = NULL; + simple_select = 0; + sort_list = NULL; diff --git a/packaging/procps-3.2.7-psman.patch b/packaging/procps-3.2.7-psman.patch new file mode 100644 index 0000000..7ba6cff --- /dev/null +++ b/packaging/procps-3.2.7-psman.patch @@ -0,0 +1,22 @@ +--- procps-3.2.7/ps/ps.1.psman 2007-06-20 08:30:47.000000000 +0200 ++++ procps-3.2.7/ps/ps.1 2007-06-20 08:50:10.000000000 +0200 +@@ -21,8 +21,8 @@ + .\" See /usr/share/groff/current/tmac/an-old.tmac for what these do. + .\" Setting them to zero provides extra space, but only do that for + .\" plain text output. PostScript and such will remain indented. +-.if n .nr IN 0n +-.if n .nr an-prevailing-indent 0n ++.\" .if n .nr IN 0n ++.\" .if n .nr an-prevailing-indent 0n + .\" + .\" + .\" ColSize is used for the format spec table. +@@ -838,6 +838,8 @@ + .\" lB1 lB1 lBw(5.5i) + .\" lB1 l1 l. + .\" ++.RE ++ + .TS + expand; + lB1 lB1 lBw(\n[ColSize]n) diff --git a/packaging/procps-3.2.7-selinux.patch b/packaging/procps-3.2.7-selinux.patch new file mode 100644 index 0000000..9935c94 --- /dev/null +++ b/packaging/procps-3.2.7-selinux.patch @@ -0,0 +1,23 @@ +--- procps-3.2.7/ps/parser.c.selinux 2006-06-25 09:14:58.000000000 +0200 ++++ procps-3.2.7/ps/parser.c 2006-07-10 10:14:22.000000000 +0200 +@@ -231,7 +231,7 @@ + // In the meantime, please do not add to it. The list is + // intended to ONLY contain flags defined by the POSIX and UNIX + // standards published by The Open Group, IEEE, and ISO. +- if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX ++ if(!strchr("aAdefgGlnoptuUZ", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX + + switch(*flagptr){ + case 'A': +--- procps-3.2.7/ps/ps.1.selinux 2005-10-30 03:44:03.000000000 +0100 ++++ procps-3.2.7/ps/ps.1 2006-07-10 10:08:12.000000000 +0200 +@@ -450,6 +450,9 @@ + Do not show flags; show rss in place of addr. + This option can only be used with \fB\-l\fR. + ++.opt \-Z ++display security context format (SELinux, etc.) ++ + .opt \-\-format \ format + user\-defined format. Identical to \fB\-o\fR and \fBo\fR. + diff --git a/packaging/procps-3.2.7-slabtop-once.patch b/packaging/procps-3.2.7-slabtop-once.patch new file mode 100644 index 0000000..d3319cf --- /dev/null +++ b/packaging/procps-3.2.7-slabtop-once.patch @@ -0,0 +1,113 @@ +diff -up procps-3.2.7/slabtop.c.once procps-3.2.7/slabtop.c +--- procps-3.2.7/slabtop.c.once 2008-12-11 13:24:52.000000000 +0100 ++++ procps-3.2.7/slabtop.c 2008-12-11 13:33:12.000000000 +0100 +@@ -268,9 +268,24 @@ static void parse_input(char c) + } + } + ++/*printw or printf depending on the context*/ ++static void printwf(int once, const char *s,...) ++{ ++va_list va; ++ ++va_start(va,s); ++if(once) ++ vprintf(s,va); ++else ++ vwprintw(stdscr, s, va); ++va_end(va); ++} ++ ++ + int main(int argc, char *argv[]) + { + int o; ++ int once = 0; + unsigned short old_rows; + struct slab_info *slab_list = NULL; + +@@ -307,6 +322,7 @@ int main(int argc, char *argv[]) + break; + case 'o': + delay = 0; ++ once = 1; + break; + case 'V': + display_version(); +@@ -322,12 +338,18 @@ int main(int argc, char *argv[]) + if (tcgetattr(0, &saved_tty) == -1) + perror("tcgetattr"); + +- initscr(); +- term_size(0); +- old_rows = rows; +- resizeterm(rows, cols); +- signal(SIGWINCH, term_size); +- signal(SIGINT, sigint_handler); ++ if(!once) { ++ initscr(); ++ term_size(0); ++ old_rows = rows; ++ resizeterm(rows, cols); ++ signal(SIGWINCH, term_size); ++ signal(SIGINT, sigint_handler); ++ } else { ++ old_rows = rows; ++ rows = 80; ++ cols = 24; ++ } + + do { + struct slab_info *curr; +@@ -341,12 +363,12 @@ int main(int argc, char *argv[]) + break; + + if (old_rows != rows) { +- resizeterm(rows, cols); ++ if(!once) resizeterm(rows, cols); + old_rows = rows; + } + + move(0,0); +- printw( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n" ++ printwf(once, " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n" +@@ -360,15 +382,15 @@ int main(int argc, char *argv[]) + + slab_list = slabsort(slab_list); + +- attron(A_REVERSE); +- printw( "%6s %6s %4s %8s %6s %8s %10s %-23s\n", ++ if(!once) attron(A_REVERSE); ++ printwf(once, "%6s %6s %4s %8s %6s %8s %10s %-23s\n", + "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS", + "OBJ/SLAB", "CACHE SIZE", "NAME"); +- attroff(A_REVERSE); ++ if(!once) attroff(A_REVERSE); + + curr = slab_list; + for (i = 0; i < rows - 8 && curr->next; i++) { +- printw("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", ++ printwf(once, "%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", + curr->nr_objs, curr->nr_active_objs, curr->use, + curr->obj_size / 1024.0, curr->nr_slabs, + curr->objs_per_slab, (unsigned)(curr->cache_size / 1024), +@@ -376,7 +398,7 @@ int main(int argc, char *argv[]) + curr = curr->next; + } + +- refresh(); ++ if(!once) refresh(); + put_slabinfo(slab_list); + + FD_ZERO(&readfds); +@@ -392,6 +414,6 @@ int main(int argc, char *argv[]) + + tcsetattr(0, TCSAFLUSH, &saved_tty); + free_slabinfo(slab_list); +- endwin(); ++ if(!once) endwin(); + return 0; + } diff --git a/packaging/procps-3.2.7-sysctl-ignore.patch b/packaging/procps-3.2.7-sysctl-ignore.patch new file mode 100644 index 0000000..a7d6880 --- /dev/null +++ b/packaging/procps-3.2.7-sysctl-ignore.patch @@ -0,0 +1,87 @@ +--- procps-3.2.7/sysctl.c.kzak 2007-04-03 01:26:01.000000000 +0200 ++++ procps-3.2.7/sysctl.c 2007-04-03 01:26:13.000000000 +0200 +@@ -63,6 +63,44 @@ + static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n"; + static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n"; + ++/* Ignore deprecated sysctls ++ * -- we use this list when we scan (DisplayAll) /proc/sys only. We don't use it ++ * in case when user direcly uses deprecated key. It's better when user can read ++ * an error message from kernel. ++ */ ++struct sysctl_ignore { ++ const char *prefix; ++ int prefix_len; ++ const char *option; ++ int option_len; ++}; ++ ++#define IGNORE_ENTRY(prefix, option) \ ++ { prefix, sizeof(prefix)-1, option, sizeof(option)-1 } ++ ++static struct sysctl_ignore Ignore[] = ++{ ++ IGNORE_ENTRY( "net.ipv6.neigh", "base_reachable_time" ), ++ IGNORE_ENTRY( "net.ipv6.neigh", "retrans_time" ) ++}; ++ ++static bool IsIgnored(const char *name) ++{ ++ unsigned int i; ++ int sz = strlen(name); ++ ++ for (i = 0; i < sizeof(Ignore)/sizeof(struct sysctl_ignore); i++) { ++ struct sysctl_ignore *p = &Ignore[i]; ++ ++ if (sz < (p->prefix_len + p->option_len)) ++ continue; ++ ++ if (strncmp(name, p->prefix, p->prefix_len) == 0 && ++ strcmp(name + (sz - p->option_len), p->option) == 0) ++ return true; ++ } ++ return false; ++} + + static void slashdot(char *restrict p, char old, char new){ + p = strpbrk(p,"/."); +@@ -122,7 +160,7 @@ + * Read a sysctl setting + * + */ +-static int ReadSetting(const char *restrict const name) { ++static int ReadSetting(const char *restrict const name, bool useign) { + int rc = 0; + char *restrict tmpname; + char *restrict outname; +@@ -145,6 +183,12 @@ + outname = strdup(name); + slashdot(outname,'/','.'); /* change / to . */ + ++ if (useign && IsIgnored(outname)) { ++ free(outname); ++ free(tmpname); ++ return rc; ++ } ++ + if (stat(tmpname, &st)==0) { + if (st.st_mode & (S_IRUSR|S_IROTH|S_IRGRP)) + fp = fopen(tmpname, "r"); +@@ -257,7 +301,7 @@ + strcat(tmpdir, "/"); + DisplayAll(tmpdir); + } else { +- rc |= ReadSetting(tmpdir+strlen(PROC_PATH)); ++ rc |= ReadSetting(tmpdir+strlen(PROC_PATH), true); + } + } + free(tmpdir); +@@ -519,7 +563,7 @@ + if (WriteMode || index(*argv, '=')) + ReturnCode = WriteSetting(*argv); + else +- ReturnCode = ReadSetting(*argv); ++ ReturnCode = ReadSetting(*argv, false); + } + } + diff --git a/packaging/procps-3.2.7-top-clrscr.patch b/packaging/procps-3.2.7-top-clrscr.patch new file mode 100644 index 0000000..8a02568 --- /dev/null +++ b/packaging/procps-3.2.7-top-clrscr.patch @@ -0,0 +1,19 @@ +diff -up procps-3.2.7/top.c.clrscr procps-3.2.7/top.c +--- procps-3.2.7/top.c.clrscr 2008-04-08 08:49:48.000000000 +0200 ++++ procps-3.2.7/top.c 2008-04-08 08:50:07.000000000 +0200 +@@ -2109,6 +2109,7 @@ static void fields_sort (void) + *p = x + 'A'; + Curwin->rc.sortindx = x; + putp(Cap_curs_norm); ++ putp(Cap_clr_scr); + } + + +@@ -2134,6 +2135,7 @@ static void fields_toggle (void) + *p = i + 'A'; + } + putp(Cap_curs_norm); ++ putp(Cap_clr_scr); + } + + /*###### Windows/Field Groups support #################################*/ diff --git a/packaging/procps-3.2.7-top-cpu0.patch b/packaging/procps-3.2.7-top-cpu0.patch new file mode 100644 index 0000000..eb81121 --- /dev/null +++ b/packaging/procps-3.2.7-top-cpu0.patch @@ -0,0 +1,20 @@ +--- procps-3.2.7/top.c.orig 2007-09-07 21:28:41.000000000 +0100 ++++ procps-3.2.7/top.c 2007-09-07 21:29:15.000000000 +0100 +@@ -959,8 +959,15 @@ + + // and just in case we're 2.2.xx compiled without SMP support... + if (Cpu_tot == 1) { +- cpus[1].id = 0; +- memcpy(cpus, &cpus[1], sizeof(CPU_t)); ++ cpus[0].id = cpus[1].id = 0; ++ cpus[0].u = cpus[1].u; ++ cpus[0].n = cpus[1].n; ++ cpus[0].s = cpus[1].s; ++ cpus[0].i = cpus[1].i; ++ cpus[0].w = cpus[1].w; ++ cpus[0].x = cpus[1].x; ++ cpus[0].y = cpus[1].y; ++ cpus[0].z = cpus[1].z; + } + + // now value each separate cpu's tics diff --git a/packaging/procps-3.2.7-top-cpuint.patch b/packaging/procps-3.2.7-top-cpuint.patch new file mode 100644 index 0000000..9d204ed --- /dev/null +++ b/packaging/procps-3.2.7-top-cpuint.patch @@ -0,0 +1,12 @@ +diff -up procps-3.2.7/top.c.cpuint procps-3.2.7/top.c +--- procps-3.2.7/top.c.cpuint 2008-01-11 08:32:44.000000000 +0100 ++++ procps-3.2.7/top.c 2008-01-11 08:32:52.000000000 +0100 +@@ -1824,7 +1824,7 @@ static void configs_read (void) + if(Rc.mode_irixps && smp_num_cpus>1){ + // good for 100 CPUs per process + pcpu_max_value = 9999.0; +- Fieldstab[P_CPU].fmts = " %4.0f"; ++ Fieldstab[P_CPU].fmts = " %4.1f"; + } + + // lastly, establish the true runtime secure mode and delay time diff --git a/packaging/procps-3.2.7-top-manpage.patch b/packaging/procps-3.2.7-top-manpage.patch new file mode 100644 index 0000000..0f16bd9 --- /dev/null +++ b/packaging/procps-3.2.7-top-manpage.patch @@ -0,0 +1,24 @@ +diff -up procps-3.2.7/top.1.cpudesc procps-3.2.7/top.1 +--- procps-3.2.7/top.1.cpudesc 2008-01-18 08:04:19.000000000 +0100 ++++ procps-3.2.7/top.1 2008-01-18 08:15:49.000000000 +0100 +@@ -540,6 +540,20 @@ You\fI move\fR a field to the\fB left\fR + upper case\fR letter and to the\fB right\fR with the\fB lower case\fR + letter. + ++.\" ...................................................................... ++.SS 2c. SUMMARY Area Fields ++.\" ---------------------------------------------------------------------- ++The summary area fields describing CPU statistics are abbreviated. They provide ++information about times spent in: ++ \fR us = user mode ++ \fR sy = system mode ++ \fR ni = low priority user mode (nice) ++ \fR id = idle task ++ \fR wa = I/O waiting ++ \fR hi = servicing IRQs ++ \fR si = servicing soft IRQs ++ \fR st = steal (time given to other DomU instances) ++ + + .\" ---------------------------------------------------------------------- + .SH 3. INTERACTIVE Commands diff --git a/packaging/procps-3.2.7-top-remcpu.patch b/packaging/procps-3.2.7-top-remcpu.patch new file mode 100644 index 0000000..88d14ef --- /dev/null +++ b/packaging/procps-3.2.7-top-remcpu.patch @@ -0,0 +1,98 @@ +--- procps-3.2.7/top.c.remcpu 2006-07-10 10:41:11.000000000 +0200 ++++ procps-3.2.7/top.c 2006-07-10 10:41:35.000000000 +0200 +@@ -912,6 +912,7 @@ + static CPU_t *cpus_refresh (CPU_t *cpus) + { + static FILE *fp = NULL; ++ static int cpu_max; + int i; + int num; + // enough for a /proc/stat CPU line (not the intr line) +@@ -926,24 +927,29 @@ + can hold tics representing the /proc/stat cpu summary (the first + line read) -- that slot supports our View_CPUSUM toggle */ + cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t)); ++ cpu_max = Cpu_tot; + } ++ else if (cpu_max > Cpu_tot) ++ /* move saved CUPs summary to cpu_max possition */ ++ memcpy(&cpus[cpu_max], &cpus[Cpu_tot], sizeof(CPU_t)); ++ + rewind(fp); + fflush(fp); + + // first value the last slot with the cpu summary line + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); +- cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number +- cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number +- cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number ++ cpus[cpu_max].x = 0; // FIXME: can't tell by kernel version number ++ cpus[cpu_max].y = 0; // FIXME: can't tell by kernel version number ++ cpus[cpu_max].z = 0; // FIXME: can't tell by kernel version number + num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", +- &cpus[Cpu_tot].u, +- &cpus[Cpu_tot].n, +- &cpus[Cpu_tot].s, +- &cpus[Cpu_tot].i, +- &cpus[Cpu_tot].w, +- &cpus[Cpu_tot].x, +- &cpus[Cpu_tot].y, +- &cpus[Cpu_tot].z ++ &cpus[cpu_max].u, ++ &cpus[cpu_max].n, ++ &cpus[cpu_max].s, ++ &cpus[cpu_max].i, ++ &cpus[cpu_max].w, ++ &cpus[cpu_max].x, ++ &cpus[cpu_max].y, ++ &cpus[cpu_max].z + ); + if (num < 4) + std_err("failed /proc/stat read"); +@@ -955,7 +961,7 @@ + } + + // now value each separate cpu's tics +- for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) { ++ for (i = 0; ; i++) { + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); + cpus[i].x = 0; // FIXME: can't tell by kernel version number + cpus[i].y = 0; // FIXME: can't tell by kernel version number +@@ -964,9 +970,35 @@ + &cpus[i].id, + &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z + ); +- if (num < 4) +- std_err("failed /proc/stat read"); ++ if (num < 4) { ++ Cpu_tot = i; ++ break; ++ } ++ if (i == cpu_max - 1) { ++ // Bump cpu_max and extend cpus ++ cpu_max++; ++ cpus = realloc(cpus, (1 + cpu_max) * sizeof(CPU_t)); ++ if (!cpus) std_err("realloc failed"); ++ memcpy(&cpus[cpu_max], &cpus[cpu_max-1], sizeof(CPU_t)); ++ } ++ } ++ ++ if (cpu_max > Cpu_tot) ++ memcpy(&cpus[Cpu_tot], &cpus[cpu_max], sizeof(CPU_t)); ++ ++ // and just in case we're 2.2.xx compiled without SMP support... ++ if (Cpu_tot == 1) { ++ cpus[0].id = cpus[1].id = 0; ++ cpus[0].u = cpus[1].u; ++ cpus[0].n = cpus[1].n; ++ cpus[0].s = cpus[1].s; ++ cpus[0].i = cpus[1].i; ++ cpus[0].w = cpus[1].w; ++ cpus[0].x = cpus[1].x; ++ cpus[0].y = cpus[1].y; ++ cpus[0].z = cpus[1].z; + } ++ + return cpus; + } + diff --git a/packaging/procps-3.2.7-top-sorthigh.path b/packaging/procps-3.2.7-top-sorthigh.path new file mode 100644 index 0000000..e17df1e --- /dev/null +++ b/packaging/procps-3.2.7-top-sorthigh.path @@ -0,0 +1,37 @@ +--- procps-3.2.7/top.c.sorthigh 2007-02-05 09:06:34.000000000 +0100 ++++ procps-3.2.7/top.c 2007-02-05 09:09:35.000000000 +0100 +@@ -3121,6 +3121,7 @@ + unsigned w = Fieldstab[i].width; + + int advance = (x==0) && !Rc.mode_altscr; ++ f += advance; + + switch (i) { + case P_CMD: +@@ -3179,7 +3180,7 @@ + break; + case P_PRI: + if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) { +- f = " RT"; ++ f = advance ? " RT" : " RT"; + MKCOL(""); + } else + MKCOL((int)p->priority); +@@ -3227,7 +3228,7 @@ + break; + case P_WCH: + if (No_ksyms) { +- f = " %08lx "; ++ f = advance ? "%08lx " : " %08lx "; + MKCOL((long)p->wchan); + } else { + MKCOL(lookup_wchan(p->wchan, p->XXXID)); +@@ -3236,7 +3237,7 @@ + + } /* end: switch 'procflag' */ + +- rp = scat(rp, cbuf+advance); ++ rp = scat(rp, cbuf); + } /* end: for 'maxpflgs' */ + + PUFF( diff --git a/packaging/procps-3.2.7-vmstat-cpusteal.patch b/packaging/procps-3.2.7-vmstat-cpusteal.patch new file mode 100644 index 0000000..a20723f --- /dev/null +++ b/packaging/procps-3.2.7-vmstat-cpusteal.patch @@ -0,0 +1,52 @@ +--- procps-3.2.7/vmstat.c.steal 2006-07-10 10:49:49.000000000 +0200 ++++ procps-3.2.7/vmstat.c 2006-07-10 10:54:03.000000000 +0200 +@@ -150,15 +150,15 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_header(void){ +- printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n"); ++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------\n"); + printf( +- "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s\n", ++ "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n", + "r","b", + "swpd", "free", a_option?"inact":"buff", a_option?"active":"cache", + "si","so", + "bi","bo", + "in","cs", +- "us","sy","id","wa" ++ "us","sy","id","wa", "st" + ); + } + +@@ -173,7 +173,7 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_format(void) { +- const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n"; ++ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\n"; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i; + unsigned int hz = Hertz; +@@ -217,8 +217,8 @@ + (unsigned)( (100*duse + divo2) / Div ), + (unsigned)( (100*dsys + divo2) / Div ), + (unsigned)( (100*didl + divo2) / Div ), +- (unsigned)( (100*diow + divo2) / Div ) /* , +- (unsigned)( (100*dstl + divo2) / Div ) */ ++ (unsigned)( (100*diow + divo2) / Div ), ++ (unsigned)( (100*dstl + divo2) / Div ) + ); + + for(i=1;iname,((const vm_table_struct*)b)->name); + } +@@ -626,10 +629,10 @@ + unsigned long vm_nr_reverse_maps; // includes PageDirect + unsigned long vm_nr_mapped; // mapped into pagetables + unsigned long vm_nr_slab; // in slab +-unsigned long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line) +-unsigned long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line) +-unsigned long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line) +-unsigned long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line) ++unsigned long long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line) ++unsigned long long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line) ++unsigned long long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line) ++unsigned long long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line) + unsigned long vm_pgalloc; // page allocations + unsigned long vm_pgfree; // page freeings + unsigned long vm_pgactivate; // pages moved inactive -> active +@@ -668,54 +671,54 @@ + + void vminfo(void){ + char namebuf[16]; /* big enough to hold any row name */ +- vm_table_struct findme = { namebuf, NULL}; ++ vm_table_struct findme = { namebuf, NULL, 0}; + vm_table_struct *found; + char *head; + char *tail; + static const vm_table_struct vm_table[] = { +- {"allocstall", &vm_allocstall}, +- {"kswapd_inodesteal", &vm_kswapd_inodesteal}, +- {"kswapd_steal", &vm_kswapd_steal}, +- {"nr_dirty", &vm_nr_dirty}, // page version of meminfo Dirty +- {"nr_mapped", &vm_nr_mapped}, // page version of meminfo Mapped +- {"nr_page_table_pages", &vm_nr_page_table_pages},// same as meminfo PageTables +- {"nr_pagecache", &vm_nr_pagecache}, // gone in 2.5.66+ kernels +- {"nr_reverse_maps", &vm_nr_reverse_maps}, // page version of meminfo ReverseMaps GONE +- {"nr_slab", &vm_nr_slab}, // page version of meminfo Slab +- {"nr_unstable", &vm_nr_unstable}, +- {"nr_writeback", &vm_nr_writeback}, // page version of meminfo Writeback +- {"pageoutrun", &vm_pageoutrun}, +- {"pgactivate", &vm_pgactivate}, +- {"pgalloc", &vm_pgalloc}, // GONE (now separate dma,high,normal) +- {"pgalloc_dma", &vm_pgalloc_dma}, +- {"pgalloc_high", &vm_pgalloc_high}, +- {"pgalloc_normal", &vm_pgalloc_normal}, +- {"pgdeactivate", &vm_pgdeactivate}, +- {"pgfault", &vm_pgfault}, +- {"pgfree", &vm_pgfree}, +- {"pginodesteal", &vm_pginodesteal}, +- {"pgmajfault", &vm_pgmajfault}, +- {"pgpgin", &vm_pgpgin}, // important +- {"pgpgout", &vm_pgpgout}, // important +- {"pgrefill", &vm_pgrefill}, // GONE (now separate dma,high,normal) +- {"pgrefill_dma", &vm_pgrefill_dma}, +- {"pgrefill_high", &vm_pgrefill_high}, +- {"pgrefill_normal", &vm_pgrefill_normal}, +- {"pgrotated", &vm_pgrotated}, +- {"pgscan", &vm_pgscan}, // GONE (now separate direct,kswapd and dma,high,normal) +- {"pgscan_direct_dma", &vm_pgscan_direct_dma}, +- {"pgscan_direct_high", &vm_pgscan_direct_high}, +- {"pgscan_direct_normal",&vm_pgscan_direct_normal}, +- {"pgscan_kswapd_dma", &vm_pgscan_kswapd_dma}, +- {"pgscan_kswapd_high", &vm_pgscan_kswapd_high}, +- {"pgscan_kswapd_normal",&vm_pgscan_kswapd_normal}, +- {"pgsteal", &vm_pgsteal}, // GONE (now separate dma,high,normal) +- {"pgsteal_dma", &vm_pgsteal_dma}, +- {"pgsteal_high", &vm_pgsteal_high}, +- {"pgsteal_normal", &vm_pgsteal_normal}, +- {"pswpin", &vm_pswpin}, // important +- {"pswpout", &vm_pswpout}, // important +- {"slabs_scanned", &vm_slabs_scanned}, ++ VM_TABENTRY(allocstall), ++ VM_TABENTRY(kswapd_inodesteal), ++ VM_TABENTRY(kswapd_steal), ++ VM_TABENTRY(nr_dirty), ++ VM_TABENTRY(nr_mapped), ++ VM_TABENTRY(nr_page_table_pages), ++ VM_TABENTRY(nr_pagecache), ++ VM_TABENTRY(nr_reverse_maps), ++ VM_TABENTRY(nr_slab), ++ VM_TABENTRY(nr_unstable), ++ VM_TABENTRY(nr_writeback), ++ VM_TABENTRY(pageoutrun), ++ VM_TABENTRY(pgactivate), ++ VM_TABENTRY(pgalloc), ++ VM_TABENTRY(pgalloc_dma), ++ VM_TABENTRY(pgalloc_high), ++ VM_TABENTRY(pgalloc_normal), ++ VM_TABENTRY(pgdeactivate), ++ VM_TABENTRY(pgfault), ++ VM_TABENTRY(pgfree), ++ VM_TABENTRY(pginodesteal), ++ VM_TABENTRY(pgmajfault), ++ VM_TABENTRY(pgpgin), ++ VM_TABENTRY(pgpgout), ++ VM_TABENTRY(pgrefill), ++ VM_TABENTRY(pgrefill_dma), ++ VM_TABENTRY(pgrefill_high), ++ VM_TABENTRY(pgrefill_normal), ++ VM_TABENTRY(pgrotated), ++ VM_TABENTRY(pgscan), ++ VM_TABENTRY(pgscan_direct_dma), ++ VM_TABENTRY(pgscan_direct_high), ++ VM_TABENTRY(pgscan_direct_normal), ++ VM_TABENTRY(pgscan_kswapd_dma), ++ VM_TABENTRY(pgscan_kswapd_high), ++ VM_TABENTRY(pgscan_kswapd_normal), ++ VM_TABENTRY(pgsteal), ++ VM_TABENTRY(pgsteal_dma), ++ VM_TABENTRY(pgsteal_high), ++ VM_TABENTRY(pgsteal_normal), ++ VM_TABENTRY(pswpin), ++ VM_TABENTRY(pswpout), ++ VM_TABENTRY(slabs_scanned) + }; + const int vm_table_count = sizeof(vm_table)/sizeof(vm_table_struct); + +@@ -741,7 +744,10 @@ + ); + head = tail+1; + if(!found) goto nextline; +- *(found->slot) = strtoul(head,&tail,10); ++ if (found->sz == sizeof(unsigned long long)) ++ *((unsigned long long *) found->slot) = strtoull(head,&tail,10); ++ else ++ *((unsigned long *) found->slot) = strtoul(head,&tail,10); + nextline: + + //if(found) fprintf(stderr,"%s=%d\n",found->name,*(found->slot)); +--- procps-3.2.7/vmstat.c.kzak 2006-08-04 10:52:38.000000000 +0200 ++++ procps-3.2.7/vmstat.c 2006-08-04 12:05:49.000000000 +0200 +@@ -180,7 +180,7 @@ + unsigned int running,blocked,dummy_1,dummy_2; + jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2], cpu_xxx[2], cpu_yyy[2], cpu_zzz[2]; + jiff duse, dsys, didl, diow, dstl, Div, divo2; +- unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; ++ unsigned long long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; + unsigned int intr[2], ctxt[2]; + unsigned int sleep_half; + unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul; +@@ -509,7 +509,7 @@ + static void sum_format(void) { + unsigned int running, blocked, btime, processes; + jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz; +- unsigned long pgpgin, pgpgout, pswpin, pswpout; ++ unsigned long long pgpgin, pgpgout, pswpin, pswpout; + unsigned int intr, ctxt; + + meminfo(); +@@ -539,10 +539,10 @@ + printf("%13Lu IRQ cpu ticks\n", cpu_xxx); + printf("%13Lu softirq cpu ticks\n", cpu_yyy); + printf("%13Lu stolen cpu ticks\n", cpu_zzz); +- printf("%13lu pages paged in\n", pgpgin); +- printf("%13lu pages paged out\n", pgpgout); +- printf("%13lu pages swapped in\n", pswpin); +- printf("%13lu pages swapped out\n", pswpout); ++ printf("%13llu pages paged in\n", pgpgin); ++ printf("%13llu pages paged out\n", pgpgout); ++ printf("%13llu pages swapped in\n", pswpin); ++ printf("%13llu pages swapped out\n", pswpout); + printf("%13u interrupts\n", intr); + printf("%13u CPU context switches\n", ctxt); + printf("%13u boot time\n", btime); +@@ -554,7 +554,7 @@ + static void fork_format(void) { + unsigned int running, blocked, btime, processes; + jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz; +- unsigned long pgpgin, pgpgout, pswpin, pswpout; ++ unsigned long long pgpgin, pgpgout, pswpin, pswpout; + unsigned int intr, ctxt; + + getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl, diff --git a/packaging/procps-3.2.7-vmstat-timestamp-manpage.patch b/packaging/procps-3.2.7-vmstat-timestamp-manpage.patch new file mode 100644 index 0000000..37a1f48 --- /dev/null +++ b/packaging/procps-3.2.7-vmstat-timestamp-manpage.patch @@ -0,0 +1,22 @@ +diff -up procps-3.2.7/vmstat.8.timestamp procps-3.2.7/vmstat.8 +--- procps-3.2.7/vmstat.8.timestamp 2009-02-03 12:32:27.000000000 +0100 ++++ procps-3.2.7/vmstat.8 2009-02-03 12:34:37.000000000 +0100 +@@ -8,6 +8,7 @@ vmstat \- Report virtual memory statisti + .B vmstat + .RB [ "\-a" ] + .RB [ "\-n" ] ++.RB [ "\-t" ] + .RI [ delay " [ " count ]] + .br + .B vmstat +@@ -43,7 +44,9 @@ equivalent to the total number of tasks + is represented by one or more tasks, depending on thread usage. + This display does not repeat. + .PP +-The \fB-m\fP displays slabinfo. ++The \fB-t\fP switch adds timestamp to the output. ++.PP ++The \fB-m\fP switch displays slabinfo. + .PP + The \fB-n\fP switch causes the header to be displayed only once rather than periodically. + .PP diff --git a/packaging/procps-3.2.7-vmstat-timestamp.patch b/packaging/procps-3.2.7-vmstat-timestamp.patch new file mode 100644 index 0000000..d7ca068 --- /dev/null +++ b/packaging/procps-3.2.7-vmstat-timestamp.patch @@ -0,0 +1,117 @@ +--- procps-3.2.7/vmstat.c 2008-12-11 22:11:30.042532106 -0500 ++++ procps-3.2.7/vmstat.c.timestamp 2008-12-11 22:08:11.553529883 -0500 +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #include "proc/sysinfo.h" + #include "proc/version.h" +@@ -56,6 +57,7 @@ + + static unsigned int height; // window height + static unsigned int moreheaders=TRUE; ++static unsigned int showtimestamp=FALSE; + + + ///////////////////////////////////////////////////////////////////////// +@@ -71,6 +73,7 @@ + fprintf(stderr," -p prints disk partition statistics\n"); + fprintf(stderr," -s prints vm table\n"); + fprintf(stderr," -m prints slabinfo\n"); ++ fprintf(stderr," -t add timestamp to output\n"); + fprintf(stderr," -S unit size\n"); + fprintf(stderr," delay is the delay between updates in seconds. \n"); + fprintf(stderr," unit size k:1000 K:1024 m:1000000 M:1048576 (default is K)\n"); +@@ -150,7 +153,11 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_header(void){ +- printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n"); ++ if(!showtimestamp){ ++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n"); ++ } else { ++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------ ---timestamp---\n"); ++ } + printf( + "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n", + "r","b", +@@ -173,7 +180,7 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_format(void) { +- const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\n"; ++ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\t%s\n"; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i; + unsigned int hz = Hertz; +@@ -185,6 +192,10 @@ + unsigned int sleep_half; + unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul; + int debt = 0; // handle idle ticks running backwards ++ struct tm *tm_ptr; ++ time_t the_time; ++ char timebuf[32]; ++ timebuf[0] = '\0'; + + sleep_half=(sleep_time/2); + new_header(); +@@ -196,6 +207,13 @@ + &running,&blocked, + &dummy_1, &dummy_2); + ++ if (showtimestamp) ++ { ++ (void) time( &the_time ); ++ tm_ptr = localtime( &the_time ); ++ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S %Z", tm_ptr); ++ } ++ + duse= *cpu_use + *cpu_nic; + dsys= *cpu_sys + *cpu_xxx + *cpu_yyy; + didl= *cpu_idl; +@@ -218,7 +236,8 @@ + (unsigned)( (100*dsys + divo2) / Div ), + (unsigned)( (100*didl + divo2) / Div ), + (unsigned)( (100*diow + divo2) / Div ), +- (unsigned)( (100*dstl + divo2) / Div ) ++ (unsigned)( (100*dstl + divo2) / Div ), ++ timebuf + ); + + for(i=1;itgid == u->ut_pid)) { + *found_utpid = 1; +- best = tmp; ++ if (!best) ++ best = tmp; + } + if(tmp->tty != line) continue; + (*jcpu) += tmp->utime + tmp->stime; +- secondbest = tmp; + /* same time-logic here as for "best" below */ + if(! (secondbest && tmp->start_time <= secondbest->start_time) ){ + secondbest = tmp; diff --git a/packaging/procps-3.2.7-watch-unicode.patch b/packaging/procps-3.2.7-watch-unicode.patch new file mode 100644 index 0000000..1196b9c --- /dev/null +++ b/packaging/procps-3.2.7-watch-unicode.patch @@ -0,0 +1,148 @@ +diff -u procps-3.2.7/Makefile procps/Makefile +--- procps-3.2.7/Makefile 2007-01-16 17:24:49.000000000 +0100 ++++ procps/Makefile 2007-01-16 17:29:27.000000000 +0100 +@@ -67,7 +67,7 @@ + # plus the top-level Makefile to make it work stand-alone. + _TARFILES := Makefile + +-CURSES := -lncurses ++CURSES := -lncursesw + + # This seems about right for the dynamic library stuff. + # Something like this is probably needed to make the SE Linux +diff -u procps-3.2.7/watch.c procps/watch.c +--- procps-3.2.7/watch.c 2007-01-16 17:24:49.000000000 +0100 ++++ procps/watch.c 2007-01-16 18:06:57.000000000 +0100 +@@ -15,7 +15,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -28,6 +28,8 @@ + #include + #include + #include ++#include ++#include + + #ifdef FORCE_8BIT + #undef isprint +@@ -137,6 +139,27 @@ + } + } + ++static wint_t ++readwc(FILE *stream, mbstate_t *mbs) ++{ ++ for (;;) { ++ int chr; ++ char c; ++ wchar_t wc; ++ size_t len; ++ ++ chr = getc(stream); ++ if (chr == EOF) ++ return WEOF; ++ c = chr; ++ len = mbrtowc(&wc, &c, 1, mbs); ++ if (len == (size_t)-1) ++ memset(mbs, 0, sizeof(*mbs)); ++ else if (len != (size_t)-2) ++ return wc; ++ } ++} ++ + int + main(int argc, char *argv[]) + { +@@ -243,6 +266,7 @@ + FILE *p; + int x, y; + int oldeolseen = 1; ++ mbstate_t mbs; + + if (screen_size_changed) { + get_terminal_size(); +@@ -276,49 +300,63 @@ + do_exit(2); + } + ++ memset(&mbs, 0, sizeof(mbs)); + for (y = show_title; y < height; y++) { + int eolseen = 0, tabpending = 0; + for (x = 0; x < width; x++) { +- int c = ' '; +- int attr = 0; ++ wint_t c = L' '; ++ int attr = 0, c_width; ++ cchar_t cc; ++ wchar_t wstr[2]; + + if (!eolseen) { + /* if there is a tab pending, just spit spaces until the + next stop instead of reading characters */ + if (!tabpending) + do +- c = getc(p); +- while (c != EOF && !isprint(c) +- && c != '\n' +- && c != '\t'); +- if (c == '\n') ++ c = readwc(p, &mbs); ++ while (c != WEOF && !iswprint(c) ++ && c != L'\n' ++ && c != L'\t'); ++ if (c == L'\n') + if (!oldeolseen && x == 0) { + x = -1; + continue; + } else + eolseen = 1; +- else if (c == '\t') ++ else if (c == L'\t') + tabpending = 1; +- if (c == EOF || c == '\n' || c == '\t') +- c = ' '; ++ if (c == WEOF || c == L'\n' || c == L'\t') ++ c = L' '; + if (tabpending && (((x + 1) % 8) == 0)) + tabpending = 0; + } ++ wstr[0] = c; ++ wstr[1] = 0; ++ setcchar (&cc, wstr, 0, 0, NULL); + move(y, x); + if (option_differences) { +- chtype oldch = inch(); +- char oldc = oldch & A_CHARTEXT; ++ cchar_t oldc; ++ wchar_t oldwstr[2]; ++ attr_t attrs; ++ short colors; ++ ++ in_wch(&oldc); ++ getcchar(&oldc, oldwstr, &attrs, &colors, NULL); + attr = !first_screen +- && ((char)c != oldc ++ && (wstr[0] != oldwstr[0] + || + (option_differences_cumulative +- && (oldch & A_ATTRIBUTES))); ++ && attrs)); + } + if (attr) + standout(); +- addch(c); ++ add_wch(&cc); + if (attr) + standend(); ++ c_width = wcwidth(c); ++ if (c_width > 1) ++ x += c_width - 1; + } + oldeolseen = eolseen; + } diff --git a/packaging/procps-enable-core.patch b/packaging/procps-enable-core.patch new file mode 100644 index 0000000..b7310c1 --- /dev/null +++ b/packaging/procps-enable-core.patch @@ -0,0 +1,11 @@ +--- procps-3.2.7/ps/display.c.enable-core 2009-02-27 11:57:21.000000000 +0000 ++++ procps-3.2.7/ps/display.c 2009-02-27 11:57:48.000000000 +0000 +@@ -562,6 +562,8 @@ + default: + sigaction(i,&sa,NULL); + case 0: ++ case SIGABRT: ++ case SIGSEGV: + case SIGINT: /* ^C */ + case SIGTSTP: /* ^Z */ + case SIGTTOU: /* see stty(1) man page */ diff --git a/packaging/procps.changes b/packaging/procps.changes new file mode 100644 index 0000000..fcd27ed --- /dev/null +++ b/packaging/procps.changes @@ -0,0 +1,474 @@ +* Wed Jul 28 2010 Ameya Palande 3.2.8 +- Fix for "Unknown HZ value! (??) Assume 100." problem + +* Mon Dec 14 2009 Yi Yang 3.2.7 +- Fix Makefile error when LD_AS_NEEDED is set + +* Mon Dec 14 2009 Yi Yang 3.2.7 +- Remove "unset LD_AS_NEEDED" + +* Thu May 7 2009 Arjan van de Ven 3.2.7 +- not ready for LD_AS_NEEDED yet + +* Wed Jul 23 2008 Zhang Xin +- Mark man file as %doc + +* Tue Feb 12 2008 Tomas Smetana 3.2.7-20 +- rebuild (gcc-4.3) + +* Thu Jan 24 2008 Tomas Smetana 3.2.7-19.2 +- install slabtop again: kernel was fixed + +* Fri Jan 18 2008 Tomas Smetana 3.2.7-19.1 +- rebuild because of errors on x86_64 + +* Fri Jan 18 2008 Tomas Smetana 3.2.7-19 +- fix #296471 - update top man page +- fix #226319 - merge review + +* Fri Jan 11 2008 Tomas Smetana 3.2.7-18 +- fix displaying the CPU column as integer (related #354001) +- don't install slabtop -- there's no /proc/slabinfo + +* Wed Sep 12 2007 Tomas Smetana 3.2.7-17 +- fix #185994 - top "Cpu0" line never updates when using + "Single Cpu = Off" option on single processor machine + +* Mon Aug 27 2007 Tomas Smetana 3.2.7-16.1 +- bump release + +* Mon Aug 27 2007 Tomas Smetana 3.2.7-16 +- fix #255441 - ps requires libselinux-devel to display security contexts + +* Thu Aug 23 2007 Tomas Smetana 3.2.7-15.1 +- rebuild + +* Mon Aug 20 2007 Tomas Smetana 3.2.7-15 +- fix #244960 - ps manpage formatted incorrectly +- update license tag + +* Mon Jun 18 2007 Tomas Smetana 3.2.7-14 +- fix #244152 ps truncates eip and esp to 32-bit values on 64-bit systems + +* Tue May 22 2007 Tomas Smetana 3.2.7-13 +- fix #208217 - ps does not accept '+' in sort specifier + +* Wed Apr 25 2007 Tomas Smetana 3.2.7-12 +- fix #183029 - watch ignores multibyte characters +- fix #222251 - STIME column can jitter +- fix array overflow in sysctl + +* Tue Apr 3 2007 Karel Zak 3.2.7-11 +- fix #234546 - 'w' doesn't give correct information about what's being run. +- fix #228870 - process `sysctl' is using deprecated sysctl +- cleanup spec file + +* Mon Feb 5 2007 Karel Zak 3.2.7-10 +- fix #212637 - sysctl using deprecated syscall +- fix #140975 - top corrupts screen when sorting on first column + +* Tue Jan 30 2007 Karel Zak 3.2.7-9 +- fix procps_version in FAQ patch (thanks to Ian Kent) + +* Wed Sep 27 2006 Karel Zak 3.2.7-8 +- remove zombie patch (needs more investigation) +- fix #208100 - top command with '-c' option become not to display list of tasks +- fix #199174 - top returns with exit code 1 even if no error occurs + +* Tue Sep 19 2006 Karel Zak 3.2.7-7 +- fix #206551 - top fails to convert to cpu single mode when hit '1' + +* Tue Sep 5 2006 Karel Zak 3.2.7-6 +- fix minor bug in procps-3.2.6-top-env-cpuloop.patch + +* Fri Aug 7 2006 Karel Zak - 3.2.7-5 +- fix #189349 - 32bit vmstat on 64bit kernel + +* Thu Aug 3 2006 Karel Zak - 3.2.7-4 +- fix #139827 - ps(1) outputs a multi-threads process as a defunct process. + +* Wed Jul 19 2006 Karel Zak - 3.2.7-3 +- spec file cleanup + +* Wed Jul 12 2006 Jesse Keating - 3.2.7-2.1 +- rebuild + +* Mon Jul 10 2006 Karel Zak 3.2.7-2 +- fix #134516 - ps ignores /proc/#/cmdline if contents 2047 bytes + +* Mon Jul 10 2006 Karel Zak 3.2.7-1 +- upgrade to 3.2.7 (and sync patches) + +* Thu Jun 1 2006 Karel Zak 3.2.6-4 +- fix #191493 - watch -n doesn't handle large integers properly +- fix #186017 - top "Cpu0" line never updates on single processor machine + (bugfix added to the 'remcpu' patch) +- fix #168444 - memory usage conflicts with /proc/meminfo +- fix #174619 - top reports wrong values for CPU(s) in batch mode +- fix #185299 - cpu steal time support + +* Fri Feb 10 2006 Jesse Keating - 3.2.6-3.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 3.2.6-3.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Tue Dec 13 2005 Dan Walsh 3.2.6-3 +- Translate context + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Tue Nov 8 2005 Karel Zak 3.2.6-2 +- fix #157725 - sysctl -A returns an error + +* Mon Oct 31 2005 Karel Zak 3.2.6-1 +- update to new upstream release + +* Wed Oct 12 2005 Karel Zak 3.2.5-8 +- fix #170083 - Top showing bad cpu usage numbers + +* Tue Sep 6 2005 Karel Zak 3.2.5-7 +- improve procps-3.2.5-sysctl-writeonly.patch +- fix #161449 - "top" ignores user and system toprc +- fix #161559 - top segfaults when resizing console +- fix #160796 - vmstat crashes when accessing LVM partition +- fix #161303 - 'top' failed when remove cpus + +* Tue May 10 2005 Karel Zak 3.2.5-6 +- fix permissions in the spec install section + +* Tue May 10 2005 Karel Zak 3.2.5-5 +- fix debuginfo + +* Tue Apr 26 2005 Karel Zak 3.2.5-4 +- fix #144459 - sysctl reports error: unknown error <...> reading key '' + (now sysctl doesn't read data from write-only /proc/sys files) + +* Thu Mar 17 2005 Karel Zak 3.2.5-3 +- fix top crashes when terminal window is resized (#149319) + +* Sat Mar 5 2005 Karel Zak 3.2.5-2 +- rebuilt + +* Tue Feb 1 2005 Karel Zak 3.2.5-1 +- update to new upstream 3.2.5 +- recreate selinux patch +- remove errno, slabinfo21 and fullpath patches -- all fixed by upstream + +* Mon Jan 25 2005 Karel Zak 3.2.4-4 +- pmap truncates filenames of mappings (#142751) + +* Mon Jan 24 2005 Karel Zak 3.2.4-3 +- add support for /proc/slabinfo 2.1 (#145369) + +* Fri Jan 7 2005 Karel Zak 3.2.4-2 +- fix sysctl errno usage (#144459) + +* Wed Dec 1 2004 Karel Zak 3.2.4-1 +- update to new upstream release + +* Mon Nov 1 2004 Karel Zak 3.2.3-6 +- update FAQ +- update spec description +- fix text in .noproc patch +- fix segv fault if cpu number exceeding 38 (#137159) + +* Tue Sep 28 2004 Dan Walsh 3.2.3-5 +- Fix terminal handling when /proc is not available. +- Patch provided by Karel Zak + +* Thu Sep 16 2004 Dan Walsh 3.2.3-4 +- Fix spec file to use makefile + +* Mon Aug 30 2004 Dan Walsh 3.2.3-3 +- Fix batch mode to use dumb terminal + +* Tue Aug 17 2004 Florian La Roche +- fix building as non-root, patch from Steve G + +* Tue Aug 10 2004 Dan Walsh 3.2.3-1 +- Latest from Upstream + +* Tue Jul 20 2004 Dan Walsh 3.2.2-2 +- Reformat ps man page + +* Mon Jul 19 2004 Dan Walsh 3.2.2-1 +- Update to upstream version. + +* Sat Jun 26 2004 Dan Walsh 3.2.1-7 +- Add patch to display vm_size when STATSIZE env set + +* Tue Jun 15 2004 Alan Cox 3.2.1-6 +- Removed broken SELinux patch to w +- Added a better alternative whereby we get less data but don't fall for + vanished processes when SELinux is running + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Mon Jun 14 2004 Dan Walsh 3.2.1-5 +- Fix FAQ Line + +* Mon Apr 09 2004 Colin Walters 3.2.1-4 +- Add little patch to make w/who work when getattr access + to /proc/ for the user's login process is denied + +* Sun Mar 28 2004 Dan Walsh 3.2.1-3 +- bump for rhel3 + +* Sun Mar 28 2004 Dan Walsh 3.2.1-2 +- Removed addtask patch, very buggy, +- Added FAQ to docdir + +* Sun Mar 28 2004 Dan Walsh 3.2.1-1 +- Update to latest from upstream + +* Thu Mar 25 2004 Dan Walsh 3.2.0-3 +- Add addtask patch to total all threads times. + +* Wed Mar 17 2004 Dan Walsh +- Clean up spec file. + +* Tue Mar 02 2004 Elliot Lee +- rebuilt + +* Tue Feb 24 2004 Dan Walsh 3.2.0-1 +- New version from upstream + +* Fri Feb 13 2004 Elliot Lee +- rebuilt + +* Thu Jan 22 2004 Dan Walsh 3.1.15-3 +- Match -Z to --context + +* Wed Jan 21 2004 Dan Walsh 3.1.15-2 +- Add back in -Z support + +* Wed Jan 21 2004 Alexander Larsson 3.1.15-1 +- upgrade to procps3 +- Some regressions, see bug #114012 + +* Tue Jan 20 2004 Dan Walsh 2.0.17-7 +- Remove LIBCURSES from skill and sysctl + +* Wed Dec 10 2003 Dan Walsh 2.0.17-6 +- Turn on SELinux + +* Mon Dec 8 2003 Alexander Larsson 2.0.17-5 +- Fix top total percentages (#109484) + +* Wed Oct 15 2003 Dan Walsh 2.0.17-4 +- Turn off selinux + +* Wed Oct 15 2003 Dan Walsh 2.0.17-3.sel +- Fix help message + +* Thu Oct 9 2003 Dan Walsh 2.0.17-2.sel +- Turn on selinux + +* Fri Oct 3 2003 Alexander Larsson 2.0.17-1 +- Update to 2.0.17, drop upstream patches, forward port remaining patches + +* Fri Sep 5 2003 Dan Walsh 2.0.13-11 +- Turn off selinux + +* Thu Aug 28 2003 Dan Walsh 2.0.13-10.sel +- Add -Z switch for SELinux + +* Sun Aug 17 2003 Doug Ledford 2.0.13-9E +- Add patch to recognize irq and softirq time accounting in kernels that + support this feature + +* Mon Aug 11 2003 Alexander Larsson 2.0.13-8 +- rebuild + +* Mon Aug 11 2003 Alexander Larsson 2.0.13-7E +- Add swapped patch from rik van riel + +* Wed Aug 6 2003 Alexander Larsson 2.0.13-6 +- rebuild + +* Wed Aug 6 2003 Alexander Larsson 2.0.13-5E +- Update iowait patch (#101657) +- Add wchan 64bit patch from Mark DeWandel + +* Mon Jul 28 2003 Dan Walsh 2.0.13-4E +- Add SELinux patch + +* Wed Jul 16 2003 Matt Wilson 2.0.13-3E +- display iowait with procps-2.0.13-iowait.patch (#99061) + +* Fri Jul 11 2003 Alexander Larsson 2.0.13-2E +- Disable linuxthreads thread hack + +* Mon Jul 7 2003 Alexander Larsson 2.0.13-1E +- rebuild + +* Fri Jul 4 2003 Alexander Larsson 2.0.13-1 +- update to 2.0.13 +- Re-merged ntpl patch +- Add hertz fix from Ernie Petrides + +* Wed Jun 04 2003 Elliot Lee +- rebuilt + +* Fri May 23 2003 Alexander Larsson 2.0.12-1 +- Update to 2.0.12 +- Add patch to fix segfault on ps axl (#91453) + +* Fri Mar 14 2003 Alexander Larsson 2.0.11-7 +- Add patch that fixes negative priorities in top. + +* Thu Feb 20 2003 Alexander Larsson 2.0.11-6 +- New NPTL patch: +- Added skipthreads optimization to top +- Don't read threads in 'w' + +* Thu Feb 20 2003 Alexander Larsson 2.0.11-5 +- Update the NPTL patch since the kernel /proc was fixed +- For kernels >= 2.4.20-2.50 + +* Mon Feb 17 2003 Alexander Larsson 2.0.11-4 +- Update nptl patch to new /proc layout. + +* Wed Jan 22 2003 Tim Powers 2.0.11-3 +- rebuilt + +* Wed Jan 22 2003 Alexander Larsson 2.0.11-2 +- Created nptl patch after discussion with ingo and arjan + +* Tue Jan 21 2003 Alexander Larsson 2.0.11-1 +- Update to 2.0.11 + +* Mon Dec 16 2002 Elliot Lee 2.0.10-4 +- Fix %%install in changelog + +* Tue Nov 19 2002 Jakub Jelinek 2.0.10-3 +- Fix for Hammer + +* Wed Oct 23 2002 Alexander Larsson 2.0.10-2 +- Remove uninstalled files in %%install. Add pmap to %%files + +* Tue Oct 8 2002 Alexander Larsson 2.0.10-1 +- Update to 2.0.10 +- Removed applied patches. + +* Mon Aug 12 2002 Alexander Larsson 2.0.7-25 +- Add patch to protect against idle ticks going backwards. Fixes #71237 + +* Thu Aug 8 2002 Alexander Larsson 2.0.7-24 +- Fix saving of sort, fixes #32757 +- Fix printing size, fixes #48224 +- Fix float decimal point input #58163 + +* Thu Aug 8 2002 Alexander Larsson 2.0.7-23 +- Fix unsigned/signed bug. Closes #60998. +- Update threadbadhack to correctly propagate process time to the main thread. + +* Wed Aug 7 2002 Alexander Larsson 2.0.7-22 +- Don't strip binaries + +* Fri Jul 12 2002 Alexander Larsson 2.0.7-21 +- Remove the X11 subpackage + +* Mon Jul 1 2002 Alexander Larsson 2.0.7-19 +- Added patch that fixes #35174 + +* Wed Jun 26 2002 Alexander Larsson 2.0.7-18 +- New thread badhack patch. Fixes a segfault. + +* Mon Jun 24 2002 Alexander Larsson 2.0.7-16 +- New thread badhack. Now enabled by default. + +* Fri Jun 21 2002 Tim Powers +- automated rebuild + +* Thu Jun 20 2002 Alexander Larsson 2.0.7-14 +- Added badhack to support hiding threads + +* Thu May 23 2002 Tim Powers +- automated rebuild + +* Mon Apr 15 2002 Bill Nottingham 2.0.7-12 +- add ldconfig in postun section + +* Mon Aug 27 2001 Trond Eivind Glomsrod 2.0.7-11 +- Add ncurses-devel as a build dependency (#49562) + +* Sat Jul 21 2001 Tim Powers +- removed applnk entry, one of the things that's cluttering our menus + +* Sun Jun 24 2001 Elliot Lee +- Bump release + rebuild. + +* Thu Apr 5 2001 Jakub Jelinek +- fix AIX style user defined formats (#34833) + +* Thu Mar 22 2001 Bill Nottingham +- add a '-e' to sysctl to ignore errors (#31852) + +* Mon Mar 5 2001 Preston Brown +- bigger buffer for reading /proc/stat fixes segfault (#27840) + +* Thu Feb 1 2001 Preston Brown +- make sysctl return a value when errors occur (#18820). +- support big UIDs (#22683) + +* Mon Jan 22 2001 Helge Deller +- work-around for negative CPU output (Bug #18380) + +* Thu Aug 17 2000 Than Ngo +- fix failing in RPM post script (Bug #16226) + +* Wed Jul 26 2000 Michael K. Johnson +- Added Jakub's locale patch + +* Fri Jul 14 2000 Michael K. Johnson +- procps-2.0.7 +- integrated all patches except for signames patch, which is broken + and unnecessary +- See NEWS for changes between 2.0.6 and 2.0.7 +- Added patch to correctly install desktop file. Oops. + +* Wed Jul 12 2000 Prospector +- automatic rebuild + +* Tue Jul 03 2000 Preston Brown +- larger buffers for reading /proc/stat + +* Tue Jun 13 2000 Preston Brown +- FHS paths + +* Tue May 30 2000 Preston Brown +- add smp, signal name patches from VA Linux. Thanks guys. + +* Mon May 22 2000 Harald Hoyer +- added sysctl.conf (5) man page + +* Wed May 10 2000 Bill Nottingham +- fix PAGE_SIZE mismatch on ia64 + +* Sun May 7 2000 Bill Nottingham +- rebuild with different optimizations for ia64 + +* Fri Mar 24 2000 Bernhard Rosenkraenzer +- rebuild with current ncurses + +* Tue Mar 7 2000 Bill Nottingham +- fix end-of-file behavior in sysctl + +* Mon Feb 07 2000 Preston Brown +- wmconfig -> desktop + +* Mon Feb 7 2000 Jakub Jelinek +- don't try to load System.map (and spit error messages if it does not + exist) if ps or top are not going to use it, both to speed things up + and remove the ugly messages when they don't make sense. +- in top, print the possible error messages using standard top SHOWMESSAGE + (because it will be now printed out when already in terminal mode). + +* Thu Feb 3 2000 Matt Wilson +- added patch to prevent divide by zero on UltraSparc +- gzip man pages + diff --git a/packaging/procps.spec b/packaging/procps.spec new file mode 100644 index 0000000..ca2920e --- /dev/null +++ b/packaging/procps.spec @@ -0,0 +1,188 @@ +#specfile originally created for Fedora, modified for Moblin Linux +Summary: System and process monitoring utilities +Name: procps +Version: 3.2.8 +Release: 1 +License: GPLv2+ and LGPLv2+ +Group: Applications/System +URL: http://procps.sourceforge.net +Source: http://procps.sourceforge.net/procps-%{version}.tar.gz +Source1: FAQ +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +Patch1: procps-3.2.7-selinux.patch +Patch2: procps-3.1.15-misc.patch +Patch3: procps-3.2.3-FAQ.patch +Patch4: procps-3.2.1-selinux-workaround.patch +Patch6: procps-3.2.3-noproc.patch +Patch7: procps-3.2.3-pseudo.patch +Patch8: procps-3.2.4-0x9b.patch +# 157725 - sysctl -A returns an error +Patch9: procps-3.2.5-sysctl-writeonly.patch +# 161449 - "top" ignores user and system toprc +Patch10: procps-3.2.5-top-rc.patch +# 161303 - 'top' failed when remove cpus +# 186017 - Top "Cpu0" line never updates on single processor machine +Patch11: procps-3.2.7-top-remcpu.patch +# Selinux +Patch12: procps-3.2.6-libselinux.patch +# 177453 - for VIRT use proc->vm_size rather then proc->size (workaround) +Patch13: procps-3.2.6-top-env-vmsize.patch +# 174619 - workaround for reliable Cpu(s) data in the first loop +Patch14: procps-3.2.6-top-env-cpuloop.patch +# 185299 - cpu steal time support +Patch15: procps-3.2.7-vmstat-cpusteal.patch +# 134516 - ps ignores /proc/#/cmdline if contents 2047 bytes +Patch16: procps-3.2.7-longcmd.patch +# 189349 - 32bit vmstat on 64bit kernel +Patch17: procps-3.2.7-vmstat-pgpg.patch +# 212637 - sysctl using deprecated syscall +# 228870 - process `sysctl' is using deprecated sysctl ... +Patch18: procps-3.2.7-sysctl-ignore.patch +# 140975 - top corrupts screen when sorting on first column +Patch19: procps-3.2.7-top-sorthigh.path +# 234546 - 'w' doesn't give correct information about what's being run. +Patch20: procps-3.2.7-w-best.patch +# 183029 - watch ignores unicode characters +#Patch21: procps-3.2.7-watch-unicode.patch +# 222251 - STIME can jitter by one second +Patch22: procps-3.2.7-ps-stime.patch +#244152 - ps truncates eip and esp to 32-bit values on 64-bit systems +Patch23: procps-3.2.7-ps-eip64.patch +#244960 - ps manpage formatted incorrectly +Patch24: procps-3.2.7-psman.patch +#255441 - ldopen libselinux.so.1 instead of libselinux.so +Patch25: procps-3.2.7-ps-libselinux.patch +#185994 - error when using "Single Cpu = Off" option +Patch26: procps-3.2.7-top-cpu0.patch +#354001 - CPU value in top is reported as an integer +Patch27: procps-3.2.7-top-cpuint.patch +#296471 - update top man page +Patch28: procps-3.2.7-top-manpage.patch +#440694 - strange text after selecting few field +Patch30: procps-3.2.7-top-clrscr.patch +#435453 - errors with man -t formatting of ps man page +Patch31: procps-3.2.7-ps-man-fmt.patch +#472783 - 'vmstat -p ', +# the detailed statistics of the partition name is not output. +Patch32: procps-3.2.7-vmstat-partstats-long.patch +# Fix vmstat header to be 80 chars not 81 +Patch33: procps-3.2.7-vmstat-header.patch +# rhel bug #475963: slabtop -o should display the info once +Patch34: procps-3.2.7-slabtop-once.patch +#476134 - added timestamp to vmstat with new option -t +Patch35: procps-3.2.7-vmstat-timestamp.patch +#manual page updated to document the -t functionality +Patch36: procps-3.2.7-vmstat-timestamp-manpage.patch +# 'requested writes' display in partition statistics +Patch37: procps-3.2.7-vmstat-partstats-reqwrites.patch +# '-l' option of 'free' documented +Patch38: procps-3.2.7-free-hlmem.patch +# enable core dump generation +Patch39: procps-enable-core.patch +# fix "Unknown HZ value! (??) Assume 100." issue +Patch40: unknown-hz-value-fix.patch + +BuildRequires: ncurses-devel + +%description +The procps package contains a set of system utilities that provide +system information. Procps includes ps, free, skill, pkill, pgrep, +snice, tload, top, uptime, vmstat, w, watch and pdwx. The ps command +displays a snapshot of running processes. The top command provides +a repetitive update of the statuses of running processes. The free +command displays the amounts of free and used memory on your +system. The skill command sends a terminate command (or another +specified signal) to a specified set of processes. The snice +command is used to change the scheduling priority of specified +processes. The tload command prints a graph of the current system +load average to a specified tty. The uptime command displays the +current time, how long the system has been running, how many users +are logged on, and system load averages for the past one, five, +and fifteen minutes. The w command displays a list of the users +who are currently logged on and what they are running. The watch +program watches a running program. The vmstat command displays +virtual memory statistics about processes, memory, paging, block +I/O, traps, and CPU activity. The pwdx command reports the current +working directory of a process or processes. + +%prep +%setup -q + +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +#%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 +%patch38 -p1 +%patch39 -p1 +%patch40 -p1 + +cp %SOURCE1 . + +%build +make SHARED=1 CFLAGS="$RPM_OPT_FLAGS" W_SHOWFROM=-DW_SHOWFROM lib64=%{_lib} + +%install +rm -rf %{buildroot} +make ldconfig=true DESTDIR=%{buildroot} lib64=%{_lib} install="install -D" \ + SKIP="/bin/kill /usr/share/man/man1/kill.1" install +mkdir -p %{buildroot}/%{_docdir}/procps-%{version} +# keep 'rpm' happy... +chmod -R u+w %{buildroot}/sbin +chmod -R u+w %{buildroot}/bin +chmod -R u+w %{buildroot}/usr/bin +chmod -R u+w %{buildroot}/lib* + +%clean +rm -rf %{buildroot} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%defattr(0644,root,root,755) +%doc NEWS BUGS TODO FAQ +%attr(755,root,root) /%{_lib}/* +%attr(755,root,root) /bin/ps +%attr(755,root,root) /sbin/sysctl +%attr(755,root,root) /usr/bin/* + +%doc %attr(0644,root,root) %{_mandir}/man1/* +%doc %attr(0644,root,root) %{_mandir}/man8/* +%doc %attr(0644,root,root) %{_mandir}/man5/* + diff --git a/packaging/unknown-hz-value-fix.patch b/packaging/unknown-hz-value-fix.patch new file mode 100644 index 0000000..63f1f89 --- /dev/null +++ b/packaging/unknown-hz-value-fix.patch @@ -0,0 +1,63 @@ +From 8e8a824533643de4a4a221358a8c5ae7d4e9e474 Mon Sep 17 00:00:00 2001 +From: Ameya Palande +Date: Wed, 28 Jul 2010 14:41:24 +0300 +Subject: [PATCH] unknown hz value fix + +Fixes linux version detection. This relied on a side-effect of the elf loader +and previously hoped that constructors would be called in a certain order. +That behavior was undefined and hence unreliable. For newer kernels, an elf +note is now checked to extract HZ tick count. + +Original author: +Backported to meego by: Ameya Palande +--- + proc/sysinfo.c | 1 + + proc/version.c | 5 ++--- + proc/version.h | 1 + + 3 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/proc/sysinfo.c b/proc/sysinfo.c +index 0ad7fba..793829f 100644 +--- a/proc/sysinfo.c ++++ b/proc/sysinfo.c +@@ -212,6 +212,7 @@ static int check_for_privs(void){ + static void init_libproc(void) __attribute__((constructor)); + static void init_libproc(void){ + have_privs = check_for_privs(); ++ init_Linux_version(); // make sure we have version before continuing... + // ought to count CPUs in /proc/stat instead of relying + // on glibc, which foolishly tries to parse /proc/cpuinfo + // +diff --git a/proc/version.c b/proc/version.c +index 391b9bb..2049e15 100644 +--- a/proc/version.c ++++ b/proc/version.c +@@ -35,11 +35,10 @@ void display_version(void) { + + int linux_version_code; + +-static void init_Linux_version(void) __attribute__((constructor)); +-static void init_Linux_version(void) { ++void init_Linux_version(void) { + static struct utsname uts; + int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */ +- ++ + if (uname(&uts) == -1) /* failure implies impending death */ + exit(1); + if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3) +diff --git a/proc/version.h b/proc/version.h +index 492d251..28c9b49 100644 +--- a/proc/version.h ++++ b/proc/version.h +@@ -14,6 +14,7 @@ + + EXTERN_C_BEGIN + ++extern void init_Linux_version(void); /* initialize linux version */ + extern void display_version(void); /* display suite version */ + extern const char procps_version[]; /* global buf for suite version */ + extern const char procps_number_version[]; /* global buf for suite number version */ +-- +1.7.0.4 + diff --git a/pgrep.1 b/pgrep.1 new file mode 100644 index 0000000..ae2edc2 --- /dev/null +++ b/pgrep.1 @@ -0,0 +1,162 @@ +.\" Manual page for pgrep / pkill. +.\" Licensed under version 2 of the GNU General Public License. +.\" Copyright 2000 Kjetil Torgrim Homme +.\" +.TH PGREP 1 "June 25, 2000" "Linux" "Linux User's Manual" +.SH NAME +pgrep, pkill \- look up or signal processes based on name and other attributes + +.SH SYNOPSIS +pgrep [\-flvx] [\-d \fIdelimiter\fP] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...] +.br + [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...] +.br + [\-t \fIterm\fP,...] [\fIpattern\fP] + +pkill [\-\fIsignal\fP] [\-fvx] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...] +.br + [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...] +.br + [\-t \fIterm\fP,...] [\fIpattern\fP] + +.SH DESCRIPTION +\fBpgrep\fP looks through the currently running processes and lists the +process IDs which matches the selection criteria to stdout. All +the criteria have to match. For example, + +pgrep -u root sshd + +will only list the processes called \fBsshd\fP AND owned by \fBroot\fP. +On the other hand, + +pgrep -u root,daemon + +will list the processes owned by \fBroot\fP OR \fBdaemon\fP. + +\fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP) +to each process instead of listing them on stdout. + +.SH OPTIONS +.TP +\-d \fIdelimiter\fP +Sets the string used to delimit each process ID in the output (by +default a newline). (\fBpgrep\fP only.) +.TP +\-f +The \fIpattern\fP is normally only matched against the process name. +When \-f is set, the full command line is used. +.TP +\-g \fIpgrp\fP,... +Only match processes in the process group IDs listed. Process group 0 +is translated into \fBpgrep\fP's or \fBpkill\fP's own process group. +.TP +\-G \fIgid\fP,... +Only match processes whose real group ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-l +List the process name as well as the process ID. (\fBpgrep\fP only.) +.TP +\-n +Select only the newest (most recently started) of the matching +processes. +.TP +\-o +Select only the oldest (least recently started) of the matching +processes. +.TP +\-P \fIppid\fP,... +Only match processes whose parent process ID is listed. +.TP +\-s \fIsid\fP,... +Only match processes whose process session ID is listed. Session ID 0 +is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID. +.TP +\-t \fIterm\fP,... +Only match processes whose controlling terminal is listed. The +terminal name should be specified without the "/dev/" prefix. +.TP +\-u \fIeuid\fP,... +Only match processes whose effective user ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-U \fIuid\fP,... +Only match processes whose real user ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-v +Negates the matching. +.TP +\-x +Only match processes whose name (or command line if \-f is specified) +\fBexactly\fP match the \fIpattern\fP. +.TP +\-\fIsignal\fP +Defines the signal to send to each matched process. Either the +numeric or the symbolic signal name can be used. (\fBpkill\fP only.) + +.SH OPERANDS +.TP +\fIpattern\fP +Specifies an Extended Regular Expression for matching against the +process names or command lines. + +.SH EXAMPLES +Example 1: Find the process ID of the \fBnamed\fP daemon: + +unix$ pgrep \-u root named + +Example 2: Make \fBsyslog\fP reread its configuration file: + +unix$ pkill \-HUP syslogd + +Example 3: Give detailed information on all \fBxterm\fP processes: + +unix$ ps \-fp $(pgrep \-d, \-x xterm) + +Example 4: Make all \fBnetscape\fP processes run nicer: + +unix$ renice +4 `pgrep netscape` + +.SH "EXIT STATUS" +.TP +.I "0" +One or more processes matched the criteria. +.TP +.I "1" +No processes matched. +.TP +.I "2" +Syntax error in the command line. +.TP +.I "3" +Fatal error: out of memory etc. + +.SH NOTES +The process name used for matching is limited to the 15 characters +present in the output of /proc/\fIpid\fP/stat. Use the \-f option to +match against the complete command line, /proc/\fIpid\fP/cmdline. + +The running \fBpgrep\fP or \fBpkill\fP process will never report +itself as a match. + +.SH BUGS +The options \-n and \-o and \-v can not be combined. Let me know if +you need to do this. + +Defunct processes are reported. + +.SH "SEE ALSO" +ps(1) regex(7) signal(7) killall(1) skill(1) kill(1) kill(2) + +.SH STANDARDS +\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This +implementation is fully compatible. + +.SH AUTHOR +Kjetil Torgrim Homme + +Albert Cahalan is the current maintainer of +the procps package. + +Please send bug reports to diff --git a/pgrep.c b/pgrep.c new file mode 100644 index 0000000..bbb7cd1 --- /dev/null +++ b/pgrep.c @@ -0,0 +1,732 @@ +/* emacs settings: -*- c-basic-offset: 8 tab-width: 8 -*- + * + * pgrep/pkill -- utilities to filter the process table + * + * Copyright 2000 Kjetil Torgrim Homme + * + * May be distributed under the conditions of the + * GNU General Public License; a copy is in COPYING + * + * Changes by Albert Cahalan, 2002,2006. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/readproc.h" +#include "proc/sig.h" +#include "proc/devname.h" +#include "proc/sysinfo.h" +#include "proc/version.h" /* procps_version */ + +// EXIT_SUCCESS is 0 +// EXIT_FAILURE is 1 +#define EXIT_USAGE 2 +#define EXIT_FATAL 3 + +static int i_am_pkill = 0; +static const char *progname = "pgrep"; + +union el { + long num; + char * str; +}; + +/* User supplied arguments */ + +static int opt_full = 0; +static int opt_long = 0; +static int opt_oldest = 0; +static int opt_newest = 0; +static int opt_negate = 0; +static int opt_exact = 0; +static int opt_signal = SIGTERM; +static int opt_lock = 0; +static int opt_case = 0; + +static const char *opt_delim = "\n"; +static union el *opt_pgrp = NULL; +static union el *opt_rgid = NULL; +static union el *opt_pid = NULL; +static union el *opt_ppid = NULL; +static union el *opt_sid = NULL; +static union el *opt_term = NULL; +static union el *opt_euid = NULL; +static union el *opt_ruid = NULL; +static char *opt_pattern = NULL; +static char *opt_pidfile = NULL; + +static int usage (int opt) NORETURN; +static int usage (int opt) +{ + int err = (opt=='?'); /* getopt() uses '?' to mark an error */ + FILE *fp = err ? stderr : stdout; + + if (i_am_pkill) + fprintf (fp, "Usage: pkill [-SIGNAL] [-fvx] "); + else + fprintf (fp, "Usage: pgrep [-flvx] [-d DELIM] "); + fprintf (fp, "[-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n" + "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] " + "[PATTERN]\n"); + + exit(err ? EXIT_USAGE : EXIT_SUCCESS); +} + + +static union el *split_list (const char *restrict str, int (*convert)(const char *, union el *)) +{ + char *copy = strdup (str); + char *ptr = copy; + char *sep_pos; + int i = 0; + int size = 0; + union el *list = NULL; + + do { + if (i == size) { + size = size * 5 / 4 + 4; + // add 1 because slot zero is a count + list = realloc (list, 1 + size * sizeof *list); + if (list == NULL) + exit (EXIT_FATAL); + } + sep_pos = strchr (ptr, ','); + if (sep_pos) + *sep_pos = 0; + // Use ++i instead of i++ because slot zero is a count + if (!convert (ptr, &list[++i])) + exit (EXIT_USAGE); + if (sep_pos) + ptr = sep_pos + 1; + } while (sep_pos); + + free (copy); + if (!i) { + free (list); + list = NULL; + } else { + list[0].num = i; + } + return list; +} + +// strict_atol returns a Boolean: TRUE if the input string +// contains a plain number, FALSE if there are any non-digits. +static int strict_atol (const char *restrict str, long *restrict value) +{ + int res = 0; + int sign = 1; + + if (*str == '+') + ++str; + else if (*str == '-') { + ++str; + sign = -1; + } + + for ( ; *str; ++str) { + if (! isdigit (*str)) + return (0); + res *= 10; + res += *str - '0'; + } + *value = sign * res; + return 1; +} + +#include + +// Seen non-BSD code do this: +// +//if (fcntl_lock(pid_fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1) +// return -1; +int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len) +{ + struct flock lock[1]; + + lock->l_type = type; + lock->l_whence = whence; + lock->l_start = start; + lock->l_len = len; + + return fcntl(fd, cmd, lock); +} + + +// We try a read lock. The daemon should have a write lock. +// Seen using flock: FreeBSD code +static int has_flock(int fd) +{ + return flock(fd, LOCK_SH|LOCK_NB)==-1 && errno==EWOULDBLOCK; +} + +// We try a read lock. The daemon should have a write lock. +// Seen using fcntl: libslack +static int has_fcntl(int fd) +{ + struct flock f; // seriously, struct flock is for a fnctl lock! + f.l_type = F_RDLCK; + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; + return fcntl(fd,F_SETLK,&f)==-1 && (errno==EACCES || errno==EAGAIN); +} + +static union el *read_pidfile(void) +{ + char buf[12]; + int fd; + struct stat sbuf; + char *endp; + int pid; + union el *list = NULL; + + fd = open(opt_pidfile, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0) + goto out; + if(fstat(fd,&sbuf) || !S_ISREG(sbuf.st_mode) || sbuf.st_size<1) + goto out; + // type of lock, if any, is not standardized on Linux + if(opt_lock && !has_flock(fd) && !has_fcntl(fd)) + goto out; + memset(buf,'\0',sizeof buf); + buf[read(fd,buf+1,sizeof buf-2)] = '\0'; + pid = strtoul(buf+1,&endp,10); + if(endp<=buf+1 || pid<1 || pid>0x7fffffff) + goto out; + if(*endp && !isspace(*endp)) + goto out; + list = malloc(2 * sizeof *list); + list[0].num = 1; + list[1].num = pid; +out: + close(fd); + return list; +} + +static int conv_uid (const char *restrict name, union el *restrict e) +{ + struct passwd *pwd; + + if (strict_atol (name, &e->num)) + return (1); + + pwd = getpwnam (name); + if (pwd == NULL) { + fprintf (stderr, "%s: invalid user name: %s\n", + progname, name); + return 0; + } + e->num = pwd->pw_uid; + return 1; +} + + +static int conv_gid (const char *restrict name, union el *restrict e) +{ + struct group *grp; + + if (strict_atol (name, &e->num)) + return 1; + + grp = getgrnam (name); + if (grp == NULL) { + fprintf (stderr, "%s: invalid group name: %s\n", + progname, name); + return 0; + } + e->num = grp->gr_gid; + return 1; +} + + +static int conv_pgrp (const char *restrict name, union el *restrict e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: invalid process group: %s\n", + progname, name); + return 0; + } + if (e->num == 0) + e->num = getpgrp (); + return 1; +} + + +static int conv_sid (const char *restrict name, union el *restrict e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: invalid session id: %s\n", + progname, name); + return 0; + } + if (e->num == 0) + e->num = getsid (0); + return 1; +} + + +static int conv_num (const char *restrict name, union el *restrict e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: not a number: %s\n", + progname, name); + return 0; + } + return 1; +} + + +static int conv_str (const char *restrict name, union el *restrict e) +{ + e->str = strdup (name); + return 1; +} + + +static int match_numlist (long value, const union el *restrict list) +{ + int found = 0; + if (list == NULL) + found = 0; + else { + int i; + for (i = list[0].num; i > 0; i--) { + if (list[i].num == value) + found = 1; + } + } + return found; +} + +static int match_strlist (const char *restrict value, const union el *restrict list) +{ + int found = 0; + if (list == NULL) + found = 0; + else { + int i; + for (i = list[0].num; i > 0; i--) { + if (! strcmp (list[i].str, value)) + found = 1; + } + } + return found; +} + +static void output_numlist (const union el *restrict list, int num) +{ + int i; + const char *delim = opt_delim; + for (i = 0; i < num; i++) { + if(i+1==num) + delim = "\n"; + printf ("%ld%s", list[i].num, delim); + } +} + +static void output_strlist (const union el *restrict list, int num) +{ +// FIXME: escape codes + int i; + const char *delim = opt_delim; + for (i = 0; i < num; i++) { + if(i+1==num) + delim = "\n"; + printf ("%s%s", list[i].str, delim); + } +} + +static PROCTAB *do_openproc (void) +{ + PROCTAB *ptp; + int flags = 0; + + if (opt_pattern || opt_full) + flags |= PROC_FILLCOM; + if (opt_ruid || opt_rgid) + flags |= PROC_FILLSTATUS; + if (opt_oldest || opt_newest || opt_pgrp || opt_sid || opt_term) + flags |= PROC_FILLSTAT; + if (!(flags & PROC_FILLSTAT)) + flags |= PROC_FILLSTATUS; // FIXME: need one, and PROC_FILLANY broken + if (opt_euid && !opt_negate) { + int num = opt_euid[0].num; + int i = num; + uid_t *uids = malloc (num * sizeof (uid_t)); + if (uids == NULL) + exit (EXIT_FATAL); + while (i-- > 0) { + uids[i] = opt_euid[i+1].num; + } + flags |= PROC_UID; + ptp = openproc (flags, uids, num); + } else { + ptp = openproc (flags); + } + return ptp; +} + +static regex_t * do_regcomp (void) +{ + regex_t *preg = NULL; + + if (opt_pattern) { + char *re; + char errbuf[256]; + int re_err; + + preg = malloc (sizeof (regex_t)); + if (preg == NULL) + exit (EXIT_FATAL); + if (opt_exact) { + re = malloc (strlen (opt_pattern) + 5); + if (re == NULL) + exit (EXIT_FATAL); + sprintf (re, "^(%s)$", opt_pattern); + } else { + re = opt_pattern; + } + + re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB | opt_case); + if (re_err) { + regerror (re_err, preg, errbuf, sizeof(errbuf)); + fputs(errbuf,stderr); + exit (EXIT_USAGE); + } + } + return preg; +} + +static union el * select_procs (int *num) +{ + PROCTAB *ptp; + proc_t task; + unsigned long long saved_start_time; // for new/old support + pid_t saved_pid = 0; // for new/old support + int matches = 0; + int size = 0; + regex_t *preg; + pid_t myself = getpid(); + union el *list = NULL; + char cmd[4096]; + + ptp = do_openproc(); + preg = do_regcomp(); + + if (opt_newest) saved_start_time = 0ULL; + if (opt_oldest) saved_start_time = ~0ULL; + if (opt_newest) saved_pid = 0; + if (opt_oldest) saved_pid = INT_MAX; + + memset(&task, 0, sizeof (task)); + while(readproc(ptp, &task)) { + int match = 1; + + if (task.XXXID == myself) + continue; + else if (opt_newest && task.start_time < saved_start_time) + match = 0; + else if (opt_oldest && task.start_time > saved_start_time) + match = 0; + else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid)) + match = 0; + else if (opt_pid && ! match_numlist (task.tgid, opt_pid)) + match = 0; + else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp)) + match = 0; + else if (opt_euid && ! match_numlist (task.euid, opt_euid)) + match = 0; + else if (opt_ruid && ! match_numlist (task.ruid, opt_ruid)) + match = 0; + else if (opt_rgid && ! match_numlist (task.rgid, opt_rgid)) + match = 0; + else if (opt_sid && ! match_numlist (task.session, opt_sid)) + match = 0; + else if (opt_term) { + if (task.tty == 0) { + match = 0; + } else { + char tty[256]; + dev_to_tty (tty, sizeof(tty) - 1, + task.tty, task.XXXID, ABBREV_DEV); + match = match_strlist (tty, opt_term); + } + } + if (opt_long || (match && opt_pattern)) { + if (opt_full && task.cmdline) { + int i = 0; + int bytes = sizeof (cmd) - 1; + + /* make sure it is always NUL-terminated */ + cmd[bytes] = 0; + /* make room for SPC in loop below */ + --bytes; + + strncpy (cmd, task.cmdline[i], bytes); + bytes -= strlen (task.cmdline[i++]); + while (task.cmdline[i] && bytes > 0) { + strncat (cmd, " ", bytes); + strncat (cmd, task.cmdline[i], bytes); + bytes -= strlen (task.cmdline[i++]) + 1; + } + } else { + strcpy (cmd, task.cmd); + } + } + + if (match && opt_pattern) { + if (regexec (preg, cmd, 0, NULL, 0) != 0) + match = 0; + } + + if (match ^ opt_negate) { /* Exclusive OR is neat */ + if (opt_newest) { + if (saved_start_time == task.start_time && + saved_pid > task.XXXID) + continue; + saved_start_time = task.start_time; + saved_pid = task.XXXID; + matches = 0; + } + if (opt_oldest) { + if (saved_start_time == task.start_time && + saved_pid < task.XXXID) + continue; + saved_start_time = task.start_time; + saved_pid = task.XXXID; + matches = 0; + } + if (matches == size) { + size = size * 5 / 4 + 4; + list = realloc(list, size * sizeof *list); + if (list == NULL) + exit (EXIT_FATAL); + } + if (opt_long) { + char buff[5096]; // FIXME + sprintf (buff, "%d %s", task.XXXID, cmd); + list[matches++].str = strdup (buff); + } else { + list[matches++].num = task.XXXID; + } + } + + memset (&task, 0, sizeof (task)); + } + closeproc (ptp); + + *num = matches; + return list; +} + + +static void parse_opts (int argc, char **argv) +{ + char opts[32] = ""; + int opt; + int criteria_count = 0; + + if (strstr (argv[0], "pkill")) { + i_am_pkill = 1; + progname = "pkill"; + /* Look for a signal name or number as first argument */ + if (argc > 1 && argv[1][0] == '-') { + int sig; + sig = signal_name_to_number (argv[1] + 1); + if (sig == -1 && isdigit (argv[1][1])) + sig = atoi (argv[1] + 1); + if (sig != -1) { + int i; + for (i = 2; i < argc; i++) + argv[i-1] = argv[i]; + --argc; + opt_signal = sig; + } + } + } else { + /* These options are for pgrep only */ + strcat (opts, "ld:"); + } + + strcat (opts, "LF:fnovxP:g:s:u:U:G:t:?V"); + + while ((opt = getopt (argc, argv, opts)) != -1) { + switch (opt) { +// case 'D': // FreeBSD: print info about non-matches for debugging +// break; + case 'F': // FreeBSD: the arg is a file containing a PID to match + opt_pidfile = strdup (optarg); + ++criteria_count; + break; + case 'G': // Solaris: match rgid/rgroup + opt_rgid = split_list (optarg, conv_gid); + if (opt_rgid == NULL) + usage (opt); + ++criteria_count; + break; +// case 'I': // FreeBSD: require confirmation before killing +// break; +// case 'J': // Solaris: match by project ID (name or number) +// break; + case 'L': // FreeBSD: fail if pidfile (see -F) not locked + opt_lock++; + break; +// case 'M': // FreeBSD: specify core (OS crash dump) file +// break; +// case 'N': // FreeBSD: specify alternate namelist file (for us, System.map -- but we don't need it) +// break; + case 'P': // Solaris: match by PPID + opt_ppid = split_list (optarg, conv_num); + if (opt_ppid == NULL) + usage (opt); + ++criteria_count; + break; +// case 'S': // FreeBSD: don't ignore the built-in kernel tasks +// break; +// case 'T': // Solaris: match by "task ID" (probably not a Linux task) +// break; + case 'U': // Solaris: match by ruid/rgroup + opt_ruid = split_list (optarg, conv_uid); + if (opt_ruid == NULL) + usage (opt); + ++criteria_count; + break; + case 'V': + fprintf(stdout, "%s (%s)\n", progname, procps_version); + exit(EXIT_SUCCESS); +// case 'c': // Solaris: match by contract ID +// break; + case 'd': // Solaris: change the delimiter + opt_delim = strdup (optarg); + break; + case 'f': // Solaris: match full process name (as in "ps -f") + opt_full = 1; + break; + case 'g': // Solaris: match pgrp + opt_pgrp = split_list (optarg, conv_pgrp); + if (opt_pgrp == NULL) + usage (opt); + ++criteria_count; + break; +// case 'i': // FreeBSD: ignore case. OpenBSD: withdrawn. See -I. This sucks. +// if (opt_case) +// usage (opt); +// opt_case = REG_ICASE; +// break; +// case 'j': // FreeBSD: restricted to the given jail ID +// break; + case 'l': // Solaris: long output format (pgrep only) Should require -f for beyond argv[0] maybe? + opt_long = 1; + break; + case 'n': // Solaris: match only the newest + if (opt_oldest|opt_negate|opt_newest) + usage (opt); + opt_newest = 1; + ++criteria_count; + break; + case 'o': // Solaris: match only the oldest + if (opt_oldest|opt_negate|opt_newest) + usage (opt); + opt_oldest = 1; + ++criteria_count; + break; + case 's': // Solaris: match by session ID -- zero means self + opt_sid = split_list (optarg, conv_sid); + if (opt_sid == NULL) + usage (opt); + ++criteria_count; + break; + case 't': // Solaris: match by tty + opt_term = split_list (optarg, conv_str); + if (opt_term == NULL) + usage (opt); + ++criteria_count; + break; + case 'u': // Solaris: match by euid/egroup + opt_euid = split_list (optarg, conv_uid); + if (opt_euid == NULL) + usage (opt); + ++criteria_count; + break; + case 'v': // Solaris: as in grep, invert the matching (uh... applied after selection I think) + if (opt_oldest|opt_negate|opt_newest) + usage (opt); + opt_negate = 1; + break; + // OpenBSD -x, being broken, does a plain string + case 'x': // Solaris: use ^(regexp)$ in place of regexp (FreeBSD too) + opt_exact = 1; + break; +// case 'z': // Solaris: match by zone ID +// break; + case '?': + usage (opt); + break; + } + } + + if(opt_lock && !opt_pidfile){ + fprintf(stderr, "%s: -L without -F makes no sense\n",progname); + usage(0); + } + + if(opt_pidfile){ + opt_pid = read_pidfile(); + if(!opt_pid){ + fprintf(stderr, "%s: pidfile not valid\n",progname); + usage(0); + } + } + + if (argc - optind == 1) + opt_pattern = argv[optind]; + else if (argc - optind > 1) + usage (0); + else if (criteria_count == 0) { + fprintf (stderr, "%s: No matching criteria specified\n", + progname); + usage (0); + } +} + + +int main (int argc, char *argv[]) +{ + union el *procs; + int num; + + parse_opts (argc, argv); + + procs = select_procs (&num); + if (i_am_pkill) { + int i; + for (i = 0; i < num; i++) { + if (kill (procs[i].num, opt_signal) != -1) continue; + if (errno==ESRCH) continue; // gone now, which is OK + fprintf (stderr, "pkill: %ld - %s\n", + procs[i].num, strerror (errno)); + } + } else { + if (opt_long) + output_strlist(procs,num); + else + output_numlist(procs,num); + } + return !num; // exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE) +} diff --git a/pkill.1 b/pkill.1 new file mode 100644 index 0000000..0e94b52 --- /dev/null +++ b/pkill.1 @@ -0,0 +1 @@ +.so man1/pgrep.1 diff --git a/pmap.1 b/pmap.1 new file mode 100644 index 0000000..a91d0e8 --- /dev/null +++ b/pmap.1 @@ -0,0 +1,39 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for pmap. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan. +.\" +.TH PMAP 1 "October 26, 2002" "Linux" "Linux User's Manual" +.SH NAME +pmap \- report memory map of a process + +.SH SYNOPSIS +.nf +pmap [ -x | -d ] [ -q ] pids... +pmap -V +.fi + +.SH DESCRIPTION +The pmap command reports the memory map of a process or processes. + +.SH "GENERAL OPTIONS" +.TS +l l l. +-x extended Show the extended format. +-d device Show the device format. +-q quiet Do not display some header/footer lines. +-V show version Displays version of program. +.TE + +.SH "SEE ALSO" +ps(1) pgrep(1) + +.SH STANDARDS +No standards apply, but pmap looks an awful lot like a SunOS command. + +.SH AUTHOR +Albert Cahalan wrote pmap in 2002, and is the current +maintainer of the procps collection. Please send bug reports +to . diff --git a/pmap.c b/pmap.c new file mode 100644 index 0000000..6b865fc --- /dev/null +++ b/pmap.c @@ -0,0 +1,372 @@ +/* + * Copyright 2002 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "proc/readproc.h" +#include "proc/version.h" +#include "proc/escape.h" + +static void usage(void) NORETURN; +static void usage(void){ + fprintf(stderr, + "Usage: pmap [-x | -d] [-q] [-A low,high] pid...\n" + "-x show details\n" + "-d show offset and device number\n" + "-q quiet; less header/footer info\n" + "-V show the version number\n" + "-A limit results to the given range\n" + ); + exit(1); +} + + +static unsigned KLONG range_low; +static unsigned KLONG range_high = ~0ull; + +static int V_option; +static int r_option; // ignored -- for SunOS compatibility +static int x_option; +static int d_option; +static int q_option; + +static unsigned shm_minor = ~0u; + +static void discover_shm_minor(void){ + void *addr; + int shmid; + char mapbuf[256]; + + if(!freopen("/proc/self/maps", "r", stdin)) return; + + // create + shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666); + if(shmid==-1) return; // failed; oh well + // attach + addr = shmat(shmid, NULL, SHM_RDONLY); + if(addr==(void*)-1) goto out_destroy; + + while(fgets(mapbuf, sizeof mapbuf, stdin)){ + char flags[32]; + char *tmp; // to clean up unprintables + unsigned KLONG start, end; + unsigned long long file_offset, inode; + unsigned dev_major, dev_minor; + sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode); + tmp = strchr(mapbuf,'\n'); + if(tmp) *tmp='\0'; + tmp = mapbuf; + while(*tmp){ + if(!isprint(*tmp)) *tmp='?'; + tmp++; + } + if(start > (unsigned long)addr) continue; + if(dev_major) continue; + if(flags[3] != 's') continue; + if(strstr(mapbuf,"/SYSV")){ + shm_minor = dev_minor; + break; + } + } + + if(shmdt(addr)) perror("shmdt"); + +out_destroy: + if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID"); + + return; +} + + +static const char *mapping_name(proc_t *p, unsigned KLONG addr, unsigned KLONG len, const char *mapbuf, unsigned showpath, unsigned dev_major, unsigned dev_minor, unsigned long long inode){ + const char *cp; + + if(!dev_major && dev_minor==shm_minor && strstr(mapbuf,"/SYSV")){ + static char shmbuf[64]; + snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode); + return shmbuf; + } + + cp = strrchr(mapbuf,'/'); + if(cp){ + if(showpath) return strchr(mapbuf,'/'); + return cp[1] ? cp+1 : cp; + } + + cp = strchr(mapbuf,'/'); + if(cp){ + if(showpath) return cp; + return strrchr(cp,'/') + 1; // it WILL succeed + } + + cp = " [ anon ]"; + if( (p->start_stack >= addr) && (p->start_stack <= addr+len) ) cp = " [ stack ]"; + return cp; +} + +static int one_proc(proc_t *p){ + char buf[32]; + char mapbuf[9600]; + char cmdbuf[512]; + unsigned long total_shared = 0ul; + unsigned long total_private_readonly = 0ul; + unsigned long total_private_writeable = 0ul; + + // Overkill, but who knows what is proper? The "w" prog + // uses the tty width to determine this. + int maxcmd = 0xfffff; + + sprintf(buf,"/proc/%u/maps",p->tgid); + if(!freopen(buf, "r", stdin)) return 1; + + escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS); + printf("%u: %s\n", p->tgid, cmdbuf); + + if(!q_option && (x_option|d_option)){ + if(x_option){ + if(sizeof(KLONG)==4) printf("Address Kbytes RSS Anon Locked Mode Mapping\n"); + else printf("Address Kbytes RSS Anon Locked Mode Mapping\n"); + } + if(d_option){ + if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n"); + else printf("Address Kbytes Mode Offset Device Mapping\n"); + } + } + + while(fgets(mapbuf,sizeof mapbuf,stdin)){ + char flags[32]; + char *tmp; // to clean up unprintables + unsigned KLONG start, end, diff; + unsigned long long file_offset, inode; + unsigned dev_major, dev_minor; + sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode); + + if(start > range_high) + break; + if(end < range_low) + continue; + + tmp = strchr(mapbuf,'\n'); + if(tmp) *tmp='\0'; + tmp = mapbuf; + while(*tmp){ + if(!isprint(*tmp)) *tmp='?'; + tmp++; + } + + diff = end-start; + if(flags[3]=='s') total_shared += diff; + if(flags[3]=='p'){ + flags[3] = '-'; + if(flags[1]=='w') total_private_writeable += diff; + else total_private_readonly += diff; + } + + // format used by Solaris 9 and procps-3.2.0+ + // an 'R' if swap not reserved (MAP_NORESERVE, SysV ISM shared mem, etc.) + flags[4] = '-'; + flags[5] = '\0'; + + if(x_option){ + const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode); + printf( + (sizeof(KLONG)==8) + ? "%016"KLF"x %7lu - - - %s %s\n" + : "%08lx %7lu - - - %s %s\n", + start, + (unsigned long)(diff>>10), + flags, + cp + ); + } + if(d_option){ + const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode); + printf( + (sizeof(KLONG)==8) + ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n" + : "%08lx %7lu %s %016Lx %03x:%05x %s\n", + start, + (unsigned long)(diff>>10), + flags, + file_offset, + dev_major, dev_minor, + cp + ); + } + if(!x_option && !d_option){ + const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode); + printf( + (sizeof(KLONG)==8) + ? "%016"KLF"x %6luK %s %s\n" + : "%08lx %6luK %s %s\n", + start, + (unsigned long)(diff>>10), + flags, + cp + ); + } + + } + + + + + if(!q_option){ + if(x_option){ + if(sizeof(KLONG)==8){ + printf("---------------- ------ ------ ------ ------\n"); + printf( + "total kB %15ld - - -\n", + (total_shared + total_private_writeable + total_private_readonly) >> 10 + ); + }else{ + printf("-------- ------- ------- ------- -------\n"); + printf( + "total kB %7ld - - -\n", + (total_shared + total_private_writeable + total_private_readonly) >> 10 + ); + } + } + if(d_option){ + printf( + "mapped: %ldK writeable/private: %ldK shared: %ldK\n", + (total_shared + total_private_writeable + total_private_readonly) >> 10, + total_private_writeable >> 10, + total_shared >> 10 + ); + } + if(!x_option && !d_option){ + if(sizeof(KLONG)==8) printf(" total %16ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10); + else printf(" total %8ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10); + } + } + + return 0; +} + + +int main(int argc, char *argv[]){ + unsigned *pidlist; + unsigned count = 0; + PROCTAB* PT; + proc_t p; + int ret = 0; + + if(argc<2) usage(); + pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps + + while(*++argv){ + if(!strcmp("--version",*argv)){ + V_option++; + continue; + } + if(**argv=='-'){ + char *walk = *argv; + if(!walk[1]) usage(); + while(*++walk){ + switch(*walk){ + case 'V': + V_option++; + break; + case 'x': + x_option++; + break; + case 'r': + r_option++; + break; + case 'd': + d_option++; + break; + case 'q': + q_option++; + break; + case 'A':{ + char *arg1; + if(walk[1]){ + arg1 = walk+1; + walk += strlen(walk)-1; + }else{ + arg1 = *++argv; + if(!arg1) + usage(); + } + char *arg2 = strchr(arg1,','); + if(arg2) + *arg2 = '\0'; + arg2 = arg2 ? arg2++ : arg1; + + if(*arg1) + range_low = STRTOUKL(arg1,&arg1,16); + if(*arg2) + range_high = STRTOUKL(arg2,&arg2,16); + if(*arg1 || *arg2) + usage(); + } + break; + case 'a': // Sun prints anon/swap reservations + case 'F': // Sun forces hostile ptrace-like grab + case 'l': // Sun shows unresolved dynamic names + case 'L': // Sun shows lgroup info + case 's': // Sun shows page sizes + case 'S': // Sun shows swap reservations + default: + usage(); + } + } + }else{ + char *walk = *argv; + char *endp; + unsigned long pid; + if(!strncmp("/proc/",walk,6)){ + walk += 6; + // user allowed to do: pmap /proc/* + if(*walk<'0' || *walk>'9') continue; + } + if(*walk<'0' || *walk>'9') usage(); + pid = strtoul(walk, &endp, 0); + if(pid<1ul || pid>0x7ffffffful || *endp) usage(); + pidlist[count++] = pid; + } + } + + if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes + if(V_option){ + if(count|x_option|r_option|d_option|q_option) usage(); + fprintf(stdout, "pmap (%s)\n", procps_version); + return 0; + } + if(count<1) usage(); // no processes + if(d_option && x_option) usage(); + + discover_shm_minor(); + + pidlist[count] = 0; // old libproc interface is zero-terminated + PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist); + while(readproc(PT, &p)){ + ret |= one_proc(&p); + if(p.cmdline) free((void*)*p.cmdline); + count--; + } + closeproc(PT); + + if(count) ret |= 42; // didn't find all processes asked for + return ret; +} diff --git a/proc/COPYING b/proc/COPYING new file mode 100644 index 0000000..92b8903 --- /dev/null +++ b/proc/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/proc/alloc.c b/proc/alloc.c new file mode 100644 index 0000000..4a0aca7 --- /dev/null +++ b/proc/alloc.c @@ -0,0 +1,49 @@ +// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com +// Copyright 2002 Albert Cahalan +// +// This file is placed under the conditions of the GNU Library +// General Public License, version 2, or any later version. +// See file COPYING for information on distribution conditions. + +#include +#include +#include "alloc.h" + +void *xcalloc(void *pointer, int size) { + void * ret; + if (pointer) + free(pointer); + if (!(ret = calloc(1, size))) { + fprintf(stderr, "xcalloc: allocation error, size = %d\n", size); + exit(1); + } + return ret; +} + +void *xmalloc(unsigned int size) { + void *p; + + if (size == 0) + ++size; + p = malloc(size); + if (!p) { + fprintf(stderr, "xmalloc: malloc(%d) failed", size); + perror(NULL); + exit(1); + } + return(p); +} + +void *xrealloc(void *oldp, unsigned int size) { + void *p; + + if (size == 0) + ++size; + p = realloc(oldp, size); + if (!p) { + fprintf(stderr, "xrealloc: realloc(%d) failed", size); + perror(NULL); + exit(1); + } + return(p); +} diff --git a/proc/alloc.h b/proc/alloc.h new file mode 100644 index 0000000..8c5016d --- /dev/null +++ b/proc/alloc.h @@ -0,0 +1,14 @@ +#ifndef PROCPS_PROC_ALLOC_H +#define PROCPS_PROC_ALLOC_H + +#include "procps.h" + +EXTERN_C_BEGIN + +extern void *xrealloc(void *oldp, unsigned int size) MALLOC; +extern void *xmalloc(unsigned int size) MALLOC; +extern void *xcalloc(void *pointer, int size) MALLOC; + +EXTERN_C_END + +#endif diff --git a/proc/devname.c b/proc/devname.c new file mode 100644 index 0000000..32ad954 --- /dev/null +++ b/proc/devname.c @@ -0,0 +1,329 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "version.h" +#include "devname.h" + +// This is the buffer size for a tty name. Any path is legal, +// which makes PAGE_SIZE appropriate (see kernel source), but +// that is only 99% portable and utmp only holds 32 anyway. +// We need at least 20 for guess_name(). +#define TTY_NAME_SIZE 128 + +/* Who uses what: + * + * tty_to_dev w (there is a fancy version in ps) + * dev_to_tty top, ps + */ + +#if 0 +#include +#define MAJOR_OF(d) ((unsigned)major(d)) +#define MINOR_OF(d) ((unsigned)minor(d)) +#else +#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu ) +#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) ) +#undef major +#undef minor +#define major <-- do not use --> +#define minor <-- do not use --> +#endif + +typedef struct tty_map_node { + struct tty_map_node *next; + unsigned short devfs_type; // bool + unsigned short major_number; + unsigned minor_first; + unsigned minor_last; + char name[16]; +} tty_map_node; + +static tty_map_node *tty_map = NULL; + +/* Load /proc/tty/drivers for device name mapping use. */ +static void load_drivers(void){ + char buf[10000]; + char *p; + int fd; + int bytes; + fd = open("/proc/tty/drivers",O_RDONLY); + if(fd == -1) goto fail; + bytes = read(fd, buf, sizeof(buf) - 1); + if(bytes == -1) goto fail; + buf[bytes] = '\0'; + p = buf; + while(( p = strstr(p, " /dev/") )){ // " /dev/" is the second column + tty_map_node *tmn; + int len; + char *end; + p += 6; + end = strchr(p, ' '); + if(!end) continue; + len = end - p; + tmn = calloc(1, sizeof(tty_map_node)); + tmn->next = tty_map; + tty_map = tmn; + /* if we have a devfs type name such as /dev/tts/%d then strip the %d but + keep a flag. */ + if(len >= 3 && !strncmp(end - 2, "%d", 2)){ + len -= 2; + tmn->devfs_type = 1; + } + if(len >= sizeof tmn->name) + len = sizeof tmn->name - 1; // mangle it to avoid overflow + memcpy(tmn->name, p, len); + p = end; /* set p to point past the %d as well if there is one */ + while(*p == ' ') p++; + tmn->major_number = atoi(p); + p += strspn(p, "0123456789"); + while(*p == ' ') p++; + switch(sscanf(p, "%u-%u", &tmn->minor_first, &tmn->minor_last)){ + default: + /* Can't finish parsing this line so we remove it from the list */ + tty_map = tty_map->next; + free(tmn); + break; + case 1: + tmn->minor_last = tmn->minor_first; + break; + case 2: + break; + } + } +fail: + if(fd != -1) close(fd); + if(!tty_map) tty_map = (tty_map_node *)-1; +} + +/* Try to guess the device name from /proc/tty/drivers info. */ +static int driver_name(char *restrict const buf, unsigned maj, unsigned min){ + struct stat sbuf; + tty_map_node *tmn; + if(!tty_map) load_drivers(); + if(tty_map == (tty_map_node *)-1) return 0; + tmn = tty_map; + for(;;){ + if(!tmn) return 0; + if(tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) break; + tmn = tmn->next; + } + sprintf(buf, "/dev/%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */ + if(stat(buf, &sbuf) < 0){ + if(tmn->devfs_type) return 0; + sprintf(buf, "/dev/%s", tmn->name); /* like "/dev/ttyZZ255" */ + if(stat(buf, &sbuf) < 0) return 0; + } + if(min != MINOR_OF(sbuf.st_rdev)) return 0; + if(maj != MAJOR_OF(sbuf.st_rdev)) return 0; + return 1; +} + +// major 204 is a mess -- "Low-density serial ports" +static const char low_density_names[][6] = { +"LU0", "LU1", "LU2", "LU3", +"FB0", +"SA0", "SA1", "SA2", +"SC0", "SC1", "SC2", "SC3", +"FW0", "FW1", "FW2", "FW3", +"AM0", "AM1", "AM2", "AM3", "AM4", "AM5", "AM6", "AM7", +"AM8", "AM9", "AM10", "AM11", "AM12", "AM13", "AM14", "AM15", +"DB0", "DB1", "DB2", "DB3", "DB4", "DB5", "DB6", "DB7", +"SG0", +"SMX0", "SMX1", "SMX2", +"MM0", "MM1", +"CPM0", "CPM1", "CPM2", "CPM3", /* "CPM4", "CPM5", */ // bad allocation? +"IOC0", "IOC1", "IOC2", "IOC3", "IOC4", "IOC5", "IOC6", "IOC7", +"IOC8", "IOC9", "IOC10", "IOC11", "IOC12", "IOC13", "IOC14", "IOC15", +"IOC16", "IOC17", "IOC18", "IOC19", "IOC20", "IOC21", "IOC22", "IOC23", +"IOC24", "IOC25", "IOC26", "IOC27", "IOC28", "IOC29", "IOC30", "IOC31", +"VR0", "VR1", +"IOC84", "IOC85", "IOC86", "IOC87", "IOC88", "IOC89", "IOC90", "IOC91", +"IOC92", "IOC93", "IOC94", "IOC95", "IOC96", "IOC97", "IOC98", "IOC99", +"IOC100", "IOC101", "IOC102", "IOC103", "IOC104", "IOC105", "IOC106", "IOC107", +"IOC108", "IOC109", "IOC110", "IOC111", "IOC112", "IOC113", "IOC114", "IOC115", +"SIOC0", "SIOC1", "SIOC2", "SIOC3", "SIOC4", "SIOC5", "SIOC6", "SIOC7", +"SIOC8", "SIOC9", "SIOC10", "SIOC11", "SIOC12", "SIOC13", "SIOC14", "SIOC15", +"SIOC16", "SIOC17", "SIOC18", "SIOC19", "SIOC20", "SIOC21", "SIOC22", "SIOC23", +"SIOC24", "SIOC25", "SIOC26", "SIOC27", "SIOC28", "SIOC29", "SIOC30", "SIOC31", +"PSC0", "PSC1", "PSC2", "PSC3", "PSC4", "PSC5", +"AT0", "AT1", "AT2", "AT3", "AT4", "AT5", "AT6", "AT7", +"AT8", "AT9", "AT10", "AT11", "AT12", "AT13", "AT14", "AT15", +"NX0", "NX1", "NX2", "NX3", "NX4", "NX5", "NX6", "NX7", +"NX8", "NX9", "NX10", "NX11", "NX12", "NX13", "NX14", "NX15", +"J0", // minor is 186 +"UL0","UL1","UL2","UL3", +"xvc0", // FAIL -- "/dev/xvc0" lacks "tty" prefix +"PZ0","PZ1","PZ2","PZ3", +"TX0","TX1","TX2","TX3","TX4","TX5","TX6","TX7", +"SC0","SC1","SC2","SC3", +"MAX0","MAX1","MAX2","MAX3", +}; + +#if 0 +// test code +#include +#define AS(x) (sizeof(x)/sizeof((x)[0])) +int main(int argc, char *argv[]){ + int i = 0; + while(i 255) return 0; // should never happen; array index protection + t0 = "pqrstuvwxyzabcde"[tmpmin>>4]; + t1 = "0123456789abcdef"[tmpmin&0x0f]; + sprintf(buf, "/dev/tty%c%c", t0, t1); + break; + case 4: + if(min<64){ + sprintf(buf, "/dev/tty%d", min); + break; + } + sprintf(buf, "/dev/ttyS%d", min-64); + break; + case 11: sprintf(buf, "/dev/ttyB%d", min); break; + case 17: sprintf(buf, "/dev/ttyH%d", min); break; + case 19: sprintf(buf, "/dev/ttyC%d", min); break; + case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */ + case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */ + case 24: sprintf(buf, "/dev/ttyE%d", min); break; + case 32: sprintf(buf, "/dev/ttyX%d", min); break; + case 43: sprintf(buf, "/dev/ttyI%d", min); break; + case 46: sprintf(buf, "/dev/ttyR%d", min); break; + case 48: sprintf(buf, "/dev/ttyL%d", min); break; + case 57: sprintf(buf, "/dev/ttyP%d", min); break; + case 71: sprintf(buf, "/dev/ttyF%d", min); break; + case 75: sprintf(buf, "/dev/ttyW%d", min); break; + case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */ + case 105: sprintf(buf, "/dev/ttyV%d", min); break; + case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */ + /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */ + case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break; + case 148: sprintf(buf, "/dev/ttyT%d", min); break; + case 154: sprintf(buf, "/dev/ttySR%d", min); break; + case 156: sprintf(buf, "/dev/ttySR%d", min+256); break; + case 164: sprintf(buf, "/dev/ttyCH%d", min); break; + case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */ + case 172: sprintf(buf, "/dev/ttyMX%d", min); break; + case 174: sprintf(buf, "/dev/ttySI%d", min); break; + case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */ + case 204: + if(min >= sizeof low_density_names / sizeof low_density_names[0]) return 0; + memcpy(buf,"/dev/tty",8); + memcpy(buf+8, low_density_names[min], sizeof low_density_names[0]); + buf[8 + sizeof low_density_names[0]] = '\0'; +// snprintf(buf, 9 + sizeof low_density_names[0], "/dev/tty%.*s", sizeof low_density_names[0], low_density_names[min]); + break; + case 208: sprintf(buf, "/dev/ttyU%d", min); break; + case 216: sprintf(buf, "/dev/ttyUB%d", min); break; // "/dev/rfcomm%d" now? + case 224: sprintf(buf, "/dev/ttyY%d", min); break; + case 227: sprintf(buf, "/dev/3270/tty%d", min); break; /* bummer, HUGE */ + case 229: sprintf(buf, "/dev/iseries/vtty%d", min); break; /* bummer, HUGE */ + case 256: sprintf(buf, "/dev/ttyEQ%d", min); break; + default: return 0; + } + if(stat(buf, &sbuf) < 0) return 0; + if(min != MINOR_OF(sbuf.st_rdev)) return 0; + if(maj != MAJOR_OF(sbuf.st_rdev)) return 0; + return 1; +} + +/* Linux 2.2 can give us filenames that might be correct. + * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected) + * and in /proc/PID/fd/255 (used by bash to remember the tty). + */ +static int link_name(char *restrict const buf, unsigned maj, unsigned min, int pid, const char *restrict name){ + struct stat sbuf; + char path[32]; + int count; + sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */ + count = readlink(path,buf,TTY_NAME_SIZE-1); + if(count == -1) return 0; + buf[count] = '\0'; + if(stat(buf, &sbuf) < 0) return 0; + if(min != MINOR_OF(sbuf.st_rdev)) return 0; + if(maj != MAJOR_OF(sbuf.st_rdev)) return 0; + return 1; +} + +/* number --> name */ +unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) { + static char buf[TTY_NAME_SIZE]; + char *restrict tmp = buf; + unsigned dev = dev_t_dev; + unsigned i = 0; + int c; + if(dev == 0u) goto no_tty; + if(linux_version_code > LINUX_VERSION(2, 7, 0)){ // not likely to make 2.6.xx + if(link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "tty" )) goto abbrev; + } + if(driver_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev; + if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/2" )) goto abbrev; + if( guess_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev; + if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/255")) goto abbrev; + // fall through if unable to find a device file +no_tty: + strcpy(ret, "?"); + return 1; +abbrev: + if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5; + if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3; + if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4; + /* gotta check before we chop or we may chop someone else's memory */ + if(chop + (unsigned long)(tmp-buf) <= sizeof buf) + tmp[chop] = '\0'; + /* replace non-ASCII characters with '?' and return the number of chars */ + for(;;){ + c = *tmp; + tmp++; + if(!c) break; + i++; + if(c<=' ') c = '?'; + if(c>126) c = '?'; + *ret = c; + ret++; + } + *ret = '\0'; + return i; +} + +/* name --> number */ +int tty_to_dev(const char *restrict const name) { + struct stat sbuf; + static char buf[32]; + if(name[0]=='/' && stat(name, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/tty%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/pts/%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + return -1; +} diff --git a/proc/devname.h b/proc/devname.h new file mode 100644 index 0000000..10c2cb6 --- /dev/null +++ b/proc/devname.h @@ -0,0 +1,17 @@ +#ifndef PROC_DEVNAME_H +#define PROC_DEVNAME_H + +#include "procps.h" + +EXTERN_C_BEGIN + +#define ABBREV_DEV 1 /* remove /dev/ */ +#define ABBREV_TTY 2 /* remove tty */ +#define ABBREV_PTS 4 /* remove pts/ */ + +extern unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags); + +extern int tty_to_dev(const char *restrict const name); + +EXTERN_C_END +#endif diff --git a/proc/escape.c b/proc/escape.c new file mode 100644 index 0000000..0eb1418 --- /dev/null +++ b/proc/escape.c @@ -0,0 +1,216 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include "procps.h" +#include "escape.h" +#include "readproc.h" + +#if (__GNU_LIBRARY__ >= 6) +# include +# include +# include /* MB_CUR_MAX */ +# include +# include +#endif + +#if (__GNU_LIBRARY__ >= 6) +static int escape_str_utf8(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){ + int my_cells = 0; + int my_bytes = 0; + mbstate_t s; + + memset(&s, 0, sizeof (s)); + + for(;;) { + wchar_t wc; + int len = 0; + + if(my_cells >= *maxcells || my_bytes+1 >= bufsize) + break; + + if (!(len = mbrtowc (&wc, src, MB_CUR_MAX, &s))) + /* 'str' contains \0 */ + break; + + if (len < 0) { + /* invalid multibyte sequence -- zeroize state */ + memset (&s, 0, sizeof (s)); + *(dst++) = '?'; + src++; + my_cells++; + my_bytes++; + + } else if (len==1) { + /* non-multibyte */ + *(dst++) = isprint(*src) ? *src : '?'; + src++; + my_cells++; + my_bytes++; + + } else if (!iswprint(wc)) { + /* multibyte - no printable */ + *(dst++) = '?'; + src+=len; + my_cells++; + my_bytes++; + + } else { + /* multibyte - printable */ + int wlen = wcwidth(wc); + + if (wlen==0) { + // invisible multibyte -- we don't ignore it, because some terminal + // interpret it wrong and more safe is replace it with '?' + *(dst++) = '?'; + src+=len; + my_cells++; + my_bytes++; + } else { + // multibyte - printable + // Got space? + if (my_cells+wlen > *maxcells || my_bytes+1+len >= bufsize) break; + // 0x9b is control byte for some terminals + if (memchr(src, 0x9B, len)) { + // unsafe multibyte + *(dst++) = '?'; + src+=len; + my_cells++; + my_bytes++; + } else { + // safe multibyte + memcpy(dst, src, len); + my_cells += wlen; + dst += len; + my_bytes += len; + src += len; + } + } + } + //fprintf(stdout, "cells: %d\n", my_cells); + } + *(dst++) = '\0'; + + // fprintf(stderr, "maxcells: %d, my_cells; %d\n", *maxcells, my_cells); + + *maxcells -= my_cells; + return my_bytes; // bytes of text, excluding the NUL +} + +#endif /* __GNU_LIBRARY__ */ + +/* sanitize a string via one-way mangle */ +int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){ + unsigned char c; + int my_cells = 0; + int my_bytes = 0; + const char codes[] = + "Z-------------------------------" + "********************************" + "********************************" + "*******************************-" + "--------------------------------" + "********************************" + "********************************" + "********************************"; + +#if (__GNU_LIBRARY__ >= 6) + static int utf_init=0; + + if(utf_init==0){ + /* first call -- check if UTF stuff is usable */ + char *enc = nl_langinfo(CODESET); + utf_init = enc && strcasecmp(enc, "UTF-8")==0 ? 1 : -1; + } + if (utf_init==1) + /* UTF8 locales */ + return escape_str_utf8(dst, src, bufsize, maxcells); +#endif + + if(bufsize > *maxcells+1) bufsize=*maxcells+1; // FIXME: assumes 8-bit locale + + for(;;){ + if(my_cells >= *maxcells || my_bytes+1 >= bufsize) + break; + c = (unsigned char) *(src++); + if(!c) break; + if(codes[c]=='-') c='?'; + my_cells++; + my_bytes++; + *(dst++) = c; + } + *(dst++) = '\0'; + + *maxcells -= my_cells; + return my_bytes; // bytes of text, excluding the NUL +} + +///////////////////////////////////////////////// + +// escape an argv or environment string array +// +// bytes arg means sizeof(buf) +int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t bytes, int *cells){ + size_t i = 0; + + for(;;){ + i += escape_str(dst+i, *src, bytes-i, cells); + if(bytes-i < 3) break; // need room for space, a character, and the NUL + src++; + if(!*src) break; // need something to print + if (*cells<=1) break; // need room for printed size of text + dst[i++] = ' '; + --*cells; + } + return i; // bytes, excluding the NUL +} + +/////////////////////////////////////////////////// + +int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags){ + int overhead = 0; + int end = 0; + + if(flags & ESC_ARGS){ + const char **lc = (const char**)pp->cmdline; + if(lc && *lc) return escape_strlist(outbuf, lc, bytes, cells); + } + if(flags & ESC_BRACKETS){ + overhead += 2; + } + if(flags & ESC_DEFUNCT){ + if(pp->state=='Z') overhead += 10; // chars in " " + else flags &= ~ESC_DEFUNCT; + } + if(overhead + 1 >= *cells){ // if no room for even one byte of the command name + // you'd damn well better have _some_ space +// outbuf[0] = '-'; // Oct23 + outbuf[1] = '\0'; + return 1; + } + if(flags & ESC_BRACKETS){ + outbuf[end++] = '['; + } + *cells -= overhead; + end += escape_str(outbuf+end, pp->cmd, bytes-overhead, cells); + + // Hmmm, do we want "[foo] " or "[foo ]"? + if(flags & ESC_BRACKETS){ + outbuf[end++] = ']'; + } + if(flags & ESC_DEFUNCT){ + memcpy(outbuf+end, " ", 10); + end += 10; + } + outbuf[end] = '\0'; + return end; // bytes, not including the NUL +} diff --git a/proc/escape.h b/proc/escape.h new file mode 100644 index 0000000..172960f --- /dev/null +++ b/proc/escape.h @@ -0,0 +1,22 @@ +#ifndef PROCPS_PROC_ESCAPE_H +#define PROCPS_PROC_ESCAPE_H + +//#include +#include +#include "procps.h" +#include "readproc.h" + +EXTERN_C_BEGIN + +#define ESC_STRETCH 1 // since we mangle to '?' this is 1 (would be 4 for octal escapes) + +#define ESC_ARGS 0x1 // try to use cmdline instead of cmd +#define ESC_BRACKETS 0x2 // if using cmd, put '[' and ']' around it +#define ESC_DEFUNCT 0x4 // mark zombies with " " + +extern int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t n, int *cells); +extern int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells); +extern int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags); + +EXTERN_C_END +#endif diff --git a/proc/ksym.c b/proc/ksym.c new file mode 100644 index 0000000..2e5379f --- /dev/null +++ b/proc/ksym.c @@ -0,0 +1,631 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "procps.h" +#include "version.h" +#include "sysinfo.h" /* smp_num_cpus */ +#include "wchan.h" // to verify prototypes + +#define KSYMS_FILENAME "/proc/ksyms" + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/would/be/nice/to/have/this/file" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked" +#define linux_version_code 131598 /* ? */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.12" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12" +#define linux_version_code 131852 /* 2.3.12 */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS" +#define linux_version_code 131858 /* 2.3.18ac8 */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS" +#define linux_version_code 131858 /* 2.3.18ac8 */ +#define smp_num_cpus 2 +#endif + +/* These are the symbol types, with relative popularity: + * ? w machine type junk for Alpha -- odd syntax + * ? S not for i386 + * 4 W not for i386 + * 60 R + * 100 A + * 125 r + * 363 s not for i386 + * 858 B + * 905 g generated by modutils? + * 929 G generated by modutils? + * 1301 b + * 2750 D + * 4481 d + * 11417 ? + * 13666 t + * 15442 T + * + * For i386, that is: "RArBbDd?tT" + */ + +#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS" + +/* + * '?' is a symbol type + * '.' is part of a name (versioning?) + * "\t[]" are for the module name in /proc/ksyms + */ +#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +/* System.map lines look like: + * hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n + * + * Alpha systems can start with a few lines that have the address replaced + * by space padding and a 'w' for the type. For those lines, the last space + * is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv + * (just one of those, always with a "_mv", then the newline) + * + * The /proc/ksyms lines are like System.map lines w/o the symbol type char. + * When odd features are used, the name part contains: + * "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}" + * It is likely that more crap will be added... + */ + +typedef struct symb { + unsigned KLONG addr; + const char *name; +} symb; + +/* These mostly rely on POSIX to make them zero. */ + +static symb hashtable[256]; + +static char *sysmap_data; +static unsigned sysmap_room; +static symb *sysmap_index; +static unsigned sysmap_count; + +static char *ksyms_data; +static unsigned ksyms_room = 4096; +static symb *ksyms_index; +static unsigned ksyms_count; +static unsigned idx_room; + +/*********************************/ + +/* Kill this: _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$ + * We kill: (_R[^A-Z]*[0-9a-f]{8,})+$ + * + * The loop should almost never be taken, but it has to be there. + * It gets rid of anything that _looks_ like a version code, even + * if a real version code has already been found. This is because + * the inability to perfectly recognize a version code may lead to + * symbol mangling, which in turn leads to mismatches between the + * /proc/ksyms and System.map data files. + */ +#if 0 +static char *chop_version(char *arg){ + char *cp; + cp = strchr(arg,'\t'); + if(cp) *cp = '\0'; /* kill trailing module name first */ + for(;;){ + char *p; + int len = 0; + cp = strrchr(arg, 'R'); + if(!cp || cp<=arg+1 || cp[-1]!='_') break; + for(p=cp; *++p; ){ + switch(*p){ + default: + goto out; + case '0' ... '9': + case 'a' ... 'f': + len++; + continue; + case 'g' ... 'z': + case '_': + len=0; + continue; + } + } + if(len<8) break; + cp[-1] = '\0'; + } +out: + if(*arg=='G'){ + int len = strlen(arg); + while( len>8 && !memcmp(arg,"GPLONLY_",8) ){ + arg += 8; + len -= 8; + } + } + return arg; +} +#endif +static char *chop_version(char *arg){ + char *cp; + cp = strchr(arg,'\t'); + if(cp) *cp = '\0'; /* kill trailing module name first */ + for(;;){ + int len; + cp = strrchr(arg, 'R'); + if(!cp || cp<=arg+1 || cp[-1]!='_') break; + len=strlen(cp); + if(len<9) break; + if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break; + if(strspn(cp+len-8,"0123456789abcdef")!=8) break; + cp[-1] = '\0'; + } + if(*arg=='G'){ + int len = strlen(arg); + while( len>8 && !memcmp(arg,"GPLONLY_",8) ){ + arg += 8; + len -= 8; + } + } + return arg; +} + +/***********************************/ + +static const symb *search(unsigned KLONG address, symb *idx, unsigned count){ + unsigned left; + unsigned mid; + unsigned right; + if(!idx) return NULL; /* maybe not allocated */ + if(address < idx[0].addr) return NULL; + if(address >= idx[count-1].addr) return idx+count-1; + left = 0; + right = count-1; + for(;;){ + mid = (left + right) / 2; + if(address >= idx[mid].addr) left = mid; + if(address <= idx[mid].addr) right = mid; + if(right-left <= 1) break; + } + if(address == idx[right].addr) return idx+right; + return idx+left; +} + +/*********************************/ + +/* allocate if needed, read, and return buffer size */ +static void read_file(const char *restrict filename, char **bufp, unsigned *restrict roomp) { + int fd = 0; + ssize_t done; + char *buf = *bufp; + ssize_t total = 0; + unsigned room = *roomp; + + if(!room) goto hell; /* failed before */ + if(!buf) buf = malloc(room); + if(!buf) goto hell; +open_again: + fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0){ + switch(errno){ + case EINTR: goto open_again; + default: _exit(101); + case EACCES: /* somebody screwing around? */ + /* FIXME: set a flag to disable symbol lookup? */ + case ENOENT:; /* no module support */ + } + goto hell; + } + for(;;){ + done = read(fd, buf+total, room-total-1); + if(done==0) break; /* nothing left */ + if(done==-1){ + if(errno==EINTR) continue; /* try again */ + perror(""); + goto hell; + } + if(done==(ssize_t)room-total-1){ + char *tmp; + total += done; + /* more to go, but no room in buffer */ + room *= 2; + tmp = realloc(buf, room); + if(!tmp) goto hell; + buf = tmp; + continue; + } + if(done>0 && done<(ssize_t)room-total-1){ + total += done; + continue; /* OK, we read some. Go do more. */ + } + fprintf(stderr,"%ld can't happen\n", (long)done); + /* FIXME: memory leak */ + _exit(42); + } + buf[total] = '\0'; // parse_ksyms() expects NUL-terminated file + *bufp = buf; + *roomp = room; + close(fd); + return; +hell: + if(buf) free(buf); + *bufp = NULL; + *roomp = 0; /* this function will never work again */ + total = 0; + if(fd>0) close(fd); + return; +} + +/*********************************/ + +static int parse_ksyms(void) { + char *endp; + if(!ksyms_room || !ksyms_data) goto quiet_goodbye; + endp = ksyms_data; + ksyms_count = 0; + if(idx_room) goto bypass; /* some space already allocated */ + idx_room = 512; + for(;;){ + void *vp; + idx_room *= 2; + vp = realloc(ksyms_index, sizeof(symb)*idx_room); + if(!vp) goto bad_alloc; + ksyms_index = vp; +bypass: + for(;;){ + char *saved; + if(!*endp) return 1; + saved = endp; + ksyms_index[ksyms_count].addr = STRTOUKL(endp, &endp, 16); + if(endp==saved || *endp != ' ') goto bad_parse; + endp++; + saved = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + ksyms_index[ksyms_count].name = chop_version(saved); + ++endp; + if(++ksyms_count >= idx_room) break; /* need more space */ + } + } + + if(0){ +bad_alloc: + fprintf(stderr, "Warning: not enough memory available\n"); + } + if(0){ +bad_parse: + fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n"); + } +quiet_goodbye: + idx_room = 0; + if(ksyms_data) free(ksyms_data) , ksyms_data = NULL; + ksyms_room = 0; + if(ksyms_index) free(ksyms_index) , ksyms_index = NULL; + ksyms_count = 0; + return 0; +} + +/*********************************/ + +#define VCNT 16 + +static int sysmap_mmap(const char *restrict const filename, message_fn message) { + struct stat sbuf; + char *endp; + int fd; + char Version[32]; + fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0) return 0; + if(fstat(fd, &sbuf) < 0) goto bad_open; + if(!S_ISREG(sbuf.st_mode)) goto bad_open; + if(sbuf.st_size < 5000) goto bad_open; /* if way too small */ + /* Would be shared read-only, but we want '\0' after each name. */ + endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + sysmap_data = endp; + while(*endp==' '){ /* damn Alpha machine types */ + if(strncmp(endp," w ", 19)) goto bad_parse; + endp += 19; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse; + endp++; + } + if(sysmap_data == (caddr_t) -1) goto bad_open; + close(fd); + fd = -1; + sprintf(Version, "Version_%d", linux_version_code); + sysmap_room = 512; + for(;;){ + void *vp; + sysmap_room *= 2; + vp = realloc(sysmap_index, sizeof(symb)*sysmap_room); + if(!vp) goto bad_alloc; + sysmap_index = vp; + for(;;){ + char *vstart; + if(endp - sysmap_data >= sbuf.st_size){ /* if we reached the end */ + int i = VCNT; /* check VCNT times to verify this file */ + if(*Version) goto bad_version; + if(!ksyms_index) return 1; /* if can not verify, assume success */ + while(i--){ +#if 1 + const symb *findme; + const symb *map_symb; + /* Choose VCNT entries from /proc/ksyms to test */ + findme = ksyms_index + (ksyms_count*i/VCNT); + /* Search for them in the System.map */ + map_symb = search(findme->addr, sysmap_index, sysmap_count); + if(map_symb){ + if(map_symb->addr != findme->addr) continue; + /* backup to first matching address */ + while (map_symb != sysmap_index){ + if (map_symb->addr != (map_symb-1)->addr) break; + map_symb--; + } + /* search for name in symbols with same address */ + while (map_symb != (sysmap_index+sysmap_count)){ + if (map_symb->addr != findme->addr) break; + if (!strcmp(map_symb->name,findme->name)) goto good_match; + map_symb++; + } + map_symb--; /* backup to last symbol with matching address */ + message("{%s} {%s}\n",map_symb->name,findme->name); + goto bad_match; + } +good_match:; +#endif + } + return 1; /* success */ + } + sysmap_index[sysmap_count].addr = STRTOUKL(endp, &endp, 16); + if(*endp != ' ') goto bad_parse; + endp++; + if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse; + endp++; + if(*endp != ' ') goto bad_parse; + endp++; + vstart = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + ++endp; + vstart = chop_version(vstart); + sysmap_index[sysmap_count].name = vstart; + if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0'; + if(++sysmap_count >= sysmap_room) break; /* need more space */ + } + } + + if(0){ +bad_match: + message("Warning: %s does not match kernel data.\n", filename); + } + if(0){ +bad_version: + message("Warning: %s has an incorrect kernel version.\n", filename); + } + if(0){ +bad_alloc: + message("Warning: not enough memory available\n"); + } + if(0){ +bad_parse: + message("Warning: %s not parseable as a System.map\n", filename); + } + if(0){ +bad_open: + message("Warning: %s could not be opened as a System.map\n", filename); + } + + sysmap_room=0; + sysmap_count=0; + if(sysmap_index) free(sysmap_index); + sysmap_index = NULL; + if(fd>=0) close(fd); + if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1); + sysmap_data = NULL; + return 0; +} + +/*********************************/ + +static void read_and_parse(void){ + static time_t stamp; /* after data gets old, load /proc/ksyms again */ + if(time(NULL) != stamp){ + read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room); + parse_ksyms(); + memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */ + stamp = time(NULL); + } +} + +/*********************************/ + +static void default_message(const char *restrict format, ...) __attribute__((format(printf,1,2))); +static void default_message(const char *restrict format, ...) { + va_list arg; + + va_start (arg, format); + vfprintf (stderr, format, arg); + va_end (arg); +} + +/*********************************/ + +static int use_wchan_file; + +int open_psdb_message(const char *restrict override, message_fn message) { + static const char *sysmap_paths[] = { + "/boot/System.map-%s", + "/boot/System.map", + "/lib/modules/%s/System.map", + "/usr/src/linux/System.map", + "/System.map", + NULL + }; + struct stat sbuf; + struct utsname uts; + char path[128]; + const char **fmt = sysmap_paths; + const char *sm; + +#ifdef SYSMAP_FILENAME /* debug feature */ + override = SYSMAP_FILENAME; +#endif + + // first allow for a user-selected System.map file + if( + (sm=override) + || + (sm=getenv("PS_SYSMAP")) + || + (sm=getenv("PS_SYSTEM_MAP")) + ){ + if(!have_privs){ + read_and_parse(); + if(sysmap_mmap(sm, message)) return 0; + } + /* failure is better than ignoring the user & using bad data */ + return -1; /* ought to return "Namelist not found." */ + } + + // next try the Linux 2.5.xx method + if(!stat("/proc/self/wchan", &sbuf)){ + use_wchan_file = 1; // hack + return 0; + } + + // finally, search for the System.map file + uname(&uts); + path[sizeof path - 1] = '\0'; + do{ + int did_ksyms = 0; + snprintf(path, sizeof path - 1, *fmt, uts.release); + if(!stat(path, &sbuf)){ + if (did_ksyms++) read_and_parse(); + if (sysmap_mmap(path, message)) return 0; + } + }while(*++fmt); + /* TODO: Without System.map, no need to keep ksyms loaded. */ + return -1; +} + +/***************************************/ + +int open_psdb(const char *restrict override) { + return open_psdb_message(override, default_message); +} + +/***************************************/ + +static const char * read_wchan_file(unsigned pid){ + static char buf[64]; + const char *ret = buf; + ssize_t num; + int fd; + + snprintf(buf, sizeof buf, "/proc/%d/wchan", pid); + fd = open(buf, O_RDONLY); + if(fd==-1) return "?"; + num = read(fd, buf, sizeof buf - 1); + close(fd); + if(num<1) return "?"; // allow for "0" + buf[num] = '\0'; + + if(buf[0]=='0' && buf[1]=='\0') return "-"; + + // would skip over numbers if they existed -- but no + + // lame ppc64 has a '.' in front of every name + if(*ret=='.') ret++; + switch(*ret){ + case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; + case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; + case '_': while(*ret=='_') ret++; break; + } + return ret; +} + +/***************************************/ + +static const symb fail = { .name = "?" }; +static const char dash[] = "-"; +static const char star[] = "*"; + +#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */ + +/* return pointer to temporary static buffer with function name */ +const char * lookup_wchan(unsigned KLONG address, unsigned pid) { + const symb *mod_symb; + const symb *map_symb; + const symb *good_symb; + const char *ret; + unsigned hash; + + // can't cache it due to a race condition :-( + if(use_wchan_file) return read_wchan_file(pid); + + if(!address) return dash; + if(!~address) return star; + + read_and_parse(); + hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */ + if(hashtable[hash].addr == address) return hashtable[hash].name; + mod_symb = search(address, ksyms_index, ksyms_count); + if(!mod_symb) mod_symb = &fail; + map_symb = search(address, sysmap_index, sysmap_count); + if(!map_symb) map_symb = &fail; + + /* which result is closest? */ + good_symb = (mod_symb->addr > map_symb->addr) + ? mod_symb + : map_symb + ; + if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail; + + /* good_symb->name has the data, but needs to be trimmed */ + ret = good_symb->name; + // lame ppc64 has a '.' in front of every name + if(*ret=='.') ret++; + switch(*ret){ + case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; + case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; + case '_': while(*ret=='_') ret++; break; + } + /* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */ + + /* cache name after abbreviation */ + hashtable[hash].addr = address; + hashtable[hash].name = ret; + + return ret; +} diff --git a/proc/library.map b/proc/library.map new file mode 100644 index 0000000..a38627b --- /dev/null +++ b/proc/library.map @@ -0,0 +1,24 @@ +# for --version-script +# WTF is the syntax for this file? +# Give me a BNF, man! +_3_2_5 { +global: + __cyg_profile_func_enter; __cyg_profile_func_exit; main; + + readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command; + escape_str; escape_strlist; + openproc; closeproc; + tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan; + display_version; procps_version; linux_version_code; + Hertz; smp_num_cpus; have_privs; + sprint_uptime; uptime; user_from_uid; print_uptime; loadavg; + pretty_print_signals; print_given_signals; unix_print_signals; signal_name_to_number; signal_number_to_name; + meminfo; vminfo; getstat; getdiskstat; getpartitions_num; getslabinfo; get_pid_digits; + kb_active; kb_inactive; kb_main_buffers; kb_main_cached; + kb_main_free; kb_main_total; kb_main_used; kb_swap_free; + kb_swap_total; kb_swap_used; kb_main_shared; + kb_low_total; kb_low_free; kb_high_total; kb_high_free; + vm_pgpgin; vm_pgpgout; vm_pswpin; vm_pswpout; + free_slabinfo; put_slabinfo; get_slabinfo; get_proc_stats; +local: *; +}; diff --git a/proc/module.mk b/proc/module.mk new file mode 100644 index 0000000..af20334 --- /dev/null +++ b/proc/module.mk @@ -0,0 +1,130 @@ +# This file gets included into the main Makefile, in the top directory. + +# Ideally, we want something like this: +# +# /lib/libproc.so.w ELF soname ('w' is a digit, starting from 1) +# /lib/procps-x.y.z.so file itself (x.y.z is the procps version) +# /lib/libproc.so for linking, UNSUPPORTED +# /usr/lib/libproc.a for linking, UNSUPPORTED +# proc/libproc.so.w as above, if testing with LD_LIBRARY_PATH +# proc/whatever if testing with LD_PRELOAD +# proc/libproc.a for static build +# +# Without a stable ABI, there's no point in having any of that. +# Without a stable API, there's no point in having the *.a file. +# +# A new ELF soname is required for every big ABI change. To conserve +# numbers for future use, the ELF soname can be set equal to the +# file name until some future date when a stable ABI is declared. + +SHARED := 1 + +# for lib$(NAME).so and /usr/include/($NAME) and such +NAME := proc + +LIBVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION) +ABIVERSION := 0 + +SOFILE := lib$(NAME)-$(LIBVERSION).so +ifneq ($(ABIVERSION),0) +SOLINK := lib$(NAME).so +SONAME := lib$(NAME).so.$(ABIVERSION) +else +SONAME := $(SOFILE) +SOLINK := $(SOFILE) +endif + +ANAME := lib$(NAME).a + +############ + +FPIC := -fpic + +ifeq ($(SHARED),1) +ALL += proc/$(SONAME) +INSTALL += ldconfig +LIBFLAGS := -DSHARED=1 $(FPIC) +# This is in gcc 3.5, but exported functions must be marked. +#LIBFLAGS += $(call check_gcc,-fvisibility=hidden,) +LIBPROC := proc/$(SONAME) +else +ALL += proc/$(ANAME) +#INSTALL += $(usr/lib)$(ANAME) +LIBFLAGS := -DSHARED=0 +LIBPROC := proc/$(ANAME) +endif + +LIBSRC := $(wildcard proc/*.c) +LIBHDR := $(wildcard proc/*.h) +LIBOBJ := $(LIBSRC:.c=.o) + +# Separate rule for this directory, to use -fpic or -fPIC +$(filter-out proc/version.o,$(LIBOBJ)): proc/%.o: proc/%.c + $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) $< -o $@ + +LIB_X := COPYING module.mk library.map +TARFILES += $(LIBSRC) $(LIBHDR) $(addprefix proc/,$(LIB_X)) + + +# Clean away all output files, .depend, and symlinks. +# Use wildcards in case the version has changed. +CLEAN += proc/.depend proc/lib*.so* proc/lib*.a $(LIBOBJ) +DIRS += proc/ + +proc/$(ANAME): $(LIBOBJ) + $(AR) rcs $@ $^ + +#proc/$(SONAME): proc/library.map +proc/$(SONAME): $(LIBOBJ) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -shared -Wl,-soname,$(SONAME) -Wl,--version-script=proc/library.map -o $@ $^ -lc + + +# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT +proc/.depend: $(LIBSRC) $(LIBHDR) + $(strip $(CC) $(ALL_CPPFLAGS) $(LIB_CFLAGS) -MM -MG $(LIBSRC) > $@) + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),tar) +ifneq ($(MAKECMDGOALS),extratar) +ifneq ($(MAKECMDGOALS),beta) +-include proc/.depend +endif +endif +endif +endif + +#################### install rules ########################### + +$(lib)$(SOFILE) : proc/$(SONAME) + $(install) --mode a=rx $< $@ + +ifneq ($(SOLINK),$(SOFILE)) +.PHONY: $(lib)$(SOLINK) +$(lib)$(SOLINK) : $(lib)$(SOFILE) + cd $(lib) && $(ln_sf) $(SOFILE) $(SOLINK) +endif + +ifneq ($(SONAME),$(SOFILE)) +.PHONY: $(lib)$(SONAME) +$(lib)$(SONAME) : $(lib)$(SOFILE) + cd $(lib) && $(ln_sf) $(SOFILE) $(SONAME) +endif + +.PHONY: ldconfig +ldconfig : $(lib)$(SONAME) $(lib)$(SOLINK) + $(ldconfig) + +$(usr/lib)$(ANAME) : proc/$(ANAME) + $(install) --mode a=r $< $@ + +# Junk anyway... supposed to go in /usr/include/$(NAME) +#INSTALL += $(addprefix $(include),$(HDRFILES)) +# +#$(addprefix $(include),$(HDRFILES)): $(include)% : proc/% +#$(include)% : proc/% +# $(install) --mode a=r $< $@ + +################################################################## + +proc/version.o: proc/version.c proc/version.h + $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c -o $@ $< diff --git a/proc/procps.h b/proc/procps.h new file mode 100644 index 0000000..a70e925 --- /dev/null +++ b/proc/procps.h @@ -0,0 +1,112 @@ +#ifndef PROCPS_PROC_PROCPS_H +#define PROCPS_PROC_PROCPS_H + +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif + +// Some ports make the mistake of running a 32-bit userspace +// on a 64-bit kernel. Shame on them. It's not at all OK to +// make everything "long long", since that causes unneeded +// slowness on 32-bit hardware. +// +// SPARC: The 32-bit kernel was looking like an ex-penguin, +// but it lives! ("I'm not dead yet.") So, 64-bit users will +// just have to compile for 64-bit. Aw, the suffering. +// +// MIPS: Used 32-bit for embedded systems and obsolete hardware. +// The 64-bit systems use an n32 format executable, defining +// _ABIN32 to indicate this. Since n32 doesn't currently run on +// any 32-bit system, nobody get hurt if it's bloated. Not that +// this is sane of course, but it won't hurt the 32-bit users. +// __mips_eabi means eabi, which comes in both sizes, but isn't used. +// +// PowerPC: Big ugly problem! 32-bit Macs are still popular. :-/ +// +// x86-64: So far, nobody has been dumb enough to go 32-bit. +// +// Unknown: PA-RISC and zSeries +// +#if defined(k64test) || (defined(_ABIN32) && _MIPS_SIM == _ABIN32) +#define KLONG long long // not typedef; want "unsigned KLONG" to work +#define KLF "L" +#define STRTOUKL strtoull +#else +#define KLONG long +#define KLF "l" +#define STRTOUKL strtoul +#endif + +// since gcc-2.5 +#define NORETURN __attribute__((__noreturn__)) +#define FUNCTION __attribute__((__const__)) // no access to global mem, even via ptr, and no side effect + +#if !defined(restrict) && __STDC_VERSION__ < 199901 +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 92 +#define restrict __restrict__ +#else +#warning No restrict keyword? +#define restrict +#endif +#endif + +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 96 +// won't alias anything, and aligned enough for anything +#define MALLOC __attribute__ ((__malloc__)) +// no side effect, may read globals +#define PURE __attribute__ ((__pure__)) +// tell gcc what to expect: if(unlikely(err)) die(err); +#define likely(x) __builtin_expect(!!(x),1) +#define unlikely(x) __builtin_expect(!!(x),0) +#define expected(x,y) __builtin_expect((x),(y)) +#else +#define MALLOC +#define PURE +#define likely(x) (x) +#define unlikely(x) (x) +#define expected(x,y) (x) +#endif + +#if SHARED==1 && (__GNUC__ > 2 || __GNUC_MINOR__ >= 96) +#define LABEL_OFFSET +#endif + +#define STRINGIFY_ARG(a) #a +#define STRINGIFY(a) STRINGIFY_ARG(a) + +// marks old junk, to warn non-procps library users +#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3 +#define OBSOLETE __attribute__((deprecated)) +#else +#define OBSOLETE +#endif + +#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 1 ) || __GNUC__ > 3 +// Tells gcc that function is library-internal; +// so no need to do dynamic linking at run-time. +// This might work with slightly older compilers too. +#define HIDDEN __attribute__((visibility("hidden"))) +// The opposite, in case -fvisibility=hidden used +#define EXPORT __attribute__((visibility("default"))) +// Tell g++ that a function won't throw exceptions. +#define NOTHROW __attribute__((__nothrow__)) +#else +#define HIDDEN +#define EXPORT +#define NOTHROW +#endif + +// Like HIDDEN, but for an alias that gets created. +// In gcc-3.2 there is an alias+hidden conflict. +// Many will have patched this bug, but oh well. +#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 2 ) || __GNUC__ > 3 +#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x),visibility("hidden"))) +#else +#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x))) +#endif + +#endif diff --git a/proc/pwcache.c b/proc/pwcache.c new file mode 100644 index 0000000..ab7e528 --- /dev/null +++ b/proc/pwcache.c @@ -0,0 +1,77 @@ +// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com +// Note: most likely none of his code remains +// +// Copyright 2002, Albert Cahalan +// +// This file is placed under the conditions of the GNU Library +// General Public License, version 2, or any later version. +// See file COPYING for information on distribution conditions. + +#include +#include +#include +#include +#include +#include "alloc.h" +#include "pwcache.h" +#include + +// might as well fill cache lines... else we waste memory anyway + +#define HASHSIZE 64 /* power of 2 */ +#define HASH(x) ((x) & (HASHSIZE - 1)) + +static struct pwbuf { + struct pwbuf *next; + uid_t uid; + char name[P_G_SZ]; +} *pwhash[HASHSIZE]; + +char *user_from_uid(uid_t uid) { + struct pwbuf **p; + struct passwd *pw; + + p = &pwhash[HASH(uid)]; + while (*p) { + if ((*p)->uid == uid) + return((*p)->name); + p = &(*p)->next; + } + *p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf)); + (*p)->uid = uid; + pw = getpwuid(uid); + if(!pw || strlen(pw->pw_name) >= P_G_SZ) + sprintf((*p)->name, "%u", uid); + else + strcpy((*p)->name, pw->pw_name); + + (*p)->next = NULL; + return((*p)->name); +} + +static struct grpbuf { + struct grpbuf *next; + gid_t gid; + char name[P_G_SZ]; +} *grphash[HASHSIZE]; + +char *group_from_gid(gid_t gid) { + struct grpbuf **g; + struct group *gr; + + g = &grphash[HASH(gid)]; + while (*g) { + if ((*g)->gid == gid) + return((*g)->name); + g = &(*g)->next; + } + *g = (struct grpbuf *) malloc(sizeof(struct grpbuf)); + (*g)->gid = gid; + gr = getgrgid(gid); + if (!gr || strlen(gr->gr_name) >= P_G_SZ) + sprintf((*g)->name, "%u", gid); + else + strcpy((*g)->name, gr->gr_name); + (*g)->next = NULL; + return((*g)->name); +} diff --git a/proc/pwcache.h b/proc/pwcache.h new file mode 100644 index 0000000..678554d --- /dev/null +++ b/proc/pwcache.h @@ -0,0 +1,17 @@ +#ifndef PROCPS_PROC_PWCACHE_H +#define PROCPS_PROC_PWCACHE_H + +#include +#include "procps.h" + +EXTERN_C_BEGIN + +// used in pwcache and in readproc to set size of username or groupname +#define P_G_SZ 20 + +extern char *user_from_uid(uid_t uid); +extern char *group_from_gid(gid_t gid); + +EXTERN_C_END + +#endif diff --git a/proc/readproc.c b/proc/readproc.c new file mode 100644 index 0000000..4fad11d --- /dev/null +++ b/proc/readproc.c @@ -0,0 +1,1054 @@ +/* + * New Interface to Process Table -- PROCTAB Stream (a la Directory streams) + * Copyright (C) 1996 Charles L. Blake. + * Copyright (C) 1998 Michael K. Johnson + * Copyright 1998-2003 Albert Cahalan + * May be distributed under the conditions of the + * GNU Library General Public License; a copy is in COPYING + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "version.h" +#include "readproc.h" +#include "alloc.h" +#include "pwcache.h" +#include "devname.h" +#include "procps.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// sometimes it's easier to do this manually, w/o gcc helping +#ifdef PROF +extern void __cyg_profile_func_enter(void*,void*); +#define ENTER(x) __cyg_profile_func_enter((void*)x,(void*)x) +#define LEAVE(x) __cyg_profile_func_exit((void*)x,(void*)x) +#else +#define ENTER(x) +#define LEAVE(x) +#endif + +// convert hex string to unsigned long long +static unsigned long long unhex(const char *restrict cp){ + unsigned long long ull = 0; + for(;;){ + char c = *cp++; + if(unlikely(c<0x30)) break; + ull = (ull<<4) | (c - (c>0x57) ? 0x57 : 0x30) ; + } + return ull; +} + +static int task_dir_missing; + +/////////////////////////////////////////////////////////////////////////// + +typedef struct status_table_struct { + unsigned char name[7]; // /proc/*/status field name + unsigned char len; // name length +#ifdef LABEL_OFFSET + long offset; // jump address offset +#else + void *addr; +#endif +} status_table_struct; + +#ifdef LABEL_OFFSET +#define F(x) {#x, sizeof(#x)-1, (long)(&&case_##x-&&base)}, +#else +#define F(x) {#x, sizeof(#x)-1, &&case_##x}, +#endif +#define NUL {"", 0, 0}, + +// Derived from: +// gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf +// +// Suggested method: +// Grep this file for "case_", then strip those down to the name. +// (leave the colon and newline) So "Pid:\n" and "Threads:\n" +// would be lines in the file. (no quote, no escape, etc.) +// +// Watch out for name size in the status_table_struct (grrr, expanding) +// and the number of entries (we mask with 63 for now). The table +// must be padded out to 64 entries, maybe 128 in the future. + +static void status2proc(char *S, proc_t *restrict P, int is_proc){ + long Threads = 0; + long Tgid = 0; + long Pid = 0; + + static const unsigned char asso[] = + { + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 15, 61, + 61, 61, 61, 61, 61, 61, 30, 3, 5, 5, + 61, 5, 61, 8, 61, 61, 3, 61, 10, 61, + 6, 61, 13, 0, 30, 25, 0, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 3, 61, 13, + 0, 0, 61, 30, 61, 25, 61, 61, 61, 0, + 61, 61, 61, 61, 5, 61, 0, 61, 61, 61, + 0, 61, 61, 61, 61, 61, 61, 61 + }; + + static const status_table_struct table[] = { + F(VmStk) + NUL NUL + F(State) + NUL + F(VmExe) + F(ShdPnd) + NUL + F(VmData) + NUL + F(Name) + NUL NUL + F(VmRSS) + NUL NUL + F(VmLck) + NUL NUL NUL + F(Gid) + F(Pid) + NUL NUL NUL + F(VmSize) + NUL NUL + F(VmLib) + NUL NUL + F(PPid) + NUL + F(SigCgt) + NUL + F(Threads) + F(SigPnd) + NUL + F(SigIgn) + NUL + F(Uid) + NUL NUL NUL NUL NUL NUL NUL NUL NUL + NUL NUL NUL NUL NUL + F(Tgid) + NUL NUL NUL NUL + F(SigBlk) + NUL NUL NUL + }; + +#undef F +#undef NUL + +ENTER(0x220); + + P->vm_size = 0; + P->vm_lock = 0; + P->vm_rss = 0; + P->vm_data = 0; + P->vm_stack= 0; + P->vm_exe = 0; + P->vm_lib = 0; + P->nlwp = 0; + P->signal[0] = '\0'; // so we can detect it as missing for very old kernels + + goto base; + + for(;;){ + char *colon; + status_table_struct entry; + + // advance to next line + S = strchr(S, '\n'); + if(unlikely(!S)) break; // if no newline + S++; + + // examine a field name (hash and compare) + base: + if(unlikely(!*S)) break; + entry = table[63 & (asso[S[3]] + asso[S[2]] + asso[S[0]])]; + colon = strchr(S, ':'); + if(unlikely(!colon)) break; + if(unlikely(colon[1]!='\t')) break; + if(unlikely(colon-S != entry.len)) continue; + if(unlikely(memcmp(entry.name,S,colon-S))) continue; + + S = colon+2; // past the '\t' + +#ifdef LABEL_OFFSET + goto *(&&base + entry.offset); +#else + goto *entry.addr; +#endif + + case_Name:{ + unsigned u = 0; + while(u < sizeof P->cmd - 1u){ + int c = *S++; + if(unlikely(c=='\n')) break; + if(unlikely(c=='\0')) break; // should never happen + if(unlikely(c=='\\')){ + c = *S++; + if(c=='\n') break; // should never happen + if(!c) break; // should never happen + if(c=='n') c='\n'; // else we assume it is '\\' + } + P->cmd[u++] = c; + } + P->cmd[u] = '\0'; + S--; // put back the '\n' or '\0' + continue; + } +#ifdef SIGNAL_STRING + case_ShdPnd: + memcpy(P->signal, S, 16); + P->signal[16] = '\0'; + continue; + case_SigBlk: + memcpy(P->blocked, S, 16); + P->blocked[16] = '\0'; + continue; + case_SigCgt: + memcpy(P->sigcatch, S, 16); + P->sigcatch[16] = '\0'; + continue; + case_SigIgn: + memcpy(P->sigignore, S, 16); + P->sigignore[16] = '\0'; + continue; + case_SigPnd: + memcpy(P->_sigpnd, S, 16); + P->_sigpnd[16] = '\0'; + continue; +#else + case_ShdPnd: + P->signal = unhex(S); + continue; + case_SigBlk: + P->blocked = unhex(S); + continue; + case_SigCgt: + P->sigcatch = unhex(S); + continue; + case_SigIgn: + P->sigignore = unhex(S); + continue; + case_SigPnd: + P->_sigpnd = unhex(S); + continue; +#endif + case_State: + P->state = *S; + continue; + case_Tgid: + Tgid = strtol(S,&S,10); + continue; + case_Pid: + Pid = strtol(S,&S,10); + continue; + case_PPid: + P->ppid = strtol(S,&S,10); + continue; + case_Threads: + Threads = strtol(S,&S,10); + continue; + case_Uid: + P->ruid = strtol(S,&S,10); + P->euid = strtol(S,&S,10); + P->suid = strtol(S,&S,10); + P->fuid = strtol(S,&S,10); + continue; + case_Gid: + P->rgid = strtol(S,&S,10); + P->egid = strtol(S,&S,10); + P->sgid = strtol(S,&S,10); + P->fgid = strtol(S,&S,10); + continue; + case_VmData: + P->vm_data = strtol(S,&S,10); + continue; + case_VmExe: + P->vm_exe = strtol(S,&S,10); + continue; + case_VmLck: + P->vm_lock = strtol(S,&S,10); + continue; + case_VmLib: + P->vm_lib = strtol(S,&S,10); + continue; + case_VmRSS: + P->vm_rss = strtol(S,&S,10); + continue; + case_VmSize: + P->vm_size = strtol(S,&S,10); + continue; + case_VmStk: + P->vm_stack = strtol(S,&S,10); + continue; + } + +#if 0 + // recent kernels supply per-tgid pending signals + if(is_proc && *ShdPnd){ + memcpy(P->signal, ShdPnd, 16); + P->signal[16] = '\0'; + } +#endif + + // recent kernels supply per-tgid pending signals +#ifdef SIGNAL_STRING + if(!is_proc || !P->signal[0]){ + memcpy(P->signal, P->_sigpnd, 16); + P->signal[16] = '\0'; + } +#else + if(!is_proc || !have_process_pending){ + P->signal = P->_sigpnd; + } +#endif + + // Linux 2.4.13-pre1 to max 2.4.xx have a useless "Tgid" + // that is not initialized for built-in kernel tasks. + // Only 2.6.0 and above have "Threads" (nlwp) info. + + if(Threads){ + P->nlwp = Threads; + P->tgid = Tgid; // the POSIX PID value + P->tid = Pid; // the thread ID + }else{ + P->nlwp = 1; + P->tgid = Pid; + P->tid = Pid; + } + +LEAVE(0x220); +} + +/////////////////////////////////////////////////////////////////////// + +// Reads /proc/*/stat files, being careful not to trip over processes with +// names like ":-) 1 2 3 4 5 6". +static void stat2proc(const char* S, proc_t *restrict P) { + unsigned num; + char* tmp; + +ENTER(0x160); + + /* fill in default values for older kernels */ + P->processor = 0; + P->rtprio = -1; + P->sched = -1; + P->nlwp = 0; + + S = strchr(S, '(') + 1; + tmp = strrchr(S, ')'); + num = tmp - S; + if(unlikely(num >= sizeof P->cmd)) num = sizeof P->cmd - 1; + memcpy(P->cmd, S, num); + P->cmd[num] = '\0'; + S = tmp + 2; // skip ") " + + num = sscanf(S, + "%c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu " + "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */ + "%ld %ld " + "%d " + "%ld " + "%Lu " /* start_time */ + "%lu " + "%ld " + "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u " + "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ + "%"KLF"u %*lu %*lu " + "%d %d " + "%lu %lu", + &P->state, + &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid, + &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, + &P->utime, &P->stime, &P->cutime, &P->cstime, + &P->priority, &P->nice, + &P->nlwp, + &P->alarm, + &P->start_time, + &P->vsize, + &P->rss, + &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip, +/* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */ + &P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */ +/* -- Linux 2.0.35 ends here -- */ + &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */ +/* -- Linux 2.2.8 to 2.5.17 end here -- */ + &P->rtprio, &P->sched /* both added to 2.5.18 */ + ); + + if(!P->nlwp){ + P->nlwp = 1; + } + +LEAVE(0x160); +} + +///////////////////////////////////////////////////////////////////////// + +static void statm2proc(const char* s, proc_t *restrict P) { + int num; + num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld", + &P->size, &P->resident, &P->share, + &P->trs, &P->lrs, &P->drs, &P->dt); +/* fprintf(stderr, "statm2proc converted %d fields.\n",num); */ +} + +static int file2str(const char *directory, const char *what, char *ret, int cap) { + static char filename[80]; + int fd, num_read; + + sprintf(filename, "%s/%s", directory, what); + fd = open(filename, O_RDONLY, 0); + if(unlikely(fd==-1)) return -1; + num_read = read(fd, ret, cap - 1); + close(fd); + if(unlikely(num_read<=0)) return -1; + ret[num_read] = '\0'; + return num_read; +} + +static char** file2strvec(const char* directory, const char* what) { + char buf[2048]; /* read buf bytes at a time */ + char *p, *rbuf = 0, *endbuf, **q, **ret; + int fd, tot = 0, n, c, end_of_file = 0; + int align; + + sprintf(buf, "%s/%s", directory, what); + fd = open(buf, O_RDONLY, 0); + if(fd==-1) return NULL; + + /* read whole file into a memory buffer, allocating as we go */ + while ((n = read(fd, buf, sizeof buf - 1)) > 0) { + if (n < (int)(sizeof buf - 1)) + end_of_file = 1; + if (n == 0 && rbuf == 0) + return NULL; /* process died between our open and read */ + if (n < 0) { + if (rbuf) + free(rbuf); + return NULL; /* read error */ + } + if (end_of_file && buf[n-1]) /* last read char not null */ + buf[n++] = '\0'; /* so append null-terminator */ + rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */ + memcpy(rbuf + tot, buf, n); /* copy buffer into it */ + tot += n; /* increment total byte ctr */ + if (end_of_file) + break; + } + close(fd); + if (n <= 0 && !end_of_file) { + if (rbuf) free(rbuf); + return NULL; /* read error */ + } + endbuf = rbuf + tot; /* count space for pointers */ + align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1)); + for (c = 0, p = rbuf; p < endbuf; p++) + if (!*p) + c += sizeof(char*); + c += sizeof(char*); /* one extra for NULL term */ + + rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */ + endbuf = rbuf + tot; /* addr just past data buf */ + q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */ + *q++ = p = rbuf; /* point ptrs to the strings */ + endbuf--; /* do not traverse final NUL */ + while (++p < endbuf) + if (!*p) /* NUL char implies that */ + *q++ = p+1; /* next string -> next char */ + + *q = 0; /* null ptr list terminator */ + return ret; +} + +// warning: interface may change +int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){ + char name[32]; + int fd; + unsigned n = 0; + dst[0] = '\0'; + snprintf(name, sizeof name, "/proc/%u/cmdline", pid); + fd = open(name, O_RDONLY); + if(fd==-1) return 0; + for(;;){ + ssize_t r = read(fd,dst+n,sz-n); + if(r==-1){ + if(errno==EINTR) continue; + break; + } + n += r; + if(n==sz) break; // filled the buffer + if(r==0) break; // EOF + } + close(fd); + if(n){ + int i; + if(n==sz) n--; + dst[n] = '\0'; + i=n; + while(i--){ + int c = dst[i]; + if(c<' ' || c>'~') dst[i]=' '; + } + } + return n; +} + +/* These are some nice GNU C expression subscope "inline" functions. + * The can be used with arbitrary types and evaluate their arguments + * exactly once. + */ + +/* Test if item X of type T is present in the 0 terminated list L */ +# define XinL(T, X, L) ( { \ + T x = (X), *l = (L); \ + while (*l && *l != x) l++; \ + *l == x; \ + } ) + +/* Test if item X of type T is present in the list L of length N */ +# define XinLN(T, X, L, N) ( { \ + T x = (X), *l = (L); \ + int i = 0, n = (N); \ + while (i < n && l[i] != x) i++; \ + i < n && l[i] == x; \ + } ) + +////////////////////////////////////////////////////////////////////////////////// +// This reads process info from /proc in the traditional way, for one process. +// The pid (tgid? tid?) is already in p, and a path to it in path, with some +// room to spare. +static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) { + static struct stat sb; // stat() buffer + static char sbuf[1024]; // buffer for stat,statm + char *restrict const path = PT->path; + unsigned flags = PT->flags; + + if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ + goto next_proc; + + if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) + goto next_proc; /* not one of the requested uids */ + + p->euid = sb.st_uid; /* need a way to get real uid */ + p->egid = sb.st_gid; /* need a way to get real gid */ + + if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */ + if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 )) + goto next_proc; /* error reading /proc/#/stat */ + stat2proc(sbuf, p); /* parse /proc/#/stat */ + } + + if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */ + if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 )) + statm2proc(sbuf, p); /* ignore statm errors here */ + } /* statm fields just zero */ + + if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */ + if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){ + status2proc(sbuf, p, 1); + } + } + + // if multithreaded, some values are crap + if(p->nlwp > 1){ + p->wchan = (KLONG)~0ull; + } + + /* some number->text resolving which is time consuming and kind of insane */ + if (flags & PROC_FILLUSR){ + memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser); + if(flags & PROC_FILLSTATUS) { + memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser); + memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser); + memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser); + } + } + + /* some number->text resolving which is time consuming and kind of insane */ + if (flags & PROC_FILLGRP){ + memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup); + if(flags & PROC_FILLSTATUS) { + memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup); + memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup); + memcpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup); + } + } + + if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */ + p->cmdline = file2strvec(path, "cmdline"); + else + p->cmdline = NULL; + + if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */ + p->environ = file2strvec(path, "environ"); + else + p->environ = NULL; + + return p; +next_proc: + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// +// This reads /proc/*/task/* data, for one task. +// p is the POSIX process (task group summary) (not needed by THIS implementation) +// t is the POSIX thread (task group member, generally not the leader) +// path is a path to the task, with some room to spare. +static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) { + static struct stat sb; // stat() buffer + static char sbuf[1024]; // buffer for stat,statm + unsigned flags = PT->flags; + +//printf("hhh\n"); + if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ + goto next_task; + +// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) +// goto next_task; /* not one of the requested uids */ + + t->euid = sb.st_uid; /* need a way to get real uid */ + t->egid = sb.st_gid; /* need a way to get real gid */ + +//printf("iii\n"); + if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */ + if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 )) + goto next_task; /* error reading /proc/#/stat */ + stat2proc(sbuf, t); /* parse /proc/#/stat */ + } + + if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */ +#if 0 + if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 )) + statm2proc(sbuf, t); /* ignore statm errors here */ +#else + t->size = p->size; + t->resident = p->resident; + t->share = p->share; + t->trs = p->trs; + t->lrs = p->lrs; + t->drs = p->drs; + t->dt = p->dt; +#endif + } /* statm fields just zero */ + + if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */ + if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){ + status2proc(sbuf, t, 0); + } + } + + /* some number->text resolving which is time consuming */ + if (flags & PROC_FILLUSR){ + memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser); + if(flags & PROC_FILLSTATUS) { + memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser); + memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser); + memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser); + } + } + + /* some number->text resolving which is time consuming */ + if (flags & PROC_FILLGRP){ + memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup); + if(flags & PROC_FILLSTATUS) { + memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup); + memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup); + memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup); + } + } + +#if 0 + if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */ + t->cmdline = file2strvec(path, "cmdline"); + else + t->cmdline = NULL; + + if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */ + t->environ = file2strvec(path, "environ"); + else + t->environ = NULL; +#else + t->cmdline = p->cmdline; // better not free these until done with all threads! + t->environ = p->environ; +#endif + + t->ppid = p->ppid; // ought to put the per-task ppid somewhere + + return t; +next_task: + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// +// This finds processes in /proc in the traditional way. +// Return non-zero on success. +static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) { + static struct direct *ent; /* dirent handle */ + char *restrict const path = PT->path; + for (;;) { + ent = readdir(PT->procfs); + if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; + if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + } + p->tgid = strtoul(ent->d_name, NULL, 10); + p->tid = p->tgid; + memcpy(path, "/proc/", 6); + strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries + return 1; +} + +////////////////////////////////////////////////////////////////////////////////// +// This finds tasks in /proc/*/task/ in the traditional way. +// Return non-zero on success. +static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) { + static struct direct *ent; /* dirent handle */ + if(PT->taskdir_user != p->tgid){ + if(PT->taskdir){ + closedir(PT->taskdir); + } + // use "path" as some tmp space + snprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid); + PT->taskdir = opendir(path); + if(!PT->taskdir) return 0; + PT->taskdir_user = p->tgid; + } + for (;;) { + ent = readdir(PT->taskdir); + if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; + if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + } + t->tid = strtoul(ent->d_name, NULL, 10); + t->tgid = p->tgid; + t->ppid = p->ppid; // cover for kernel behavior? we want both actually...? + snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name); + return 1; +} + +////////////////////////////////////////////////////////////////////////////////// +// This "finds" processes in a list that was given to openproc(). +// Return non-zero on success. (tgid was handy) +static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) { + char *restrict const path = PT->path; + pid_t tgid = *(PT->pids)++; + if(likely( tgid )){ + snprintf(path, PROCPATHLEN, "/proc/%d", tgid); + p->tgid = tgid; + p->tid = tgid; // they match for leaders + } + return tgid; +} + +////////////////////////////////////////////////////////////////////////////////// +/* readproc: return a pointer to a proc_t filled with requested info about the + * next process available matching the restriction set. If no more such + * processes are available, return a null pointer (boolean false). Use the + * passed buffer instead of allocating space if it is non-NULL. */ + +/* This is optimized so that if a PID list is given, only those files are + * searched for in /proc. If other lists are given in addition to the PID list, + * the same logic can follow through as for the no-PID list case. This is + * fairly complex, but it does try to not to do any unnecessary work. + */ +proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) { + proc_t *ret; + proc_t *saved_p; + + PT->did_fake=0; +// if (PT->taskdir) { +// closedir(PT->taskdir); +// PT->taskdir = NULL; +// PT->taskdir_user = -1; +// } + + saved_p = p; + if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */ + + for(;;){ + // fills in the path, plus p->tid and p->tgid + if (unlikely(! PT->finder(PT,p) )) goto out; + + // go read the process data + ret = PT->reader(PT,p); + if(ret) return ret; + } + +out: + if(!saved_p) free(p); + // FIXME: maybe set tid to -1 here, for "-" in display? + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// +// readtask: return a pointer to a proc_t filled with requested info about the +// next task available. If no more such tasks are available, return a null +// pointer (boolean false). Use the passed buffer instead of allocating +// space if it is non-NULL. +proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) { + static char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline + proc_t *ret; + proc_t *saved_t; + + saved_t = t; + if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */ + + // 1. got to fake a thread for old kernels + // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!) + if(task_dir_missing || p->nlwp < 2){ + if(PT->did_fake) goto out; + PT->did_fake=1; + memcpy(t,p,sizeof(proc_t)); + // use the per-task pending, not per-tgid pending +#ifdef SIGNAL_STRING + memcpy(&t->signal, &t->_sigpnd, sizeof t->signal); +#else + t->signal = t->_sigpnd; +#endif + return t; + } + + for(;;){ + // fills in the path, plus t->tid and t->tgid + if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out; // simple_nexttid + + // go read the task data + ret = PT->taskreader(PT,p,t,path); // simple_readtask + if(ret) return ret; + } + +out: + if(!saved_t) free(t); + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// + +// initiate a process table scan +PROCTAB* openproc(int flags, ...) { + va_list ap; + struct stat sbuf; + static int did_stat; + PROCTAB* PT = xmalloc(sizeof(PROCTAB)); + + if(!did_stat){ + task_dir_missing = stat("/proc/self/task", &sbuf); + did_stat = 1; + } + PT->taskdir = NULL; + PT->taskdir_user = -1; + PT->taskfinder = simple_nexttid; + PT->taskreader = simple_readtask; + + PT->reader = simple_readproc; + if (flags & PROC_PID){ + PT->procfs = NULL; + PT->finder = listed_nextpid; + }else{ + PT->procfs = opendir("/proc"); + if(!PT->procfs) return NULL; + PT->finder = simple_nextpid; + } + PT->flags = flags; + + va_start(ap, flags); /* Init args list */ + if (flags & PROC_PID) + PT->pids = va_arg(ap, pid_t*); + else if (flags & PROC_UID) { + PT->uids = va_arg(ap, uid_t*); + PT->nuid = va_arg(ap, int); + } + va_end(ap); /* Clean up args list */ + + return PT; +} + +// terminate a process table scan +void closeproc(PROCTAB* PT) { + if (PT){ + if (PT->procfs) closedir(PT->procfs); + if (PT->taskdir) closedir(PT->taskdir); + memset(PT,'#',sizeof(PROCTAB)); + free(PT); + } +} + +// deallocate the space allocated by readproc if the passed rbuf was NULL +void freeproc(proc_t* p) { + if (!p) /* in case p is NULL */ + return; + /* ptrs are after strings to avoid copying memory when building them. */ + /* so free is called on the address of the address of strvec[0]. */ + if (p->cmdline) + free((void*)*p->cmdline); + if (p->environ) + free((void*)*p->environ); + free(p); +} + + +////////////////////////////////////////////////////////////////////////////////// +void look_up_our_self(proc_t *p) { + char sbuf[1024]; + + if(file2str("/proc/self", "stat", sbuf, sizeof sbuf) == -1){ + fprintf(stderr, "Error, do this: mount -t proc none /proc\n"); + _exit(47); + } + stat2proc(sbuf, p); // parse /proc/self/stat +} + +HIDDEN_ALIAS(readproc); +HIDDEN_ALIAS(readtask); + +/* Convenient wrapper around openproc and readproc to slurp in the whole process + * table subset satisfying the constraints of flags and the optional PID list. + * Free allocated memory with exit(). Access via tab[N]->member. The pointer + * list is NULL terminated. + */ +proc_t** readproctab(int flags, ...) { + PROCTAB* PT = NULL; + proc_t** tab = NULL; + int n = 0; + va_list ap; + + va_start(ap, flags); /* pass through args to openproc */ + if (flags & PROC_UID) { + /* temporary variables to ensure that va_arg() instances + * are called in the right order + */ + uid_t* u; + int i; + + u = va_arg(ap, uid_t*); + i = va_arg(ap, int); + PT = openproc(flags, u, i); + } + else if (flags & PROC_PID) + PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */ + else + PT = openproc(flags); + va_end(ap); + do { /* read table: */ + tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */ + tab[n] = readproc_direct(PT, NULL); /* final null to terminate */ + } while (tab[n++]); /* stop when NULL reached */ + closeproc(PT); + return tab; +} + +// Try again, this time with threads and selection. +proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) { + proc_t** ptab = NULL; + unsigned n_proc_alloc = 0; + unsigned n_proc = 0; + + proc_t** ttab = NULL; + unsigned n_task_alloc = 0; + unsigned n_task = 0; + + proc_t* data = NULL; + unsigned n_alloc = 0; + unsigned long n_used = 0; + + proc_data_t *pd; + + for(;;){ + proc_t *tmp; + if(n_alloc == n_used){ + //proc_t *old = data; + n_alloc = n_alloc*5/4+30; // grow by over 25% + data = realloc(data,sizeof(proc_t)*n_alloc); + //if(!data) return NULL; + } + if(n_proc_alloc == n_proc){ + //proc_t **old = ptab; + n_proc_alloc = n_proc_alloc*5/4+30; // grow by over 25% + ptab = realloc(ptab,sizeof(proc_t*)*n_proc_alloc); + //if(!ptab) return NULL; + } + tmp = readproc_direct(PT, data+n_used); + if(!tmp) break; + if(!want_proc(tmp)) continue; + ptab[n_proc++] = (proc_t*)(n_used++); + if(!( PT->flags & PROC_LOOSE_TASKS )) continue; + for(;;){ + proc_t *t; + if(n_alloc == n_used){ + proc_t *old = data; + n_alloc = n_alloc*5/4+30; // grow by over 25% + data = realloc(data,sizeof(proc_t)*n_alloc); + // have to move tmp too + tmp = data+(tmp-old); + //if(!data) return NULL; + } + if(n_task_alloc == n_task){ + //proc_t **old = ttab; + n_task_alloc = n_task_alloc*5/4+1; // grow by over 25% + ttab = realloc(ttab,sizeof(proc_t*)*n_task_alloc); + //if(!ttab) return NULL; + } + t = readtask_direct(PT, tmp, data+n_used); + if(!t) break; + if(!want_task(t)) continue; + ttab[n_task++] = (proc_t*)(n_used++); + } + } + + pd = malloc(sizeof(proc_data_t)); + pd->proc = ptab; + pd->task = ttab; + pd->nproc = n_proc; + pd->ntask = n_task; + if(PT->flags & PROC_LOOSE_TASKS){ + pd->tab = ttab; + pd->n = n_task; + }else{ + pd->tab = ptab; + pd->n = n_proc; + } + // change array indexes to pointers + while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]); + while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]); + + return pd; +} + +/* + * get_proc_stats - lookup a single tasks information and fill out a proc_t + * + * On failure, returns NULL. On success, returns 'p' and 'p' is a valid + * and filled out proc_t structure. + */ +proc_t * get_proc_stats(pid_t pid, proc_t *p) { + static char path[PATH_MAX], sbuf[1024]; + struct stat statbuf; + + sprintf(path, "/proc/%d", pid); + if (stat(path, &statbuf)) { + perror("stat"); + return NULL; + } + + if (file2str(path, "stat", sbuf, sizeof sbuf) >= 0) + stat2proc(sbuf, p); /* parse /proc/#/stat */ + if (file2str(path, "statm", sbuf, sizeof sbuf) >= 0) + statm2proc(sbuf, p); /* ignore statm errors here */ + if (file2str(path, "status", sbuf, sizeof sbuf) >= 0) + status2proc(sbuf, p, 0 /*FIXME*/); + + return p; +} diff --git a/proc/readproc.h b/proc/readproc.h new file mode 100644 index 0000000..a953b29 --- /dev/null +++ b/proc/readproc.h @@ -0,0 +1,253 @@ +#ifndef PROCPS_PROC_READPROC_H +#define PROCPS_PROC_READPROC_H + +// New Interface to Process Table -- PROCTAB Stream (a la Directory streams) +// Copyright 1996 Charles L. Blake. +// Copyright 1998 Michael K. Johnson +// Copyright 1998-2003 Albert Cahalan +// May be distributed under the terms of the +// GNU Library General Public License, a copy of which is provided +// in the file COPYING + + +#include "procps.h" +#include "pwcache.h" + +#define SIGNAL_STRING + +EXTERN_C_BEGIN + +// ld cutime, cstime, priority, nice, timeout, alarm, rss, +// c state, +// d ppid, pgrp, session, tty, tpgid, +// s signal, blocked, sigignore, sigcatch, +// lu flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime, +// lu rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip, +// lu start_time, vsize, wchan, + +// This is to help document a transition from pid to tgid/tid caused +// by the introduction of thread support. It is used in cases where +// neither tgid nor tid seemed correct. (in other words, FIXME) +#define XXXID tid + +// Basic data structure which holds all information we can get about a process. +// (unless otherwise specified, fields are read from /proc/#/stat) +// +// Most of it comes from task_struct in linux/sched.h +// +typedef struct proc_t { +// 1st 16 bytes + int + tid, // (special) task id, the POSIX thread ID (see also: tgid) + ppid; // stat,status pid of parent process + unsigned + pcpu; // stat (special) %CPU usage (is not filled in by readproc!!!) + char + state, // stat,status single-char code for process state (S=sleeping) + pad_1, // n/a padding + pad_2, // n/a padding + pad_3; // n/a padding +// 2nd 16 bytes + unsigned long long + utime, // stat user-mode CPU time accumulated by process + stime, // stat kernel-mode CPU time accumulated by process +// and so on... + cutime, // stat cumulative utime of process and reaped children + cstime, // stat cumulative stime of process and reaped children + start_time; // stat start time of process -- seconds since 1-1-70 +#ifdef SIGNAL_STRING + char + // Linux 2.1.7x and up have 64 signals. Allow 64, plus '\0' and padding. + signal[18], // status mask of pending signals, per-task for readtask() but per-proc for readproc() + blocked[18], // status mask of blocked signals + sigignore[18], // status mask of ignored signals + sigcatch[18], // status mask of caught signals + _sigpnd[18]; // status mask of PER TASK pending signals +#else + long long + // Linux 2.1.7x and up have 64 signals. + signal, // status mask of pending signals, per-task for readtask() but per-proc for readproc() + blocked, // status mask of blocked signals + sigignore, // status mask of ignored signals + sigcatch, // status mask of caught signals + _sigpnd; // status mask of PER TASK pending signals +#endif + unsigned KLONG + start_code, // stat address of beginning of code segment + end_code, // stat address of end of code segment + start_stack, // stat address of the bottom of stack for the process + kstk_esp, // stat kernel stack pointer + kstk_eip, // stat kernel instruction pointer + wchan; // stat (special) address of kernel wait channel proc is sleeping in + long + priority, // stat kernel scheduling priority + nice, // stat standard unix nice level of process + rss, // stat resident set size from /proc/#/stat (pages) + alarm, // stat ? + // the next 7 members come from /proc/#/statm + size, // statm total # of pages of memory + resident, // statm number of resident set (non-swapped) pages (4k) + share, // statm number of pages of shared (mmap'd) memory + trs, // statm text resident set size + lrs, // statm shared-lib resident set size + drs, // statm data resident set size + dt; // statm dirty pages + unsigned long + vm_size, // status same as vsize in kb + vm_lock, // status locked pages in kb + vm_rss, // status same as rss in kb + vm_data, // status data size + vm_stack, // status stack size + vm_exe, // status executable size + vm_lib, // status library size (all pages, not just used ones) + rtprio, // stat real-time priority + sched, // stat scheduling class + vsize, // stat number of pages of virtual memory ... + rss_rlim, // stat resident set size limit? + flags, // stat kernel flags for the process + min_flt, // stat number of minor page faults since process start + maj_flt, // stat number of major page faults since process start + cmin_flt, // stat cumulative min_flt of process and child processes + cmaj_flt; // stat cumulative maj_flt of process and child processes + char + **environ, // (special) environment string vector (/proc/#/environ) + **cmdline; // (special) command line string vector (/proc/#/cmdline) + char + // Be compatible: Digital allows 16 and NT allows 14 ??? + euser[P_G_SZ], // stat(),status effective user name + ruser[P_G_SZ], // status real user name + suser[P_G_SZ], // status saved user name + fuser[P_G_SZ], // status filesystem user name + rgroup[P_G_SZ], // status real group name + egroup[P_G_SZ], // status effective group name + sgroup[P_G_SZ], // status saved group name + fgroup[P_G_SZ], // status filesystem group name + cmd[16]; // stat,status basename of executable file in call to exec(2) + struct proc_t + *ring, // n/a thread group ring + *next; // n/a various library uses + int + pgrp, // stat process group id + session, // stat session id + nlwp, // stat,status number of threads, or 0 if no clue + tgid, // (special) task group ID, the POSIX PID (see also: tid) + tty, // stat full device number of controlling terminal + euid, egid, // stat(),status effective + ruid, rgid, // status real + suid, sgid, // status saved + fuid, fgid, // status fs (used for file access only) + tpgid, // stat terminal process group id + exit_signal, // stat might not be SIGCHLD + processor; // stat current (or most recent?) CPU +} proc_t; + +// PROCTAB: data structure holding the persistent information readproc needs +// from openproc(). The setup is intentionally similar to the dirent interface +// and other system table interfaces (utmp+wtmp come to mind). + +#include +#include +#include + +#define PROCPATHLEN 64 // must hold /proc/2000222000/task/2000222000/cmdline + +typedef struct PROCTAB { + DIR* procfs; +// char deBug0[64]; + DIR* taskdir; // for threads +// char deBug1[64]; + pid_t taskdir_user; // for threads + int did_fake; // used when taskdir is missing + int(*finder)(struct PROCTAB *restrict const, proc_t *restrict const); + proc_t*(*reader)(struct PROCTAB *restrict const, proc_t *restrict const); + int(*taskfinder)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const); + proc_t*(*taskreader)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const); + pid_t* pids; // pids of the procs + uid_t* uids; // uids of procs + int nuid; // cannot really sentinel-terminate unsigned short[] + int i; // generic + unsigned flags; + unsigned u; // generic + void * vp; // generic + char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline + unsigned pathlen; // length of string in the above (w/o '\0') +} PROCTAB; + +// initialize a PROCTAB structure holding needed call-to-call persistent data +extern PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ ); + +typedef struct proc_data_t { + proc_t **tab; + proc_t **proc; + proc_t **task; + int n; + int nproc; + int ntask; +} proc_data_t; + +extern proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT); + +// Convenient wrapper around openproc and readproc to slurp in the whole process +// table subset satisfying the constraints of flags and the optional PID list. +// Free allocated memory with exit(). Access via tab[N]->member. The pointer +// list is NULL terminated. + +extern proc_t** readproctab(int flags, ... /* same as openproc */ ); + +// clean-up open files, etc from the openproc() +extern void closeproc(PROCTAB* PT); + +// retrieve the next process matching the criteria set by the openproc() +extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p); +extern proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t); + +// warning: interface may change +extern int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid); + +extern void look_up_our_self(proc_t *p); + +// deallocate space allocated by readproc + +extern void freeproc(proc_t* p); + +//fill out a proc_t for a single task +extern proc_t * get_proc_stats(pid_t pid, proc_t *p); + +// openproc/readproctab: +// +// Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be +// opened.) By default readproc will consider all processes as valid to parse +// and return, but not actually fill in the cmdline, environ, and /proc/#/statm +// derived memory fields. +// +// `flags' (a bitwise-or of PROC_* below) modifies the default behavior. The +// "fill" options will cause more of the proc_t to be filled in. The "filter" +// options all use the second argument as the pointer to a list of objects: +// process status', process id's, user id's. The third +// argument is the length of the list (currently only used for lists of user +// id's since uid_t supports no convenient termination sentinel.) + +#define PROC_FILLMEM 0x0001 // read statm +#define PROC_FILLCOM 0x0002 // alloc and fill in `cmdline' +#define PROC_FILLENV 0x0004 // alloc and fill in `environ' +#define PROC_FILLUSR 0x0008 // resolve user id number -> user name +#define PROC_FILLGRP 0x0010 // resolve group id number -> group name +#define PROC_FILLSTATUS 0x0020 // read status -- currently unconditional +#define PROC_FILLSTAT 0x0040 // read stat -- currently unconditional +#define PROC_FILLWCHAN 0x0080 // look up WCHAN name +#define PROC_FILLARG 0x0100 // alloc and fill in `cmdline' + +#define PROC_LOOSE_TASKS 0x0200 // threat threads as if they were processes + +// Obsolete, consider only processes with one of the passed: +#define PROC_PID 0x1000 // process id numbers ( 0 terminated) +#define PROC_UID 0x4000 // user id numbers ( length needed ) + +// it helps to give app code a few spare bits +#define PROC_SPARE_1 0x01000000 +#define PROC_SPARE_2 0x02000000 +#define PROC_SPARE_3 0x04000000 +#define PROC_SPARE_4 0x08000000 + +EXTERN_C_END +#endif diff --git a/proc/sig.c b/proc/sig.c new file mode 100644 index 0000000..ea63397 --- /dev/null +++ b/proc/sig.c @@ -0,0 +1,240 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include "sig.h" + +/* Linux signals: + * + * SIGSYS is required by Unix98. + * SIGEMT is part of SysV, BSD, and ancient UNIX tradition. + * + * They are provided by these Linux ports: alpha, mips, sparc, and sparc64. + * You get SIGSTKFLT and SIGUNUSED instead on i386, m68k, ppc, and arm. + * (this is a Linux & libc bug -- both must be fixed) + * + * Total garbage: SIGIO SIGINFO SIGIOT SIGLOST SIGCLD + * (popular ones are handled as aliases) + * Nearly garbage: SIGSTKFLT SIGUNUSED (nothing else to fill slots) + */ + +/* Linux 2.3.29 replaces SIGUNUSED with the standard SIGSYS signal */ +#ifndef SIGSYS +# warning Standards require that define SIGSYS +# define SIGSYS SIGUNUSED +#endif + +/* If we see both, it is likely SIGSTKFLT (junk) was replaced. */ +#ifdef SIGEMT +# undef SIGSTKFLT +#endif + +#ifndef SIGRTMIN +# warning Standards require that define SIGRTMIN; assuming 32 +# define SIGRTMIN 32 +#endif + +/* It seems the SPARC libc does not know the kernel supports SIGPWR. */ +#ifndef SIGPWR +# warning Your header files lack SIGPWR. (assuming it is number 29) +# define SIGPWR 29 +#endif + +typedef struct mapstruct { + const char *name; + int num; +} mapstruct; + + +static const mapstruct sigtable[] = { + {"ABRT", SIGABRT}, /* IOT */ + {"ALRM", SIGALRM}, + {"BUS", SIGBUS}, + {"CHLD", SIGCHLD}, /* CLD */ + {"CONT", SIGCONT}, +#ifdef SIGEMT + {"EMT", SIGEMT}, +#endif + {"FPE", SIGFPE}, + {"HUP", SIGHUP}, + {"ILL", SIGILL}, + {"INT", SIGINT}, + {"KILL", SIGKILL}, + {"PIPE", SIGPIPE}, + {"POLL", SIGPOLL}, /* IO */ + {"PROF", SIGPROF}, + {"PWR", SIGPWR}, + {"QUIT", SIGQUIT}, + {"SEGV", SIGSEGV}, +#ifdef SIGSTKFLT + {"STKFLT", SIGSTKFLT}, +#endif + {"STOP", SIGSTOP}, + {"SYS", SIGSYS}, /* UNUSED */ + {"TERM", SIGTERM}, + {"TRAP", SIGTRAP}, + {"TSTP", SIGTSTP}, + {"TTIN", SIGTTIN}, + {"TTOU", SIGTTOU}, + {"URG", SIGURG}, + {"USR1", SIGUSR1}, + {"USR2", SIGUSR2}, + {"VTALRM", SIGVTALRM}, + {"WINCH", SIGWINCH}, + {"XCPU", SIGXCPU}, + {"XFSZ", SIGXFSZ} +}; + +static const int number_of_signals = sizeof(sigtable)/sizeof(mapstruct); + +static int compare_signal_names(const void *a, const void *b){ + return strcasecmp( ((const mapstruct*)a)->name, ((const mapstruct*)b)->name ); +} + +/* return -1 on failure */ +int signal_name_to_number(const char *restrict name){ + long val; + int offset; + + /* clean up name */ + if(!strncasecmp(name,"SIG",3)) name += 3; + + if(!strcasecmp(name,"CLD")) return SIGCHLD; + if(!strcasecmp(name,"IO")) return SIGPOLL; + if(!strcasecmp(name,"IOT")) return SIGABRT; + + /* search the table */ + { + const mapstruct ms = {name,0}; + const mapstruct *restrict const ptr = bsearch( + &ms, + sigtable, + number_of_signals, + sizeof(mapstruct), + compare_signal_names + ); + if(ptr) return ptr->num; + } + + if(!strcasecmp(name,"RTMIN")) return SIGRTMIN; + if(!strcasecmp(name,"EXIT")) return 0; + if(!strcasecmp(name,"NULL")) return 0; + + offset = 0; + if(!strncasecmp(name,"RTMIN+",6)){ + name += 6; + offset = SIGRTMIN; + } + + /* not found, so try as a number */ + { + char *endp; + val = strtol(name,&endp,10); + if(*endp || endp==name) return -1; /* not valid */ + } + if(val+SIGRTMIN>127) return -1; /* not valid */ + return val+offset; +} + +const char *signal_number_to_name(int signo){ + static char buf[32]; + int n = number_of_signals; + signo &= 0x7f; /* need to process exit values too */ + while(n--){ + if(sigtable[n].num==signo) return sigtable[n].name; + } + if(signo == SIGRTMIN) return "RTMIN"; + if(signo) sprintf(buf, "RTMIN+%d", signo-SIGRTMIN); + else strcpy(buf,"0"); /* AIX has NULL; Solaris has EXIT */ + return buf; +} + +int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line){ + char buf[1280]; /* 128 signals, "RTMIN+xx" is largest */ + int ret = 0; /* to be used as exit code by caller */ + int place = 0; /* position on this line */ + int amt; + if(argc > 128) return 1; + while(argc--){ + char tmpbuf[16]; + const char *restrict const txt = *argv; + if(*txt >= '0' && *txt <= '9'){ + long val; + char *endp; + val = strtol(txt,&endp,10); + if(*endp){ + fprintf(stderr, "Signal \"%s\" not known.\n", txt); + ret = 1; + goto end; + } + amt = sprintf(tmpbuf, "%s", signal_number_to_name(val)); + }else{ + int sno; + sno = signal_name_to_number(txt); + if(sno == -1){ + fprintf(stderr, "Signal \"%s\" not known.\n", txt); + ret = 1; + goto end; + } + amt = sprintf(tmpbuf, "%d", sno); + } + + if(!place){ + strcpy(buf,tmpbuf); + place = amt; + goto end; + } + if(amt+place+1 > max_line){ + printf("%s\n", buf); + strcpy(buf,tmpbuf); + place = amt; + goto end; + } + sprintf(buf+place, " %s", tmpbuf); + place += amt+1; +end: + argv++; + } + if(place) printf("%s\n", buf); + return ret; +} + +void pretty_print_signals(void){ + int i = 0; + while(++i <= number_of_signals){ + int n; + n = printf("%2d %s", i, signal_number_to_name(i)); + if(i%7) printf(" \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + n); + else printf("\n"); + } + if((i-1)%7) printf("\n"); +} + +void unix_print_signals(void){ + int pos = 0; + int i = 0; + while(++i <= number_of_signals){ + if(i-1) printf("%c", (pos>73)?(pos=0,'\n'):(pos++,' ') ); + pos += printf("%s", signal_number_to_name(i)); + } + printf("\n"); +} + +/* sanity check */ +static int init_signal_list(void) __attribute__((constructor)); +static int init_signal_list(void){ + if(number_of_signals != 31){ + fprintf(stderr, "WARNING: %d signals -- adjust and recompile.\n", number_of_signals); + } + return 0; +} diff --git a/proc/sig.h b/proc/sig.h new file mode 100644 index 0000000..ee850a2 --- /dev/null +++ b/proc/sig.h @@ -0,0 +1,30 @@ +#ifndef PROC_SIG_H +#define PROC_SIG_H +/* + * Copyright 1998-2003 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include "procps.h" + +EXTERN_C_BEGIN + +/* return -1 on failure */ +extern int signal_name_to_number(const char *restrict name); + +extern const char *signal_number_to_name(int signo); + +extern int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line); + +extern void pretty_print_signals(void); + +extern void unix_print_signals(void); + +EXTERN_C_END +#endif diff --git a/proc/slab.c b/proc/slab.c new file mode 100644 index 0000000..60c5696 --- /dev/null +++ b/proc/slab.c @@ -0,0 +1,337 @@ +/* + * slab.c - slab related functions for libproc + * + * Chris Rivera + * Robert Love + * + * This program is licensed under the GNU Library General Public License, v2 + * + * Copyright (C) 2003 Chris Rivera + * Copyright 2004, Albert Cahalan + */ + +#include +#include +#include +#include +#include + +#include "slab.h" +#include "procps.h" + +#define SLABINFO_LINE_LEN 2048 +#define SLABINFO_VER_LEN 100 +#define SLABINFO_FILE "/proc/slabinfo" + +static struct slab_info *free_index; + +/* + * get_slabnode - allocate slab_info structures using a free list + * + * In the fast path, we simply return a node off the free list. In the slow + * list, we malloc() a new node. The free list is never automatically reaped, + * both for simplicity and because the number of slab caches is fairly + * constant. + */ +static struct slab_info *get_slabnode(void) +{ + struct slab_info *node; + + if (free_index) { + node = free_index; + free_index = free_index->next; + } else { + node = malloc(sizeof(struct slab_info)); + if (!node) + perror("malloc"); + } + + return node; +} + +/* + * slab_badname_detect - return true if current slab was declared with + * whitespaces for instance + * FIXME :Other cases ? + */ + +static int slab_badname_detect(const char *restrict buffer) +{ + int numberarea=0; + while (*buffer){ + if((*buffer)==' ') + numberarea=1; + if(isalpha(*buffer)&&numberarea) + return 1; + buffer++; + } + return 0; +} + +/* + * put_slabinfo - return all allocated nodes to the free list + */ +void put_slabinfo(struct slab_info *head) +{ + free_index = head; +} + +/* + * free_slabinfo - deallocate the memory associated with each node in the + * slab_info linked list + */ +void free_slabinfo(struct slab_info *list) +{ + while (list) { + struct slab_info *temp = list->next; + free(list); + list = temp; + } +} + +// parse_slabinfo20 - actual parse routine for slabinfo 2.x (2.6 kernels) +// Note: difference between 2.0 and 2.1 is in the ": globalstat" part where version 2.1 +// has extra column . We don't use ": globalstat" part in both versions. +// +// Formats (we don't use "statistics" extensions) +// +// slabinfo - version: 2.1 +// # name \ +// : tunables \ +// : slabdata +// +// slabinfo - version: 2.1 (statistics) +// # name \ +// : tunables \ +// : slabdata \ +// : globalstat \ +// : cpustat +// +// slabinfo - version: 2.0 +// # name \ +// : tunables \ +// : slabdata +// +// slabinfo - version: 2.0 (statistics) +// # name \ +// : tunables \ +// : slabdata \ +// : globalstat \ +// : cpustat +static int parse_slabinfo20(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + struct slab_info *curr = NULL, *prev = NULL; + char buffer[SLABINFO_LINE_LEN]; + int entries = 0; + int page_size = getpagesize(); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, f)) { + int assigned; + + if (buffer[0] == '#') + continue; + + curr = get_slabnode(); + if (!curr) + break; + + if (entries++ == 0) + *list = curr; + else + prev->next = curr; + + assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d : tunables %*d %*d %*d : \ + slabdata %d %d %*d", curr->name, + &curr->nr_active_objs, &curr->nr_objs, + &curr->obj_size, &curr->objs_per_slab, + &curr->pages_per_slab, &curr->nr_active_slabs, + &curr->nr_slabs); + + if (assigned < 8) { + fprintf(stderr, "unrecognizable data in slabinfo!\n"); + curr = NULL; + break; + } + + if (curr->obj_size < stats->min_obj_size) + stats->min_obj_size = curr->obj_size; + if (curr->obj_size > stats->max_obj_size) + stats->max_obj_size = curr->obj_size; + + curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; + + if (curr->nr_objs) { + curr->use = 100 * curr->nr_active_objs / curr->nr_objs; + stats->nr_active_caches++; + } else + curr->use = 0; + + stats->nr_objs += curr->nr_objs; + stats->nr_active_objs += curr->nr_active_objs; + stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size; + stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size; + stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; + stats->nr_slabs += curr->nr_slabs; + stats->nr_active_slabs += curr->nr_active_slabs; + + prev = curr; + } + + if (!curr) { + fprintf(stderr, "\rerror reading slabinfo!\n"); + return 1; + } + + curr->next = NULL; + stats->nr_caches = entries; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; + + return 0; +} + +/* + * parse_slabinfo11 - actual parsing routine for slabinfo 1.1 (2.4 kernels) + */ +static int parse_slabinfo11(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + struct slab_info *curr = NULL, *prev = NULL; + char buffer[SLABINFO_LINE_LEN]; + int entries = 0; + int page_size = getpagesize(); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, f)) { + int assigned; + + curr = get_slabnode(); + if (!curr) + break; + + if (entries++ == 0) + *list = curr; + else + prev->next = curr; + + assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d %d", + curr->name, &curr->nr_active_objs, + &curr->nr_objs, &curr->obj_size, + &curr->nr_active_slabs, &curr->nr_slabs, + &curr->pages_per_slab); + + if (assigned < 6) { + fprintf(stderr, "unrecognizable data in your slabinfo version 1.1\n\r"); + if(slab_badname_detect(buffer)) + fprintf(stderr, "Found an error in cache name at line %s\n", buffer); + curr = NULL; + break; + } + + if (curr->obj_size < stats->min_obj_size) + stats->min_obj_size = curr->obj_size; + if (curr->obj_size > stats->max_obj_size) + stats->max_obj_size = curr->obj_size; + + curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; + + if (curr->nr_objs) { + curr->use = 100 * curr->nr_active_objs / curr->nr_objs; + stats->nr_active_caches++; + } else + curr->use = 0; + + if (curr->obj_size) + curr->objs_per_slab = curr->pages_per_slab * + page_size / curr->obj_size; + + stats->nr_objs += curr->nr_objs; + stats->nr_active_objs += curr->nr_active_objs; + stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size; + stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size; + stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; + stats->nr_slabs += curr->nr_slabs; + stats->nr_active_slabs += curr->nr_active_slabs; + + prev = curr; + } + + if (!curr) { + fprintf(stderr, "\rerror reading slabinfo!\n"); + return 1; + } + + curr->next = NULL; + stats->nr_caches = entries; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; + + return 0; +} + +/* + * parse_slabinfo10 - actual parsing routine for slabinfo 1.0 (2.2 kernels) + * + * Not yet implemented. Please feel free. + */ +static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + (void) list, (void) stats, (void) f; + fprintf(stderr, "slabinfo version 1.0 not yet supported\n"); + return 1; +} + +/* + * slabinfo - parse the system's slabinfo and fill out both a linked list of + * slab_info structures and the slab_stat structure + * + * The function returns zero on success, in which case 'list' and 'stats' are + * valid. Nonzero is returned on failure and the state of 'list' and 'stats' + * are undefined. + */ +int get_slabinfo(struct slab_info **list, struct slab_stat *stats) +{ + FILE *slabfile; + char buffer[SLABINFO_VER_LEN]; + int major, minor, ret = 0; + + slabfile = fopen(SLABINFO_FILE, "r"); + if (!slabfile) { + perror("fopen " SLABINFO_FILE); + return 1; + } + + if (!fgets(buffer, SLABINFO_VER_LEN, slabfile)) { + fprintf(stderr, "cannot read from slabinfo\n"); + return 1; + } + + if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) { + fprintf(stderr, "not the good old slabinfo we know\n"); + return 1; + } + + if (major == 2) + ret = parse_slabinfo20(list, stats, slabfile); + else if (major == 1 && minor == 1) + ret = parse_slabinfo11(list, stats, slabfile); + else if (major == 1 && minor == 0) + ret = parse_slabinfo10(list, stats, slabfile); + else { + fprintf(stderr, "unrecognizable slabinfo version\n"); + return 1; + } + + fclose(slabfile); + + return ret; +} diff --git a/proc/slab.h b/proc/slab.h new file mode 100644 index 0000000..ea17ddc --- /dev/null +++ b/proc/slab.h @@ -0,0 +1,39 @@ +#ifndef _PROC_SLAB_H +#define _PROC_SLAB_H + +#define SLAB_INFO_NAME_LEN 64 + +struct slab_info { + char name[SLAB_INFO_NAME_LEN]; /* name of this cache */ + struct slab_info *next; + unsigned long cache_size; /* size of entire cache */ + unsigned nr_objs; /* number of objects in this cache */ + unsigned nr_active_objs; /* number of active objects */ + unsigned obj_size; /* size of each object */ + unsigned objs_per_slab; /* number of objects per slab */ + unsigned pages_per_slab; /* number of pages per slab */ + unsigned nr_slabs; /* number of slabs in this cache */ + unsigned nr_active_slabs; /* number of active slabs */ + unsigned use; /* percent full: total / active */ +}; + +struct slab_stat { + unsigned long total_size; /* size of all objects */ + unsigned long active_size; /* size of all active objects */ + unsigned nr_objs; /* number of objects, among all caches */ + unsigned nr_active_objs; /* number of active objects, among all caches */ + unsigned nr_pages; /* number of pages consumed by all objects */ + unsigned nr_slabs; /* number of slabs, among all caches */ + unsigned nr_active_slabs; /* number of active slabs, among all caches */ + unsigned nr_caches; /* number of caches */ + unsigned nr_active_caches; /* number of active caches */ + unsigned avg_obj_size; /* average object size */ + unsigned min_obj_size; /* size of smallest object */ + unsigned max_obj_size; /* size of largest object */ +}; + +extern void put_slabinfo(struct slab_info *); +extern void free_slabinfo(struct slab_info *); +extern int get_slabinfo(struct slab_info **, struct slab_stat *); + +#endif /* _PROC_SLAB_H */ diff --git a/proc/smaps.c b/proc/smaps.c new file mode 100644 index 0000000..17789a1 --- /dev/null +++ b/proc/smaps.c @@ -0,0 +1,171 @@ +#if 0 +#include "procps.h" +#include +#include +#include +#include +#include +#include +#include +#include "procps.h" + +struct smap_entry { + unsigned KLONG start; + unsigned KLONG beyond; + long long offset; + char flags[8]; + unsigned dev_major; + unsigned dev_minor; + unsigned long long inode; + + unsigned long rss; + unsigned long pss; + unsigned long sclean; + unsigned long sdirty; + unsigned long pclean; + unsigned long pdirty; + unsigned long ref; + unsigned long swap; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// This code will surely make normal programmers cry. I need speed though, +// and /proc/*/smaps should make anybody cry. (WTF kind of brain damage...?) + +struct smap_summary { + unsigned long size; + unsigned long rss; + unsigned long pss; + unsigned long sclean; + unsigned long sdirty; + unsigned long pclean; + unsigned long pdirty; + unsigned long ref; + unsigned long swap; +}; + +struct ssjt { + char str[16]; + int len; + int offset; +}; + +#define JTE(o,x) {#x,sizeof(#x)-1,o} + +void get_smap_sums(struct smap_summary *restrict ss, const char *restrict const filename){ + static struct ssjt table[] = { + JTE(-1,default), + JTE( 1,Rss), + JTE(-1,default), + JTE( 2,Pss), + JTE( 8,Swap), + JTE( 5,Private_Clean), + JTE( 6,Private_Dirty), + JTE(-1,default), + JTE( 7,Referenced), + JTE(-1,default), + JTE( 0,Size), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), // KernelPageSize would go here + JTE(-1,default), + JTE(-1,default), + JTE( 4,Shared_Dirty), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE( 3,Shared_Clean), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + }; + char buf[20480]; + int p1 = 0; + int p2 = 0; + memset(ss,0,sizeof *ss); + int fd = open(filename,O_RDONLY); + if(fd==-1) + return; + for(;;){ + char *nlp = memchr(buf+p1,'\n',p2-p1); + if(!nlp){ + if(p1){ + // the memmove should never do anything, because the + // kernel seems to give us the greatest number of + // complete lines of text that fit in a single page + // (and thus p2-p1 is zero) + memmove(buf,buf+p1,p2-p1); + p2 -= p1; + p1 = 0; + } + ssize_t rb = read(fd,buf+p1,sizeof buf - p1); + if(rb < 1) + break; + p2 += rb; + nlp = memchr(buf+p1,'\n',p2-p1); + if(!nlp) + break; + } + char *s = buf+p1; + int len = nlp-s; + p1 += len+1; + if(len<27) + continue; +//printf("j <%13.13s>\n",s); + if(s[0]<'A' || s[0]>'Z') + continue; + unsigned hash = ( (s[8]&15) + (s[1]&15) ) ^ (s[0]&3); + hash &= 31; +//printf("x %2d <%13.13s>\n",hash,s); + if(s[table[hash].len] != ':') + continue; +//printf("y %2d <%13.13s>\n",hash,s); + if(memcmp(table[hash].str,s,table[hash].len)) + continue; +//printf("z %2d <%13.13s>\n",hash,s); + s += table[hash].len; + while(*++s==' ') + ; + unsigned long ul = 0; + for(;;){ + char c = *s++; + if(c != ' '){ + ul *= 10; + ul += c-'0'; + continue; + } + break; + } +// if(table[hash].offset == 2) +// printf("Pss:%20lu kB\n",ul); + unsigned long *ulp = &ss->size + table[hash].offset; + *ulp += ul; +// memcpy(ss+table[hash].offset*sizeof(unsigned long), &ul, sizeof(unsigned long)); + } + close(fd); +} + +int main(int argc, char *argv[]){ + struct smap_summary ss; + get_smap_sums(&ss, argv[1]); + printf("%9lu\n",ss.size); + printf("%9lu\n",ss.rss); + printf("%9lu\n",ss.pss); + printf("%9lu\n",ss.sclean); + printf("%9lu\n",ss.sdirty); + printf("%9lu\n",ss.pclean); + printf("%9lu\n",ss.pdirty); + printf("%9lu\n",ss.ref); + printf("%9lu\n",ss.swap); + return 0; +} +#endif diff --git a/proc/sysinfo.c b/proc/sysinfo.c new file mode 100644 index 0000000..cd59534 --- /dev/null +++ b/proc/sysinfo.c @@ -0,0 +1,912 @@ +// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com +// Copyright 1998-2003 Albert Cahalan +// +// This file is placed under the conditions of the GNU Library +// General Public License, version 2, or any later version. +// See file COPYING for information on distribution conditions. +// +// File for parsing top-level /proc entities. */ +// +// June 2003, Fabian Frederick, disk and slab info + +#include +#include +#include +#include +#include + +#include +#include +#include "version.h" +#include "sysinfo.h" /* include self to verify prototypes */ + +#ifndef HZ +#include /* htons */ +#endif + +long smp_num_cpus; /* number of CPUs */ + +#define BAD_OPEN_MESSAGE \ +"Error: /proc must be mounted\n" \ +" To mount /proc at boot you need an /etc/fstab line like:\n" \ +" /proc /proc proc defaults\n" \ +" In the meantime, run \"mount /proc /proc -t proc\"\n" + +#define STAT_FILE "/proc/stat" +static int stat_fd = -1; +#define UPTIME_FILE "/proc/uptime" +static int uptime_fd = -1; +#define LOADAVG_FILE "/proc/loadavg" +static int loadavg_fd = -1; +#define MEMINFO_FILE "/proc/meminfo" +static int meminfo_fd = -1; +#define VMINFO_FILE "/proc/vmstat" +static int vminfo_fd = -1; + +// As of 2.6.24 /proc/meminfo seems to need 888 on 64-bit, +// and would need 1258 if the obsolete fields were there. +static char buf[2048]; + +/* This macro opens filename only if necessary and seeks to 0 so + * that successive calls to the functions are more efficient. + * It also reads the current contents of the file into the global buf. + */ +#define FILE_TO_BUF(filename, fd) do{ \ + static int local_n; \ + if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \ + fputs(BAD_OPEN_MESSAGE, stderr); \ + fflush(NULL); \ + _exit(102); \ + } \ + lseek(fd, 0L, SEEK_SET); \ + if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ + perror(filename); \ + fflush(NULL); \ + _exit(103); \ + } \ + buf[local_n] = '\0'; \ +}while(0) + +/* evals 'x' twice */ +#define SET_IF_DESIRED(x,y) do{ if(x) *(x) = (y); }while(0) + + +/***********************************************************************/ +int uptime(double *restrict uptime_secs, double *restrict idle_secs) { + double up=0, idle=0; + char *restrict savelocale; + + FILE_TO_BUF(UPTIME_FILE,uptime_fd); + savelocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC,"C"); + if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { + setlocale(LC_NUMERIC,savelocale); + fputs("bad data in " UPTIME_FILE "\n", stderr); + return 0; + } + setlocale(LC_NUMERIC,savelocale); + SET_IF_DESIRED(uptime_secs, up); + SET_IF_DESIRED(idle_secs, idle); + return up; /* assume never be zero seconds in practice */ +} + +/*********************************************************************** + * Some values in /proc are expressed in units of 1/HZ seconds, where HZ + * is the kernel clock tick rate. One of these units is called a jiffy. + * The HZ value used in the kernel may vary according to hacker desire. + * According to Linus Torvalds, this is not true. He considers the values + * in /proc as being in architecture-dependant units that have no relation + * to the kernel clock tick rate. Examination of the kernel source code + * reveals that opinion as wishful thinking. + * + * In any case, we need the HZ constant as used in /proc. (the real HZ value + * may differ, but we don't care) There are several ways we could get HZ: + * + * 1. Include the kernel header file. If it changes, recompile this library. + * 2. Use the sysconf() function. When HZ changes, recompile the C library! + * 3. Ask the kernel. This is obviously correct... + * + * Linus Torvalds won't let us ask the kernel, because he thinks we should + * not know the HZ value. Oh well, we don't have to listen to him. + * Someone smuggled out the HZ value. :-) + * + * This code should work fine, even if Linus fixes the kernel to match his + * stated behavior. The code only fails in case of a partial conversion. + * + * Recent update: on some architectures, the 2.4 kernel provides an + * ELF note to indicate HZ. This may be for ARM or user-mode Linux + * support. This ought to be investigated. Note that sysconf() is still + * unreliable, because it doesn't return an error code when it is + * used with a kernel that doesn't support the ELF note. On some other + * architectures there may be a system call or sysctl() that will work. + */ + +unsigned long long Hertz; + +static void old_Hertz_hack(void){ + unsigned long long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */ + double up_1, up_2, seconds; + unsigned long long jiffies; + unsigned h; + char *restrict savelocale; + + savelocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + do{ + FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_1); + /* uptime(&up_1, NULL); */ + FILE_TO_BUF(STAT_FILE,stat_fd); + sscanf(buf, "cpu %Lu %Lu %Lu %Lu", &user_j, &nice_j, &sys_j, &other_j); + FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_2); + /* uptime(&up_2, NULL); */ + } while((long long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */ + setlocale(LC_NUMERIC, savelocale); + jiffies = user_j + nice_j + sys_j + other_j; + seconds = (up_1 + up_2) / 2; + h = (unsigned)( (double)jiffies/seconds/smp_num_cpus ); + /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ + switch(h){ + case 9 ... 11 : Hertz = 10; break; /* S/390 (sometimes) */ + case 18 ... 22 : Hertz = 20; break; /* user-mode Linux */ + case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */ + case 48 ... 52 : Hertz = 50; break; + case 58 ... 61 : Hertz = 60; break; + case 62 ... 65 : Hertz = 64; break; /* StrongARM /Shark */ + case 95 ... 105 : Hertz = 100; break; /* normal Linux */ + case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */ + case 195 ... 204 : Hertz = 200; break; /* normal << 1 */ + case 247 ... 252 : Hertz = 250; break; + case 253 ... 260 : Hertz = 256; break; + case 393 ... 408 : Hertz = 400; break; /* normal << 2 */ + case 790 ... 808 : Hertz = 800; break; /* normal << 3 */ + case 990 ... 1010 : Hertz = 1000; break; /* ARM */ + case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */ + case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */ + default: +#ifdef HZ + Hertz = (unsigned long long)HZ; /* */ +#else + /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */ + Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL; +#endif + fprintf(stderr, "Unknown HZ value! (%d) Assume %Ld.\n", h, Hertz); + } +} + +// same as: euid != uid || egid != gid +#ifndef AT_SECURE +#define AT_SECURE 23 // secure mode boolean (true if setuid, etc.) +#endif + +#ifndef AT_CLKTCK +#define AT_CLKTCK 17 // frequency of times() +#endif + +#define NOTE_NOT_FOUND 42 + +//extern char** environ; + +/* for ELF executables, notes are pushed before environment and args */ +static unsigned long find_elf_note(unsigned long findme){ + unsigned long *ep = (unsigned long *)environ; + while(*ep++); + while(*ep){ + if(ep[0]==findme) return ep[1]; + ep+=2; + } + return NOTE_NOT_FOUND; +} + +int have_privs; + +static int check_for_privs(void){ + unsigned long rc = find_elf_note(AT_SECURE); + if(rc==NOTE_NOT_FOUND){ + // not valid to run this code after UID or GID change! + // (if needed, may use AT_UID and friends instead) + rc = geteuid() != getuid() || getegid() != getgid(); + } + return !!rc; +} + +static void init_libproc(void) __attribute__((constructor)); +static void init_libproc(void){ + have_privs = check_for_privs(); + // ought to count CPUs in /proc/stat instead of relying + // on glibc, which foolishly tries to parse /proc/cpuinfo + // + // SourceForge has an old Alpha running Linux 2.2.20 that + // appears to have a non-SMP kernel on a 2-way SMP box. + // _SC_NPROCESSORS_CONF returns 2, resulting in HZ=512 + // _SC_NPROCESSORS_ONLN returns 1, which should work OK + smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if(smp_num_cpus<1) smp_num_cpus=1; /* SPARC glibc is buggy */ + + if(linux_version_code > LINUX_VERSION(2, 4, 0)){ + Hertz = find_elf_note(AT_CLKTCK); + if(Hertz!=NOTE_NOT_FOUND) return; + fputs("2.4+ kernel w/o ELF notes? -- report this\n", stderr); + } + old_Hertz_hack(); +} + +#if 0 +/*********************************************************************** + * The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we + * recover jiffies by adding up the 4 or 5 numbers we are given. SMP kernels + * (as of pre-2.4 era) can report idle time going backwards, perhaps due + * to non-atomic reads and updates. There is no locking for these values. + */ +#ifndef NAN +#define NAN (-0.0) +#endif +#define JT unsigned long long +void eight_cpu_numbers(double *restrict uret, double *restrict nret, double *restrict sret, double *restrict iret, double *restrict wret, double *restrict xret, double *restrict yret, double *restrict zret){ + double tmp_u, tmp_n, tmp_s, tmp_i, tmp_w, tmp_x, tmp_y, tmp_z; + double scale; /* scale values to % */ + static JT old_u, old_n, old_s, old_i, old_w, old_x, old_y, old_z; + JT new_u, new_n, new_s, new_i, new_w, new_x, new_y, new_z; + JT ticks_past; /* avoid div-by-0 by not calling too often :-( */ + + tmp_w = 0.0; + new_w = 0; + tmp_x = 0.0; + new_x = 0; + tmp_y = 0.0; + new_y = 0; + tmp_z = 0.0; + new_z = 0; + + FILE_TO_BUF(STAT_FILE,stat_fd); + sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &new_u, &new_n, &new_s, &new_i, &new_w, &new_x, &new_y, &new_z); + ticks_past = (new_u+new_n+new_s+new_i+new_w+new_x+new_y+new_z)-(old_u+old_n+old_s+old_i+old_w+old_x+old_y+old_z); + if(ticks_past){ + scale = 100.0 / (double)ticks_past; + tmp_u = ( (double)new_u - (double)old_u ) * scale; + tmp_n = ( (double)new_n - (double)old_n ) * scale; + tmp_s = ( (double)new_s - (double)old_s ) * scale; + tmp_i = ( (double)new_i - (double)old_i ) * scale; + tmp_w = ( (double)new_w - (double)old_w ) * scale; + tmp_x = ( (double)new_x - (double)old_x ) * scale; + tmp_y = ( (double)new_y - (double)old_y ) * scale; + tmp_z = ( (double)new_z - (double)old_z ) * scale; + }else{ + tmp_u = NAN; + tmp_n = NAN; + tmp_s = NAN; + tmp_i = NAN; + tmp_w = NAN; + tmp_x = NAN; + tmp_y = NAN; + tmp_z = NAN; + } + SET_IF_DESIRED(uret, tmp_u); + SET_IF_DESIRED(nret, tmp_n); + SET_IF_DESIRED(sret, tmp_s); + SET_IF_DESIRED(iret, tmp_i); + SET_IF_DESIRED(wret, tmp_w); + SET_IF_DESIRED(xret, tmp_x); + SET_IF_DESIRED(yret, tmp_y); + SET_IF_DESIRED(zret, tmp_z); + old_u=new_u; + old_n=new_n; + old_s=new_s; + old_i=new_i; + old_w=new_w; + old_x=new_x; + old_y=new_y; + old_z=new_z; +} +#undef JT +#endif + +/***********************************************************************/ +void loadavg(double *restrict av1, double *restrict av5, double *restrict av15) { + double avg_1=0, avg_5=0, avg_15=0; + char *restrict savelocale; + + FILE_TO_BUF(LOADAVG_FILE,loadavg_fd); + savelocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) { + fputs("bad data in " LOADAVG_FILE "\n", stderr); + exit(1); + } + setlocale(LC_NUMERIC, savelocale); + SET_IF_DESIRED(av1, avg_1); + SET_IF_DESIRED(av5, avg_5); + SET_IF_DESIRED(av15, avg_15); +} + + static char buff[BUFFSIZE]; /* used in the procedures */ +/***********************************************************************/ + +static void crash(const char *filename) { + perror(filename); + exit(EXIT_FAILURE); +} + +/***********************************************************************/ + +static void getrunners(unsigned int *restrict running, unsigned int *restrict blocked) { + struct direct *ent; + DIR *proc; + + *running=0; + *blocked=0; + + if((proc=opendir("/proc"))==NULL) crash("/proc"); + + while(( ent=readdir(proc) )) { + char tbuf[32]; + char *cp; + int fd; + char c; + + if (!isdigit(ent->d_name[0])) continue; + sprintf(tbuf, "/proc/%s/stat", ent->d_name); + + fd = open(tbuf, O_RDONLY, 0); + if (fd == -1) continue; + memset(tbuf, '\0', sizeof tbuf); // didn't feel like checking read() + read(fd, tbuf, sizeof tbuf - 1); // need 32 byte buffer at most + close(fd); + + cp = strrchr(tbuf, ')'); + if(!cp) continue; + c = cp[2]; + + if (c=='R') { + (*running)++; + continue; + } + if (c=='D') { + (*blocked)++; + continue; + } + } + closedir(proc); +} + +/***********************************************************************/ + +void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz, + unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout, + unsigned *restrict intr, unsigned *restrict ctxt, + unsigned int *restrict running, unsigned int *restrict blocked, + unsigned int *restrict btime, unsigned int *restrict processes) { + static int fd; + unsigned long long llbuf = 0; + int need_vmstat_file = 0; + int need_proc_scan = 0; + const char* b; + buff[BUFFSIZE-1] = 0; /* ensure null termination in buffer */ + + if(fd){ + lseek(fd, 0L, SEEK_SET); + }else{ + fd = open("/proc/stat", O_RDONLY, 0); + if(fd == -1) crash("/proc/stat"); + } + read(fd,buff,BUFFSIZE-1); + *intr = 0; + *ciow = 0; /* not separated out until the 2.5.41 kernel */ + *cxxx = 0; /* not separated out until the 2.6.0-test4 kernel */ + *cyyy = 0; /* not separated out until the 2.6.0-test4 kernel */ + *czzz = 0; /* not separated out until the 2.6.11 kernel */ + + b = strstr(buff, "cpu "); + if(b) sscanf(b, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", cuse, cice, csys, cide, ciow, cxxx, cyyy, czzz); + + b = strstr(buff, "page "); + if(b) sscanf(b, "page %lu %lu", pin, pout); + else need_vmstat_file = 1; + + b = strstr(buff, "swap "); + if(b) sscanf(b, "swap %lu %lu", s_in, sout); + else need_vmstat_file = 1; + + b = strstr(buff, "intr "); + if(b) sscanf(b, "intr %Lu", &llbuf); + *intr = llbuf; + + b = strstr(buff, "ctxt "); + if(b) sscanf(b, "ctxt %Lu", &llbuf); + *ctxt = llbuf; + + b = strstr(buff, "btime "); + if(b) sscanf(b, "btime %u", btime); + + b = strstr(buff, "processes "); + if(b) sscanf(b, "processes %u", processes); + + b = strstr(buff, "procs_running "); + if(b) sscanf(b, "procs_running %u", running); + else need_proc_scan = 1; + + b = strstr(buff, "procs_blocked "); + if(b) sscanf(b, "procs_blocked %u", blocked); + else need_proc_scan = 1; + + if(need_proc_scan){ /* Linux 2.5.46 (approximately) and below */ + getrunners(running, blocked); + } + + (*running)--; // exclude vmstat itself + + if(need_vmstat_file){ /* Linux 2.5.40-bk4 and above */ + vminfo(); + *pin = vm_pgpgin; + *pout = vm_pgpgout; + *s_in = vm_pswpin; + *sout = vm_pswpout; + } +} + +/***********************************************************************/ +/* + * Copyright 1999 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +typedef struct mem_table_struct { + const char *name; /* memory type name */ + unsigned long *slot; /* slot in return struct */ +} mem_table_struct; + +static int compare_mem_table_structs(const void *a, const void *b){ + return strcmp(((const mem_table_struct*)a)->name,((const mem_table_struct*)b)->name); +} + +/* example data, following junk, with comments added: + * + * MemTotal: 61768 kB old + * MemFree: 1436 kB old + * MemShared: 0 kB old (now always zero; not calculated) + * Buffers: 1312 kB old + * Cached: 20932 kB old + * Active: 12464 kB new + * Inact_dirty: 7772 kB new + * Inact_clean: 2008 kB new + * Inact_target: 0 kB new + * Inact_laundry: 0 kB new, and might be missing too + * HighTotal: 0 kB + * HighFree: 0 kB + * LowTotal: 61768 kB + * LowFree: 1436 kB + * SwapTotal: 122580 kB old + * SwapFree: 60352 kB old + * Inactive: 20420 kB 2.5.41+ + * Dirty: 0 kB 2.5.41+ + * Writeback: 0 kB 2.5.41+ + * Mapped: 9792 kB 2.5.41+ + * Slab: 4564 kB 2.5.41+ + * Committed_AS: 8440 kB 2.5.41+ + * PageTables: 304 kB 2.5.41+ + * ReverseMaps: 5738 2.5.41+ + * SwapCached: 0 kB 2.5.??+ + * HugePages_Total: 220 2.5.??+ + * HugePages_Free: 138 2.5.??+ + * Hugepagesize: 4096 kB 2.5.??+ + */ + +/* obsolete */ +unsigned long kb_main_shared; +/* old but still kicking -- the important stuff */ +unsigned long kb_main_buffers; +unsigned long kb_main_cached; +unsigned long kb_main_free; +unsigned long kb_main_total; +unsigned long kb_swap_free; +unsigned long kb_swap_total; +/* recently introduced */ +unsigned long kb_high_free; +unsigned long kb_high_total; +unsigned long kb_low_free; +unsigned long kb_low_total; +/* 2.4.xx era */ +unsigned long kb_active; +unsigned long kb_inact_laundry; +unsigned long kb_inact_dirty; +unsigned long kb_inact_clean; +unsigned long kb_inact_target; +unsigned long kb_swap_cached; /* late 2.4 and 2.6+ only */ +/* derived values */ +unsigned long kb_swap_used; +unsigned long kb_main_used; +/* 2.5.41+ */ +unsigned long kb_writeback; +unsigned long kb_slab; +unsigned long nr_reversemaps; +unsigned long kb_committed_as; +unsigned long kb_dirty; +unsigned long kb_inactive; +unsigned long kb_mapped; +unsigned long kb_pagetables; +// seen on a 2.6.x kernel: +static unsigned long kb_vmalloc_chunk; +static unsigned long kb_vmalloc_total; +static unsigned long kb_vmalloc_used; +// seen on 2.6.24-rc6-git12 +static unsigned long kb_anon_pages; +static unsigned long kb_bounce; +static unsigned long kb_commit_limit; +static unsigned long kb_nfs_unstable; +static unsigned long kb_swap_reclaimable; +static unsigned long kb_swap_unreclaimable; + +void meminfo(void){ + char namebuf[16]; /* big enough to hold any row name */ + mem_table_struct findme = { namebuf, NULL}; + mem_table_struct *found; + char *head; + char *tail; + static const mem_table_struct mem_table[] = { + {"Active", &kb_active}, // important + {"AnonPages", &kb_anon_pages}, + {"Bounce", &kb_bounce}, + {"Buffers", &kb_main_buffers}, // important + {"Cached", &kb_main_cached}, // important + {"CommitLimit", &kb_commit_limit}, + {"Committed_AS", &kb_committed_as}, + {"Dirty", &kb_dirty}, // kB version of vmstat nr_dirty + {"HighFree", &kb_high_free}, + {"HighTotal", &kb_high_total}, + {"Inact_clean", &kb_inact_clean}, + {"Inact_dirty", &kb_inact_dirty}, + {"Inact_laundry",&kb_inact_laundry}, + {"Inact_target", &kb_inact_target}, + {"Inactive", &kb_inactive}, // important + {"LowFree", &kb_low_free}, + {"LowTotal", &kb_low_total}, + {"Mapped", &kb_mapped}, // kB version of vmstat nr_mapped + {"MemFree", &kb_main_free}, // important + {"MemShared", &kb_main_shared}, // important, but now gone! + {"MemTotal", &kb_main_total}, // important + {"NFS_Unstable", &kb_nfs_unstable}, + {"PageTables", &kb_pagetables}, // kB version of vmstat nr_page_table_pages + {"ReverseMaps", &nr_reversemaps}, // same as vmstat nr_page_table_pages + {"SReclaimable", &kb_swap_reclaimable}, // "swap reclaimable" (dentry and inode structures) + {"SUnreclaim", &kb_swap_unreclaimable}, + {"Slab", &kb_slab}, // kB version of vmstat nr_slab + {"SwapCached", &kb_swap_cached}, + {"SwapFree", &kb_swap_free}, // important + {"SwapTotal", &kb_swap_total}, // important + {"VmallocChunk", &kb_vmalloc_chunk}, + {"VmallocTotal", &kb_vmalloc_total}, + {"VmallocUsed", &kb_vmalloc_used}, + {"Writeback", &kb_writeback}, // kB version of vmstat nr_writeback + }; + const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct); + + FILE_TO_BUF(MEMINFO_FILE,meminfo_fd); + + kb_inactive = ~0UL; + + head = buf; + for(;;){ + tail = strchr(head, ':'); + if(!tail) break; + *tail = '\0'; + if(strlen(head) >= sizeof(namebuf)){ + head = tail+1; + goto nextline; + } + strcpy(namebuf,head); + found = bsearch(&findme, mem_table, mem_table_count, + sizeof(mem_table_struct), compare_mem_table_structs + ); + head = tail+1; + if(!found) goto nextline; + *(found->slot) = strtoul(head,&tail,10); +nextline: + tail = strchr(head, '\n'); + if(!tail) break; + head = tail+1; + } + if(!kb_low_total){ /* low==main except with large-memory support */ + kb_low_total = kb_main_total; + kb_low_free = kb_main_free; + } + if(kb_inactive==~0UL){ + kb_inactive = kb_inact_dirty + kb_inact_clean + kb_inact_laundry; + } + kb_swap_used = kb_swap_total - kb_swap_free; + kb_main_used = kb_main_total - kb_main_free; +} + +/*****************************************************************/ + +/* read /proc/vminfo only for 2.5.41 and above */ + +typedef struct vm_table_struct { + const char *name; /* VM statistic name */ + unsigned long *slot; /* slot in return struct */ +} vm_table_struct; + +static int compare_vm_table_structs(const void *a, const void *b){ + return strcmp(((const vm_table_struct*)a)->name,((const vm_table_struct*)b)->name); +} + +// see include/linux/page-flags.h and mm/page_alloc.c +unsigned long vm_nr_dirty; // dirty writable pages +unsigned long vm_nr_writeback; // pages under writeback +unsigned long vm_nr_pagecache; // pages in pagecache -- gone in 2.5.66+ kernels +unsigned long vm_nr_page_table_pages;// pages used for pagetables +unsigned long vm_nr_reverse_maps; // includes PageDirect +unsigned long vm_nr_mapped; // mapped into pagetables +unsigned long vm_nr_slab; // in slab +unsigned long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line) +unsigned long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line) +unsigned long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line) +unsigned long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line) +unsigned long vm_pgalloc; // page allocations +unsigned long vm_pgfree; // page freeings +unsigned long vm_pgactivate; // pages moved inactive -> active +unsigned long vm_pgdeactivate; // pages moved active -> inactive +unsigned long vm_pgfault; // total faults (major+minor) +unsigned long vm_pgmajfault; // major faults +unsigned long vm_pgscan; // pages scanned by page reclaim +unsigned long vm_pgrefill; // inspected by refill_inactive_zone +unsigned long vm_pgsteal; // total pages reclaimed +unsigned long vm_kswapd_steal; // pages reclaimed by kswapd +// next 3 as defined by the 2.5.52 kernel +unsigned long vm_pageoutrun; // times kswapd ran page reclaim +unsigned long vm_allocstall; // times a page allocator ran direct reclaim +unsigned long vm_pgrotated; // pages rotated to the tail of the LRU for immediate reclaim +// seen on a 2.6.8-rc1 kernel, apparently replacing old fields +static unsigned long vm_pgalloc_dma; // +static unsigned long vm_pgalloc_high; // +static unsigned long vm_pgalloc_normal; // +static unsigned long vm_pgrefill_dma; // +static unsigned long vm_pgrefill_high; // +static unsigned long vm_pgrefill_normal; // +static unsigned long vm_pgscan_direct_dma; // +static unsigned long vm_pgscan_direct_high; // +static unsigned long vm_pgscan_direct_normal; // +static unsigned long vm_pgscan_kswapd_dma; // +static unsigned long vm_pgscan_kswapd_high; // +static unsigned long vm_pgscan_kswapd_normal; // +static unsigned long vm_pgsteal_dma; // +static unsigned long vm_pgsteal_high; // +static unsigned long vm_pgsteal_normal; // +// seen on a 2.6.8-rc1 kernel +static unsigned long vm_kswapd_inodesteal; // +static unsigned long vm_nr_unstable; // +static unsigned long vm_pginodesteal; // +static unsigned long vm_slabs_scanned; // + +void vminfo(void){ + char namebuf[16]; /* big enough to hold any row name */ + vm_table_struct findme = { namebuf, NULL}; + vm_table_struct *found; + char *head; + char *tail; + static const vm_table_struct vm_table[] = { + {"allocstall", &vm_allocstall}, + {"kswapd_inodesteal", &vm_kswapd_inodesteal}, + {"kswapd_steal", &vm_kswapd_steal}, + {"nr_dirty", &vm_nr_dirty}, // page version of meminfo Dirty + {"nr_mapped", &vm_nr_mapped}, // page version of meminfo Mapped + {"nr_page_table_pages", &vm_nr_page_table_pages},// same as meminfo PageTables + {"nr_pagecache", &vm_nr_pagecache}, // gone in 2.5.66+ kernels + {"nr_reverse_maps", &vm_nr_reverse_maps}, // page version of meminfo ReverseMaps GONE + {"nr_slab", &vm_nr_slab}, // page version of meminfo Slab + {"nr_unstable", &vm_nr_unstable}, + {"nr_writeback", &vm_nr_writeback}, // page version of meminfo Writeback + {"pageoutrun", &vm_pageoutrun}, + {"pgactivate", &vm_pgactivate}, + {"pgalloc", &vm_pgalloc}, // GONE (now separate dma,high,normal) + {"pgalloc_dma", &vm_pgalloc_dma}, + {"pgalloc_high", &vm_pgalloc_high}, + {"pgalloc_normal", &vm_pgalloc_normal}, + {"pgdeactivate", &vm_pgdeactivate}, + {"pgfault", &vm_pgfault}, + {"pgfree", &vm_pgfree}, + {"pginodesteal", &vm_pginodesteal}, + {"pgmajfault", &vm_pgmajfault}, + {"pgpgin", &vm_pgpgin}, // important + {"pgpgout", &vm_pgpgout}, // important + {"pgrefill", &vm_pgrefill}, // GONE (now separate dma,high,normal) + {"pgrefill_dma", &vm_pgrefill_dma}, + {"pgrefill_high", &vm_pgrefill_high}, + {"pgrefill_normal", &vm_pgrefill_normal}, + {"pgrotated", &vm_pgrotated}, + {"pgscan", &vm_pgscan}, // GONE (now separate direct,kswapd and dma,high,normal) + {"pgscan_direct_dma", &vm_pgscan_direct_dma}, + {"pgscan_direct_high", &vm_pgscan_direct_high}, + {"pgscan_direct_normal",&vm_pgscan_direct_normal}, + {"pgscan_kswapd_dma", &vm_pgscan_kswapd_dma}, + {"pgscan_kswapd_high", &vm_pgscan_kswapd_high}, + {"pgscan_kswapd_normal",&vm_pgscan_kswapd_normal}, + {"pgsteal", &vm_pgsteal}, // GONE (now separate dma,high,normal) + {"pgsteal_dma", &vm_pgsteal_dma}, + {"pgsteal_high", &vm_pgsteal_high}, + {"pgsteal_normal", &vm_pgsteal_normal}, + {"pswpin", &vm_pswpin}, // important + {"pswpout", &vm_pswpout}, // important + {"slabs_scanned", &vm_slabs_scanned}, + }; + const int vm_table_count = sizeof(vm_table)/sizeof(vm_table_struct); + + vm_pgalloc = 0; + vm_pgrefill = 0; + vm_pgscan = 0; + vm_pgsteal = 0; + + FILE_TO_BUF(VMINFO_FILE,vminfo_fd); + + head = buf; + for(;;){ + tail = strchr(head, ' '); + if(!tail) break; + *tail = '\0'; + if(strlen(head) >= sizeof(namebuf)){ + head = tail+1; + goto nextline; + } + strcpy(namebuf,head); + found = bsearch(&findme, vm_table, vm_table_count, + sizeof(vm_table_struct), compare_vm_table_structs + ); + head = tail+1; + if(!found) goto nextline; + *(found->slot) = strtoul(head,&tail,10); +nextline: + +//if(found) fprintf(stderr,"%s=%d\n",found->name,*(found->slot)); +//else fprintf(stderr,"%s not found\n",findme.name); + + tail = strchr(head, '\n'); + if(!tail) break; + head = tail+1; + } + if(!vm_pgalloc) + vm_pgalloc = vm_pgalloc_dma + vm_pgalloc_high + vm_pgalloc_normal; + if(!vm_pgrefill) + vm_pgrefill = vm_pgrefill_dma + vm_pgrefill_high + vm_pgrefill_normal; + if(!vm_pgscan) + vm_pgscan = vm_pgscan_direct_dma + vm_pgscan_direct_high + vm_pgscan_direct_normal + + vm_pgscan_kswapd_dma + vm_pgscan_kswapd_high + vm_pgscan_kswapd_normal; + if(!vm_pgsteal) + vm_pgsteal = vm_pgsteal_dma + vm_pgsteal_high + vm_pgsteal_normal; +} + +/////////////////////////////////////////////////////////////////////// +// based on Fabian Frederick's /proc/diskstats parser + + +unsigned int getpartitions_num(struct disk_stat *disks, int ndisks){ + int i=0; + int partitions=0; + + for (i=0;i +#include +#include "procps.h" + +EXTERN_C_BEGIN + +extern unsigned long long Hertz; /* clock tick frequency */ +extern long smp_num_cpus; /* number of CPUs */ +extern int have_privs; /* boolean, true if setuid or similar */ + +#if 0 +#define JT double +extern void eight_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret, JT *wret, JT *xret, JT *yret, JT *zret); +#undef JT +#endif + +extern int uptime (double *uptime_secs, double *idle_secs); +extern void loadavg(double *av1, double *av5, double *av15); + + +/* obsolete */ +extern unsigned long kb_main_shared; +/* old but still kicking -- the important stuff */ +extern unsigned long kb_main_buffers; +extern unsigned long kb_main_cached; +extern unsigned long kb_main_free; +extern unsigned long kb_main_total; +extern unsigned long kb_swap_free; +extern unsigned long kb_swap_total; +/* recently introduced */ +extern unsigned long kb_high_free; +extern unsigned long kb_high_total; +extern unsigned long kb_low_free; +extern unsigned long kb_low_total; +/* 2.4.xx era */ +extern unsigned long kb_active; +extern unsigned long kb_inact_laundry; // grrr... +extern unsigned long kb_inact_dirty; +extern unsigned long kb_inact_clean; +extern unsigned long kb_inact_target; +extern unsigned long kb_swap_cached; /* late 2.4+ */ +/* derived values */ +extern unsigned long kb_swap_used; +extern unsigned long kb_main_used; +/* 2.5.41+ */ +extern unsigned long kb_writeback; +extern unsigned long kb_slab; +extern unsigned long nr_reversemaps; +extern unsigned long kb_committed_as; +extern unsigned long kb_dirty; +extern unsigned long kb_inactive; +extern unsigned long kb_mapped; +extern unsigned long kb_pagetables; + +#define BUFFSIZE (64*1024) +typedef unsigned long long jiff; +extern void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz, + unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout, + unsigned *restrict intr, unsigned *restrict ctxt, + unsigned int *restrict running, unsigned int *restrict blocked, + unsigned int *restrict btime, unsigned int *restrict processes); + +extern void meminfo(void); + + +extern unsigned long vm_nr_dirty; +extern unsigned long vm_nr_writeback; +extern unsigned long vm_nr_pagecache; +extern unsigned long vm_nr_page_table_pages; +extern unsigned long vm_nr_reverse_maps; +extern unsigned long vm_nr_mapped; +extern unsigned long vm_nr_slab; +extern unsigned long vm_pgpgin; +extern unsigned long vm_pgpgout; +extern unsigned long vm_pswpin; +extern unsigned long vm_pswpout; +extern unsigned long vm_pgalloc; +extern unsigned long vm_pgfree; +extern unsigned long vm_pgactivate; +extern unsigned long vm_pgdeactivate; +extern unsigned long vm_pgfault; +extern unsigned long vm_pgmajfault; +extern unsigned long vm_pgscan; +extern unsigned long vm_pgrefill; +extern unsigned long vm_pgsteal; +extern unsigned long vm_kswapd_steal; +extern unsigned long vm_pageoutrun; +extern unsigned long vm_allocstall; + +extern void vminfo(void); + +typedef struct disk_stat{ + unsigned long long reads_sectors; + unsigned long long written_sectors; + char disk_name [16]; + unsigned inprogress_IO; + unsigned merged_reads; + unsigned merged_writes; + unsigned milli_reading; + unsigned milli_spent_IO; + unsigned milli_writing; + unsigned partitions; + unsigned reads; + unsigned weighted_milli_spent_IO; + unsigned writes; +}disk_stat; + +typedef struct partition_stat{ + char partition_name [16]; + unsigned long long reads_sectors; + unsigned parent_disk; // index into a struct disk_stat array + unsigned reads; + unsigned writes; + unsigned requested_writes; +}partition_stat; + +extern unsigned int getpartitions_num(struct disk_stat *disks, int ndisks); +extern unsigned int getdiskstat (struct disk_stat**,struct partition_stat**); + +typedef struct slab_cache{ + char name[48]; + unsigned active_objs; + unsigned num_objs; + unsigned objsize; + unsigned objperslab; +}slab_cache; + +extern unsigned int getslabinfo (struct slab_cache**); + +extern unsigned get_pid_digits(void) FUNCTION; + +EXTERN_C_END +#endif /* SYSINFO_H */ diff --git a/proc/version.c b/proc/version.c new file mode 100644 index 0000000..69bae4f --- /dev/null +++ b/proc/version.c @@ -0,0 +1,49 @@ +/* Suite version information for procps utilities + * Copyright (c) 1995 Martin Schulze + * Ammended by cblake to only export the function symbol. + * + * Modified by Albert Cahalan, ????-2003 + * + * Redistributable under the terms of the + * GNU Library General Public License; see COPYING + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include "version.h" + +#ifdef MINORVERSION +const char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION; +#else +const char procps_version[] = "procps version " VERSION "." SUBVERSION; +#endif + +void display_version(void) { + fprintf(stdout, "%s\n", procps_version); +} + +/* Linux kernel version information for procps utilities + * Copyright (c) 1996 Charles Blake + */ +#include + +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) + +int linux_version_code; + +static void init_Linux_version(void) __attribute__((constructor)); +static void init_Linux_version(void) { + static struct utsname uts; + int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */ + + if (uname(&uts) == -1) /* failure implies impending death */ + exit(1); + if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3) + fprintf(stderr, /* *very* unlikely to happen by accident */ + "Non-standard uts for running kernel:\n" + "release %s=%d.%d.%d gives version code %d\n", + uts.release, x, y, z, LINUX_VERSION(x,y,z)); + linux_version_code = LINUX_VERSION(x, y, z); +} diff --git a/proc/version.h b/proc/version.h new file mode 100644 index 0000000..3769b84 --- /dev/null +++ b/proc/version.h @@ -0,0 +1,31 @@ +#ifndef PROC_VERSION_H +#define PROC_VERSION_H + +#include "procps.h" + +/* Suite version information for procps utilities + * Copyright (c) 1995 Martin Schulze + * Linux kernel version information for procps utilities + * Copyright (c) 1996 Charles Blake + * Distributable under the terms of the GNU Library General Public License + * + * Copyright 2002 Albert Cahalan + */ + +EXTERN_C_BEGIN + +extern void display_version(void); /* display suite version */ +extern const char procps_version[]; /* global buf for suite version */ + +extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE + in /usr/include/linux/version.h */ + +/* Convenience macros for composing/decomposing version codes */ +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) +#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF) +#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF) +#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF) + +EXTERN_C_END + +#endif /* PROC_VERSION_H */ diff --git a/proc/wchan.h b/proc/wchan.h new file mode 100644 index 0000000..93d4e70 --- /dev/null +++ b/proc/wchan.h @@ -0,0 +1,16 @@ +#ifndef PROCPS_PROC_WCHAN_H +#define PROCPS_PROC_WCHAN_H + +#include "procps.h" + +EXTERN_C_BEGIN + +typedef void (*message_fn)(const char *restrict, ...) __attribute__((format(printf,1,2))); + +extern const char * lookup_wchan(unsigned KLONG address, unsigned pid); +extern int open_psdb(const char *restrict override); +extern int open_psdb_message(const char *restrict override, message_fn message); + +EXTERN_C_END + +#endif diff --git a/proc/whattime.c b/proc/whattime.c new file mode 100644 index 0000000..d2785a8 --- /dev/null +++ b/proc/whattime.c @@ -0,0 +1,87 @@ +/* This is a trivial uptime program. I hereby release this program + * into the public domain. I disclaim any responsibility for this + * program --- use it at your own risk. (as if there were any.. ;-) + * -michaelkjohnson (johnsonm@sunsite.unc.edu) + * + * Modified by Larry Greenfield to give a more traditional output, + * count users, etc. (greenfie@gauss.rutgers.edu) + * + * Modified by mkj again to fix a few tiny buglies. + * + * Modified by J. Cowley to add printing the uptime message to a + * string (for top) and to optimize file handling. 19 Mar 1993. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "whattime.h" +#include "sysinfo.h" + +static char buf[128]; +static double av[3]; + +char *sprint_uptime(void) { + struct utmp *utmpstruct; + int upminutes, uphours, updays; + int pos; + struct tm *realtime; + time_t realseconds; + int numuser; + double uptime_secs, idle_secs; + +/* first get the current time */ + + time(&realseconds); + realtime = localtime(&realseconds); + pos = sprintf(buf, " %02d:%02d:%02d ", + realtime->tm_hour, realtime->tm_min, realtime->tm_sec); + +/* read and calculate the amount of uptime */ + + uptime(&uptime_secs, &idle_secs); + + updays = (int) uptime_secs / (60*60*24); + strcat (buf, "up "); + pos += 3; + if (updays) + pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : ""); + upminutes = (int) uptime_secs / 60; + uphours = upminutes / 60; + uphours = uphours % 24; + upminutes = upminutes % 60; + if(uphours) + pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes); + else + pos += sprintf(buf + pos, "%d min, ", upminutes); + +/* count the number of users */ + + numuser = 0; + setutent(); + while ((utmpstruct = getutent())) { + if ((utmpstruct->ut_type == USER_PROCESS) && + (utmpstruct->ut_name[0] != '\0')) + numuser++; + } + endutent(); + + pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s"); + + loadavg(&av[0], &av[1], &av[2]); + + pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f", + av[0], av[1], av[2]); + + return buf; +} + +void print_uptime(void) { + printf("%s\n", sprint_uptime()); +} diff --git a/proc/whattime.h b/proc/whattime.h new file mode 100644 index 0000000..891ccd3 --- /dev/null +++ b/proc/whattime.h @@ -0,0 +1,13 @@ +#ifndef PROC_WHATTIME_H +#define PROC_WHATTIME_H + +#include "procps.h" + +EXTERN_C_BEGIN + +extern void print_uptime(void); +extern char *sprint_uptime(void); + +EXTERN_C_END + +#endif diff --git a/procps.lsm b/procps.lsm new file mode 100644 index 0000000..f2b8064 --- /dev/null +++ b/procps.lsm @@ -0,0 +1,15 @@ +Begin4 +Title: procps +Version: 3.2.7 +Entered-date: 2006-06-25 +Description: Linux system utilities +Keywords: procps /proc libproc sysctl pmap ps uptime tload slabtop + free w top vmstat watch skill snice kill pgrep pkill +Author: Albert Cahalan, Michael K. Johnson, Jim Warner, etc. +Maintained-by: various +Primary-site: http://procps.sf.net/ + 281kB procps-3.2.7.tar.gz +Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html + 281kB procps-3.2.7.tar.gz +Copying-policy: mixed +End diff --git a/procps.spec b/procps.spec new file mode 100644 index 0000000..5ac1be9 --- /dev/null +++ b/procps.spec @@ -0,0 +1,52 @@ +URL: http://procps.sf.net/ +Summary: System and process monitoring utilities +Name: procps +%define major_version 3 +%define minor_version 2 +%define revision 8 +%define version %{major_version}.%{minor_version}.%{revision} +Version: %{version} +Release: 1 +License: LGPL, GPL, BSD-like +Group: Applications/System +Source: http://procps.sf.net/procps-%{version}.tar.gz +BuildRoot: %{_tmppath}/procps-root +Packager: + +%description +The procps package contains a set of system utilities which provide +system information. Procps includes ps, free, sysctl, skill, snice, +tload, top, uptime, vmstat, w, and watch. You need some of these. + +%prep +%setup + +%build +make SKIP="/bin/kill /usr/share/man/man1/kill.1" CFLAGS="$RPM_OPT_FLAGS" + +%install +rm -rf $RPM_BUILD_ROOT +make SKIP="/bin/kill /usr/share/man/man1/kill.1" DESTDIR=$RPM_BUILD_ROOT ldconfig=echo install="install -D" lib="$RPM_BUILD_ROOT/%{_lib}/" install + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +# add libproc to the cache +/sbin/ldconfig + +%files +%defattr(0644,root,root,755) +%doc NEWS BUGS TODO COPYING COPYING.LIB README.top README AUTHORS sysctl.conf +%attr(555,root,root) /lib*/libproc*.so* +%attr(555,root,root) /bin/* +%attr(555,root,root) /sbin/* +%attr(555,root,root) /usr/bin/* + +%attr(0644,root,root) /usr/share/man/man1/* +%attr(0644,root,root) /usr/share/man/man5/* +%attr(0644,root,root) /usr/share/man/man8/* + +%changelog +* Fri Apr 14 09:23:45 PDT 2006 Jesse Brandeburg +- fix missing trailing slash in %install to fix builds on x86_64 diff --git a/ps/COPYING b/ps/COPYING new file mode 100644 index 0000000..92b8903 --- /dev/null +++ b/ps/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ps/HACKING b/ps/HACKING new file mode 100644 index 0000000..ffae92c --- /dev/null +++ b/ps/HACKING @@ -0,0 +1,53 @@ +Warning: + +This code must corrctly handle lots of picky little details to meet +the Unix98 standard while simultaneously being as compatible as +possible with the original Linux ps. Don't "fix" something without +considering the impact on all the special-case code. For example, +the "tty" format _must_ use "TT" as the header, even though the SysV +output formats _must_ use "TTY". + +File overview: + +display.c main(), debug code, iterates over processes +escape.c Does stuff like \202 and < to command and environment. +global.c Data + code to init it. +help.c Help message. +output.c Giant tables and lots of output functions. +parser.c Initial command parsing. +select.c want_this_proc() checks a process against flags & lists +sortformat.c Parses sort & format specifier lists. Picks output format. +stacktrace.c Debug code, not normally used. +../proc/* Library used to gather data. +regression Regression tests that ought to be run. +common.h Lots of interesting stuff. +Makefile Makefile +p Script used to test ps when the library is not installed. +utf Empty file used to test "ps ut?" unmangling behavior. +ps.1 Man page. + +Compiling: + +Whatever you do, don't trust the Makefiles. They are severely broken. +You can touch top.h and top won't be recompiled, or you can change +library files and discover that the library and programs won't be +recompiled. + +Operation: + +Unless the personality forces BSD parsing, parser.c tries to parse the +command line as a mixed BSD+SysV+Gnu mess. On failure, BSD parsing is +attempted. If BSD parsing fails _after_ SysV parsing has been attempted, +the error message comes from the original SysV parse. + +Control goes to sortformat.c, which must pick apart ambiguous options +like "O". Failure can reset the whole program and set PER_FORCE_BSD, +which means a second trip through parser.c and sortformat.c. + +The choice of output format happens in sortformat.c. There is a switch() +with all the valid format_flags combinations. The SysV and default +options are NULL (unless overridden by personality), which causes a +trip through SysV output format generation hackery. Note that the +default format always goes through there, even if it is for BSD. +Formats that came from the switch() (generally BSD, plus overrides) +get mangled a bit to support various SysV output modifiers. diff --git a/ps/TRANSLATION b/ps/TRANSLATION new file mode 100644 index 0000000..ff8df84 --- /dev/null +++ b/ps/TRANSLATION @@ -0,0 +1,39 @@ +Initially I only want to translate the --help output and man page. +Common error messages would be next on the list. I want to avoid +run-time overhead and bloat. + +Translations of the --help output should not be longer than 22 lines long. +Feel free to leave out the less useful options to save space. (not even +the English help text has all the options) + +I think these are the most important options: + +*** selection *** +-C by command name list +-G by real group ID list (supports names) +-U by real user ID list (supports names) +-u by effective user ID list (supports names) +-e all processes +-p by process ID list + +*** output *** +--no-heading No header line. +-o,o user-defined output +-j,j job control format +-l,l long format +-f full format +s signal format +u user-oriented format +--forest ASCII art forest (process hierarchy) +c show true command name + +List of man page translators: + +de Wed Jan 10 19:09:15 2001 by Martin Schulze +es 19 Jan 1999 by Diego Sevilla Ruiz (dsevilla@ditec.um.es) +fr 09/06/1997 par Christophe Blaess (ccb@club-internet.fr) +hu Horv#th Andr#s (the '#' is 'a' w/ '/') +it Traduzione in italiano di Giovanni Bortolozzo +it Revisione parziale di Daniele Giacomini 30/03/1999 +ja Tue Nov 14 2000 by NAKANO Takeo +nl diff --git a/ps/common.h b/ps/common.h new file mode 100644 index 0000000..0b47d90 --- /dev/null +++ b/ps/common.h @@ -0,0 +1,337 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#ifndef PROCPS_PS_H +#define PROCPS_PS_H + +#include "../proc/procps.h" +#include "../proc/escape.h" +#include "../proc/readproc.h" + +#if 0 +#define trace(args...) printf(## args) +#else +#define trace(args...) +#endif + + +/***************** GENERAL DEFINE ********************/ + + +/* selection list */ +#define SEL_RUID 1 +#define SEL_EUID 2 +#define SEL_SUID 3 +#define SEL_FUID 4 +#define SEL_RGID 5 +#define SEL_EGID 6 +#define SEL_SGID 7 +#define SEL_FGID 8 +#define SEL_PGRP 9 +#define SEL_PID 10 +#define SEL_TTY 11 +#define SEL_SESS 12 +#define SEL_COMM 13 +#define SEL_PPID 14 + +/* Since an enum could be smashed by a #define, it would be bad. */ +#define U98 0 /* Unix98 standard */ /* This must be 0 */ +#define XXX 1 /* Common extension */ +#define DEC 2 /* Digital Unix */ +#define AIX 3 /* AIX */ +#define SCO 4 /* SCO */ +#define LNX 5 /* Linux original :-) */ +#define BSD 6 /* FreeBSD and OpenBSD */ +#define SUN 7 /* SunOS 5 (Solaris) */ +#define HPU 8 /* HP-UX */ +#define SGI 9 /* Irix */ +#define SOE 10 /* IBM's S/390 OpenEdition */ +#define TST 11 /* test code */ + +/* + * Try not to overflow the output buffer: + * 32 pages for env+cmd + * 64 kB pages on IA-64 + * 4 chars for "\377", or 1 when mangling to '?' (ESC_STRETCH) + * plus some slack for other stuff + * That is about 8.5 MB on IA-64, or 0.6 MB on i386 + * + * Sadly, current kernels only supply one page of env/command data. + * The buffer is now protected with a guard page, and via other means + * to avoid hitting the guard page. + */ + +/* output buffer size */ +#define OUTBUF_SIZE (2 * 64*1024 * ESC_STRETCH) + +/******************* PS DEFINE *******************/ + +// Column flags +// Justification control for flags field comes first. +#define CF_JUST_MASK 0x0f +// CF_AIXHACK 0 +#define CF_USER 1 // left if text, right if numeric +#define CF_LEFT 2 +#define CF_RIGHT 3 +#define CF_UNLIMITED 4 +#define CF_WCHAN 5 // left if text, right if numeric +#define CF_SIGNAL 6 // right in 9, or 16 if screen_cols>107 +// Then the other flags +#define CF_PIDMAX 0x00000010 // react to pid_max +// Only one allowed; use separate bits to catch errors. +#define CF_PRINT_THREAD_ONLY 0x10000000 +#define CF_PRINT_PROCESS_ONLY 0x20000000 +#define CF_PRINT_EVERY_TIME 0x40000000 +#define CF_PRINT_AS_NEEDED 0x80000000 // means we have no clue, so assume EVERY TIME +#define CF_PRINT_MASK 0xf0000000 + +#define needs_for_select (PROC_FILLSTAT | PROC_FILLSTATUS) + +/* thread_flags */ +#define TF_B_H 0x0001 +#define TF_B_m 0x0002 +#define TF_U_m 0x0004 +#define TF_U_T 0x0008 +#define TF_U_L 0x0010 +#define TF_show_proc 0x0100 // show the summary line +#define TF_show_task 0x0200 // show the per-thread lines +#define TF_show_both 0x0400 // distinct proc/task format lists +#define TF_loose_tasks 0x0800 // let sorting break up task groups (BSDish) +#define TF_no_sort 0x1000 // don't know if thread-grouping should survive a sort +#define TF_no_forest 0x2000 // don't see how to do threads w/ forest option +#define TF_must_use 0x4000 // options only make sense if LWP/SPID column added + +/* personality control flags */ +#define PER_BROKEN_o 0x0001 +#define PER_BSD_h 0x0002 +#define PER_BSD_m 0x0004 +#define PER_IRIX_l 0x0008 +#define PER_FORCE_BSD 0x0010 +#define PER_GOOD_o 0x0020 +#define PER_OLD_m 0x0040 +#define PER_NO_DEFAULT_g 0x0080 +#define PER_ZAP_ADDR 0x0100 +#define PER_SANE_USER 0x0200 +#define PER_HPUX_x 0x0400 +#define PER_SVR4_x 0x0800 +#define PER_BSD_COLS 0x1000 +#define PER_UNIX_COLS 0x2000 + +/* Simple selections by bit mask */ +#define SS_B_x 0x01 +#define SS_B_g 0x02 +#define SS_U_d 0x04 +#define SS_U_a 0x08 +#define SS_B_a 0x10 + +/* predefined format flags such as: -l -f l u s -j */ +#define FF_Uf 0x0001 /* -f */ +#define FF_Uj 0x0002 /* -j */ +#define FF_Ul 0x0004 /* -l */ +#define FF_Bj 0x0008 /* j */ +#define FF_Bl 0x0010 /* l */ +#define FF_Bs 0x0020 /* s */ +#define FF_Bu 0x0040 /* u */ +#define FF_Bv 0x0080 /* v */ +#define FF_LX 0x0100 /* X */ +#define FF_Lm 0x0200 /* m */ /* overloaded: threads, sort, format */ +#define FF_Fc 0x0400 /* --context */ /* Flask security context format */ + +/* predefined format modifier flags such as: -l -f l u s -j */ +#define FM_c 0x0001 /* -c */ +#define FM_j 0x0002 /* -j */ /* only set when !sysv_j_format */ +#define FM_y 0x0004 /* -y */ +//#define FM_L 0x0008 /* -L */ +#define FM_P 0x0010 /* -P */ +#define FM_M 0x0020 /* -M */ +//#define FM_T 0x0040 /* -T */ +#define FM_F 0x0080 /* -F */ /* -F also sets the regular -f flags */ + +/* sorting & formatting */ +/* U,B,G is Unix,BSD,Gnu and then there is the option itself */ +#define SF_U_O 1 +#define SF_U_o 2 +#define SF_B_O 3 +#define SF_B_o 4 +#define SF_B_m 5 /* overloaded: threads, sort, format */ +#define SF_G_sort 6 +#define SF_G_format 7 + +/* headers */ +#define HEAD_SINGLE 0 /* default, must be 0 */ +#define HEAD_NONE 1 +#define HEAD_MULTI 2 + + +/********************** GENERAL TYPEDEF *******************/ + +/* Other fields that might be useful: + * + * char *name; user-defined column name (format specification) + * int reverse; sorting in reverse (sort specification) + * + * name in place of u + * reverse in place of n + */ + +typedef union sel_union { + pid_t pid; + pid_t ppid; + uid_t uid; + gid_t gid; + dev_t tty; + char cmd[16]; /* this is _not_ \0 terminated */ +} sel_union; + +typedef struct selection_node { + struct selection_node *next; + sel_union *u; /* used if selection type has a list of values */ + int n; /* used if selection type has a list of values */ + int typecode; +} selection_node; + +typedef struct sort_node { + struct sort_node *next; + int (*sr)(const proc_t* P, const proc_t* Q); /* sort function */ + int reverse; /* can sort backwards */ + int typecode; + int need; +} sort_node; + +typedef struct format_node { + struct format_node *next; + char *name; /* user can override default name */ + int (*pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function +/* int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */ + int width; + int need; + int vendor; /* Vendor that invented this */ + int flags; + int typecode; +} format_node; + +typedef struct format_struct { + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ + int (* const pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function + int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */ + const int width; + const int need; /* data we will need (files to read, etc.) */ + const int vendor; /* Where does this come from? */ + const int flags; +} format_struct; + +/* though ps-specific, needed by general file */ +typedef struct macro_struct { + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ +} macro_struct; + +/**************** PS TYPEDEF ***********************/ + +typedef struct aix_struct { + const int desc; /* 1-character format code */ + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ +} aix_struct; + +typedef struct shortsort_struct { + const int desc; /* 1-character format code */ + const char *spec; /* format specifier */ +} shortsort_struct; + +/* Save these options for later: -o o -O O --format --sort */ +typedef struct sf_node { + struct sf_node *next; /* next arg */ + format_node *f_cooked; /* convert each arg alone, then merge */ + sort_node *s_cooked; /* convert each arg alone, then merge */ + char *sf; + int sf_code; +} sf_node; + +/********************* UNDECIDED GLOBALS **************/ + +/* output.c */ +extern void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt); +extern void print_format_specifiers(void); +extern const aix_struct *search_aix_array(const int findme); +extern const shortsort_struct *search_shortsort_array(const int findme); +extern const format_struct *search_format_array(const char *findme); +extern const macro_struct *search_macro_array(const char *findme); +extern void init_output(void); +extern int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp); + +/* global.c */ +extern void reset_global(void); + +/* global.c */ +extern int all_processes; +extern const char *bsd_j_format; +extern const char *bsd_l_format; +extern const char *bsd_s_format; +extern const char *bsd_u_format; +extern const char *bsd_v_format; +extern int bsd_c_option; +extern int bsd_e_option; +extern uid_t cached_euid; +extern dev_t cached_tty; +extern char forest_prefix[4 * 32*1024 + 100]; +extern int forest_type; +extern unsigned format_flags; /* -l -f l u s -j... */ +extern format_node *format_list; /* digested formatting options */ +extern unsigned format_modifiers; /* -c -j -y -P -L... */ +extern int header_gap; +extern int header_type; /* none, single, multi... */ +extern int include_dead_children; +extern int lines_to_next_header; +extern int max_line_width; +extern const char *namelist_file; +extern int negate_selection; +extern int page_size; // "int" for math reasons? +extern unsigned personality; +extern int prefer_bsd_defaults; +extern int running_only; +extern int screen_cols; +extern int screen_rows; +extern unsigned long seconds_since_boot; +extern selection_node *selection_list; +extern unsigned simple_select; +extern sort_node *sort_list; +extern const char *sysv_f_format; +extern const char *sysv_fl_format; +extern const char *sysv_j_format; +extern const char *sysv_l_format; +extern unsigned thread_flags; +extern int unix_f_option; +extern int user_is_number; +extern int wchan_is_number; + +/************************* PS GLOBALS *********************/ + +/* sortformat.c */ +extern int defer_sf_option(const char *arg, int source); +extern const char *process_sf_options(int localbroken); +extern void reset_sortformat(void); + +/* select.c */ +extern int want_this_proc(proc_t *buf); +extern const char *select_bits_setup(void); + +/* help.c */ +extern const char *help_message; + +/* global.c */ +extern void self_info(void); + +/* parser.c */ +extern int arg_parse(int argc, char *argv[]); + +#endif diff --git a/ps/display.c b/ps/display.c new file mode 100644 index 0000000..4574b9c --- /dev/null +++ b/ps/display.c @@ -0,0 +1,594 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include + +#if (__GNU_LIBRARY__ >= 6) +# include +#endif + +/* username lookups */ +#include +#include +#include + +/* major/minor number */ +#include + +#include /* catch signals */ + +#include "common.h" +#include "../proc/wchan.h" +#include "../proc/version.h" +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "../proc/sig.h" + +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif + +/* just reports a crash */ +static void signal_handler(int signo){ + if(signo==SIGPIPE) _exit(0); /* "ps | head" will cause this */ + /* fprintf() is not reentrant, but we _exit() anyway */ + fprintf(stderr, + "\n\n" + "Signal %d (%s) caught by ps (%s).\n" + "Please send bug reports to or \n", + signo, + signal_number_to_name(signo), + procps_version + ); + _exit(signo+128); +} + +///////////////////////////////////////////////////////////////////////////////////// +#undef DEBUG +#ifdef DEBUG +void init_stack_trace(char *prog_name); + +#include + +void hex_dump(void *vp){ + char *charlist; + int i = 0; + int line = 45; + char *cp = (char *)vp; + + while(line--){ + printf("%8lx ", (unsigned long)cp); + charlist = cp; + cp += 16; + for(i=0; i<16; i++){ + if((charlist[i]>31) && (charlist[i]<127)){ + printf("%c", charlist[i]); + }else{ + printf("."); + } + } + printf(" "); + for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i]))); + printf("\n"); + i=0; + } +} + +static void show_tgid(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d,", data[n].tgid); + } + printf("%d\n", data[0].tgid); +} + +static void show_uid(char *s, int n, sel_union *data){ + struct passwd *pw_data; + printf("%s ", s); + while(--n){ + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s,", pw_data->pw_name); + else printf("%d,", data[n].uid); + } + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s\n", pw_data->pw_name); + else printf("%d\n", data[n].uid); +} + +static void show_gid(char *s, int n, sel_union *data){ + struct group *gr_data; + printf("%s ", s); + while(--n){ + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s,", gr_data->gr_name); + else printf("%d,", data[n].gid); + } + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s\n", gr_data->gr_name); + else printf("%d\n", data[n].gid); +} + +static void show_tty(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty)); + } + printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty)); +} + +static void show_cmd(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%.8s,", data[n].cmd); + } + printf("%.8s\n", data[0].cmd); +} + +static void arg_show(void){ + selection_node *walk = selection_list; + while(walk){ + switch(walk->typecode){ + case SEL_RUID: show_uid("RUID", walk->n, walk->u); break; + case SEL_EUID: show_uid("EUID", walk->n, walk->u); break; + case SEL_SUID: show_uid("SUID", walk->n, walk->u); break; + case SEL_FUID: show_uid("FUID", walk->n, walk->u); break; + case SEL_RGID: show_gid("RGID", walk->n, walk->u); break; + case SEL_EGID: show_gid("EGID", walk->n, walk->u); break; + case SEL_SGID: show_gid("SGID", walk->n, walk->u); break; + case SEL_FGID: show_gid("FGID", walk->n, walk->u); break; + case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break; + case SEL_PID : show_pid("PID ", walk->n, walk->u); break; + case SEL_PPID: show_pid("PPID", walk->n, walk->u); break; + case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break; + case SEL_SESS: show_pid("SESS", walk->n, walk->u); break; + case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break; + default: printf("Garbage typecode value!\n"); + } + walk = walk->next; + } +} + +#endif +////////////////////////////////////////////////////////////////////////// + + +/***** check the header */ +/* Unix98: must not print empty header */ +static void check_headers(void){ + format_node *walk = format_list; + int head_normal = 0; + if(header_type==HEAD_MULTI){ + header_gap = screen_rows-1; /* true BSD */ + return; + } + if(header_type==HEAD_NONE){ + lines_to_next_header = -1; /* old Linux */ + return; + } + while(walk){ + if(!*(walk->name)){ + walk = walk->next; + continue; + } + if(walk->pr){ + head_normal++; + walk = walk->next; + continue; + } + walk = walk->next; + } + if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */ +} + +/***** check sort needs */ +/* see what files need to be read, etc. */ +static unsigned check_sort_needs(sort_node *walk){ + unsigned needs = 0; + while(walk){ + needs |= walk->need; + walk = walk->next; + } + return needs; +} + +/***** check needs */ +/* see what files need to be read, etc. */ +static unsigned collect_format_needs(format_node *walk){ + unsigned needs = 0; + while(walk){ + needs |= walk->need; + walk = walk->next; + } + return needs; +} + +static format_node *proc_format_list; +static format_node *task_format_list; + +static unsigned needs_for_threads; +static unsigned needs_for_sort; +static unsigned proc_format_needs; +static unsigned task_format_needs; + +#define needs_for_format (proc_format_needs|task_format_needs) + +#define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM) + +/***** munge lists and determine openproc() flags */ +static void lists_and_needs(void){ + check_headers(); + + // only care about the difference when showing both + if(thread_flags & TF_show_both){ + format_node pfn, tfn; // junk, to handle special case at begin of list + format_node *walk = format_list; + format_node *p_end = &pfn; + format_node *t_end = &tfn; + while(walk){ + format_node *new = malloc(sizeof(format_node)); + memcpy(new,walk,sizeof(format_node)); + p_end->next = walk; + t_end->next = new; + p_end = walk; + t_end = new; + switch(walk->flags & CF_PRINT_MASK){ + case CF_PRINT_THREAD_ONLY: + p_end->pr = pr_nop; + p_end->need = 0; + break; + case CF_PRINT_PROCESS_ONLY: + t_end->pr = pr_nop; + t_end->need = 0; + break; + default: + fprintf(stderr, "please report this bug\n"); + // FALL THROUGH + case CF_PRINT_AS_NEEDED: + case CF_PRINT_EVERY_TIME: + break; + } + walk = walk->next; + } + t_end->next = NULL; + p_end->next = NULL; + proc_format_list = pfn.next; + task_format_list = tfn.next; + }else{ + proc_format_list = format_list; + task_format_list = format_list; + } + + proc_format_needs = collect_format_needs(proc_format_list); + task_format_needs = collect_format_needs(task_format_list); + + needs_for_sort = check_sort_needs(sort_list); + + // move process-only flags to the process + proc_format_needs |= (task_format_needs &~ PROC_ONLY_FLAGS); + task_format_needs &= ~PROC_ONLY_FLAGS; + + if(bsd_c_option){ + proc_format_needs &= ~PROC_FILLARG; + needs_for_sort &= ~PROC_FILLARG; + } + if(!unix_f_option){ + proc_format_needs &= ~PROC_FILLCOM; + needs_for_sort &= ~PROC_FILLCOM; + } + // convert ARG to COM as a standard + if(proc_format_needs & PROC_FILLARG){ + proc_format_needs |= PROC_FILLCOM; + proc_format_needs &= ~PROC_FILLARG; + } + if(bsd_e_option){ + if(proc_format_needs&PROC_FILLCOM) proc_format_needs |= PROC_FILLENV; + } + + /* FIXME broken filthy hack -- got to unify some stuff here */ + if( ( (proc_format_needs|task_format_needs|needs_for_sort) & PROC_FILLWCHAN) && !wchan_is_number) + if (open_psdb(namelist_file)) wchan_is_number = 1; + + if(thread_flags&TF_loose_tasks) needs_for_threads |= PROC_LOOSE_TASKS; +} + +////////////////////////////////////////////////////////////////////////// + +/***** fill in %CPU; not in libproc because of include_dead_children */ +/* Note: for sorting, not display, so 0..0x7fffffff would be OK */ +static int want_this_proc_pcpu(proc_t *buf){ + unsigned long long used_jiffies; + unsigned long pcpu = 0; + unsigned long long avail_jiffies; + + if(!want_this_proc(buf)) return 0; + + used_jiffies = buf->utime + buf->stime; + if(include_dead_children) used_jiffies += (buf->cutime + buf->cstime); + + avail_jiffies = seconds_since_boot * Hertz - buf->start_time; + if(avail_jiffies) pcpu = (used_jiffies << 24) / avail_jiffies; + + buf->pcpu = pcpu; // fits in an int, summing children on 128 CPUs + + return 1; +} + +/***** just display */ +static void simple_spew(void){ + proc_t buf; + PROCTAB* ptp; + ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + memset(&buf, '#', sizeof(proc_t)); + switch(thread_flags & (TF_show_proc|TF_loose_tasks|TF_show_task)){ + case TF_show_proc: // normal non-thread output + while(readproc(ptp,&buf)){ + if(want_this_proc(&buf)){ + show_one_proc(&buf, proc_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + case TF_show_proc|TF_loose_tasks: // H option + while(readproc(ptp,&buf)){ + proc_t buf2; + // must still have the process allocated + while(readtask(ptp,&buf,&buf2)){ + if(!want_this_proc(&buf)) continue; + show_one_proc(&buf2, task_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + case TF_show_proc|TF_show_task: // m and -m options + while(readproc(ptp,&buf)){ + if(want_this_proc(&buf)){ + proc_t buf2; + show_one_proc(&buf, proc_format_list); + // must still have the process allocated + while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + case TF_show_task: // -L and -T options + while(readproc(ptp,&buf)){ + if(want_this_proc(&buf)){ + proc_t buf2; + // must still have the process allocated + while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + } + closeproc(ptp); +} + +/***** forest output requires sorting by ppid; add start_time by default */ +static void prep_forest_sort(void){ + sort_node *tmp_list = sort_list; + const format_struct *incoming; + + if(!sort_list) { /* assume start time order */ + incoming = search_format_array("start_time"); + if(!incoming) fprintf(stderr, "Could not find start_time!\n"); + tmp_list = malloc(sizeof(sort_node)); + tmp_list->reverse = 0; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->need = incoming->need; + tmp_list->next = sort_list; + sort_list = tmp_list; + } + /* this is required for the forest option */ + incoming = search_format_array("ppid"); + if(!incoming) fprintf(stderr, "Could not find ppid!\n"); + tmp_list = malloc(sizeof(sort_node)); + tmp_list->reverse = 0; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->need = incoming->need; + tmp_list->next = sort_list; + sort_list = tmp_list; +} + +/* we rely on the POSIX requirement for zeroed memory */ +//static proc_t *processes[98*1024]; // FIXME +static proc_t **processes; + +/***** compare function for qsort */ +static int compare_two_procs(const void *a, const void *b){ + sort_node *tmp_list = sort_list; + while(tmp_list){ + int result; + result = (*tmp_list->sr)(*(const proc_t *const*)a, *(const proc_t *const*)b); + if(result) return (tmp_list->reverse) ? -result : result; + tmp_list = tmp_list->next; + } + return 0; /* no conclusion */ +} + +/***** show pre-sorted array of process pointers */ +static void show_proc_array(PROCTAB *restrict ptp, int n){ + proc_t **p = processes; + while(n--){ + if(thread_flags & TF_show_proc) show_one_proc(*p, proc_format_list); + if(thread_flags & TF_show_task){ + proc_t buf2; + // must still have the process allocated + while(readtask(ptp,*p,&buf2)) show_one_proc(&buf2, task_format_list); + // must not attempt to free cmdline and environ + } + /* no point freeing any of this -- won't need more mem */ +// if((*p)->cmdline) free((void*)*(*p)->cmdline); +// if((*p)->environ) free((void*)*(*p)->environ); +// memset(*p, '%', sizeof(proc_t)); /* debug */ +// free(*p); + p++; + } +} + +/***** show tree */ +/* this needs some optimization work */ +#define ADOPTED(x) 1 +static void show_tree(const int self, const int n, const int level, const int have_sibling){ + int i = 0; + if(level){ + /* add prefix of "+" or "L" */ + if(have_sibling) forest_prefix[level-1] = '+'; + else forest_prefix[level-1] = 'L'; + forest_prefix[level] = '\0'; + } + show_one_proc(processes[self],format_list); /* first show self */ + /* no point freeing any of this -- won't need more mem */ +// if(processes[self]->cmdline) free((void*)*processes[self]->cmdline); +// if(processes[self]->environ) free((void*)*processes[self]->environ); + for(;;){ /* look for children */ + if(i >= n) return; /* no children */ + if(processes[i]->ppid == processes[self]->XXXID) break; + i++; + } + if(level){ + /* change our prefix to "|" or " " for the children */ + if(have_sibling) forest_prefix[level-1] = '|'; + else forest_prefix[level-1] = ' '; + forest_prefix[level] = '\0'; + } + for(;;){ + int self_pid; + int more_children = 1; + if(i >= n) break; /* over the edge */ + self_pid=processes[self]->XXXID; + if(i+1 >= n) + more_children = 0; + else + if(processes[i+1]->ppid != self_pid) more_children = 0; + if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u') + show_tree(i++, n, level, more_children); + else + show_tree(i++, n, level+1, more_children); + if(!more_children) break; + } + /* chop prefix that children added -- do we need this? */ + forest_prefix[level] = '\0'; +// memset(processes[self], '$', sizeof(proc_t)); /* debug */ +} + +/***** show forest */ +static void show_forest(const int n){ + int i = n; + int j; + while(i--){ /* cover whole array looking for trees */ + j = n; + while(j--){ /* search for parent: if none, i is a tree! */ + if(processes[j]->XXXID == processes[i]->ppid) goto not_root; + } + show_tree(i,n,0,0); +not_root: + ; + } + /* don't free the array because it takes time and ps will exit anyway */ +} + +static int want_this_proc_nop(proc_t *dummy){ + (void)dummy; + return 1; +} + +/***** sorted or forest */ +static void fancy_spew(void){ + proc_data_t *pd = NULL; + PROCTAB *restrict ptp; + int n = 0; /* number of processes & index into array */ + + ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + + if(thread_flags & TF_loose_tasks){ + pd = readproctab2(want_this_proc_nop, want_this_proc_pcpu, ptp); + }else{ + pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp); + } + n = pd->n; + processes = pd->tab; + + if(!n) return; /* no processes */ + if(forest_type) prep_forest_sort(); + qsort(processes, n, sizeof(proc_t*), compare_two_procs); + if(forest_type) show_forest(n); + else show_proc_array(ptp,n); + closeproc(ptp); +} + + +/***** no comment */ +int main(int argc, char *argv[]){ +#if (__GNU_LIBRARY__ >= 6) + setlocale (LC_CTYPE, ""); +#endif + +#ifdef DEBUG + init_stack_trace(argv[0]); +#else + do { + struct sigaction sa; + int i = 32; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigfillset(&sa.sa_mask); + while(i--) switch(i){ + default: + sigaction(i,&sa,NULL); + case 0: + case SIGINT: /* ^C */ + case SIGTSTP: /* ^Z */ + case SIGTTOU: /* see stty(1) man page */ + case SIGQUIT: /* ^\ */ + case SIGPROF: /* profiling */ + case SIGKILL: /* can not catch */ + case SIGSTOP: /* can not catch */ + case SIGWINCH: /* don't care if window size changes */ + ; + } + } while (0); +#endif + + reset_global(); /* must be before parser */ + arg_parse(argc,argv); + +/* arg_show(); */ + trace("screen is %ux%u\n",screen_cols,screen_rows); +/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */ + trace("======= ps output follows =======\n"); + + init_output(); /* must be between parser and output */ + + lists_and_needs(); + + if(forest_type || sort_list) fancy_spew(); /* sort or forest */ + else simple_spew(); /* no sort, no forest */ + show_one_proc((proc_t *)-1,format_list); /* no output yet? */ + return 0; +} diff --git a/ps/global.c b/ps/global.c new file mode 100644 index 0000000..0b3bc5b --- /dev/null +++ b/ps/global.c @@ -0,0 +1,498 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "common.h" + +#include +#include "../proc/wchan.h" +#include "../proc/version.h" +#include "../proc/sysinfo.h" + + +#ifndef __GNU_LIBRARY__ +#define __GNU_LIBRARY__ -1 +#endif +#ifndef __GLIBC__ +#define __GLIBC__ -1 +#endif +#ifndef __GLIBC_MINOR__ +#define __GLIBC_MINOR__ -1 +#endif + + +static const char * saved_personality_text = "You found a bug!"; + +int all_processes = -1; +const char *bsd_j_format = (const char *)0xdeadbeef; +const char *bsd_l_format = (const char *)0xdeadbeef; +const char *bsd_s_format = (const char *)0xdeadbeef; +const char *bsd_u_format = (const char *)0xdeadbeef; +const char *bsd_v_format = (const char *)0xdeadbeef; +int bsd_c_option = -1; +int bsd_e_option = -1; +uid_t cached_euid = -1; +dev_t cached_tty = -1; +char forest_prefix[4 * 32*1024 + 100]; // FIXME +int forest_type = -1; +unsigned format_flags = 0xffffffff; /* -l -f l u s -j... */ +format_node *format_list = (format_node *)0xdeadbeef; /* digested formatting options */ +unsigned format_modifiers = 0xffffffff; /* -c -j -y -P -L... */ +int header_gap = -1; +int header_type = -1; +int include_dead_children = -1; +int lines_to_next_header = -1; +const char *namelist_file = (const char *)0xdeadbeef; +int negate_selection = -1; +int running_only = -1; +int page_size = -1; // "int" for math reasons? +unsigned personality = 0xffffffff; +int prefer_bsd_defaults = -1; +int screen_cols = -1; +int screen_rows = -1; +unsigned long seconds_since_boot = -1; +selection_node *selection_list = (selection_node *)0xdeadbeef; +unsigned simple_select = 0xffffffff; +sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */ +const char *sysv_f_format = (const char *)0xdeadbeef; +const char *sysv_fl_format = (const char *)0xdeadbeef; +const char *sysv_j_format = (const char *)0xdeadbeef; +const char *sysv_l_format = (const char *)0xdeadbeef; +unsigned thread_flags = 0xffffffff; +int unix_f_option = -1; +int user_is_number = -1; +int wchan_is_number = -1; + + +static void reset_selection_list(void){ + selection_node *old; + selection_node *walk = selection_list; + if(selection_list == (selection_node *)0xdeadbeef){ + selection_list = NULL; + return; + } + while(walk){ + old = walk; + walk = old->next; + free(old->u); + free(old); + } + selection_list = NULL; +} + +// The rules: +// 1. Defaults are implementation-specific. (ioctl,termcap,guess) +// 2. COLUMNS and LINES override the defaults. (standards compliance) +// 3. Command line options override everything else. +// 4. Actual output may be more if the above is too narrow. +// +// SysV tends to spew semi-wide output in all cases. The args +// will be limited to 64 or 80 characters, without regard to +// screen size. So lines of 120 to 160 chars are normal. +// Tough luck if you want more or less than that! HP-UX has a +// new "-x" option for 1024-char args in place of comm that +// we'll implement at some point. +// +// BSD tends to make a good effort, then fall back to 80 cols. +// Use "ww" to get infinity. This is nicer for "ps | less" +// and "watch ps". It can run faster too. +static void set_screen_size(void){ + struct winsize ws; + char *columns; /* Unix98 environment variable */ + char *lines; /* Unix98 environment variable */ + + do{ + int fd; + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break; + if(ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break; + if(ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break; + fd = open("/dev/tty", O_NOCTTY|O_NONBLOCK|O_RDONLY); + if(fd != -1){ + int ret = ioctl(fd, TIOCGWINSZ, &ws); + close(fd); + if(ret != -1 && ws.ws_col>0 && ws.ws_row>0) break; + } + // TODO: ought to do tgetnum("co") and tgetnum("li") here + ws.ws_col = 80; + ws.ws_row = 24; + }while(0); + screen_cols = ws.ws_col; // hmmm, NetBSD subtracts 1 + screen_rows = ws.ws_row; + + // TODO: delete this line + if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE; + + columns = getenv("COLUMNS"); + if(columns && *columns){ + long t; + char *endptr; + t = strtol(columns, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t; + } + + lines = getenv("LINES"); + if(lines && *lines){ + long t; + char *endptr; + t = strtol(lines, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t; + } + + if((screen_cols<9) || (screen_rows<2)) + fprintf(stderr,"Your %dx%d screen size is bogus. Expect trouble.\n", + screen_cols, screen_rows + ); +} + +/**************** personality control **************/ + +typedef struct personality_table_struct { + const char *name; /* personality name */ + const void *jump; /* See gcc extension info. :-) */ +} personality_table_struct; + +static int compare_personality_table_structs(const void *a, const void *b){ + return strcasecmp(((const personality_table_struct*)a)->name,((const personality_table_struct*)b)->name); +} + +static const char *set_personality(void){ + const char *s; + size_t sl; + char buf[16]; + personality_table_struct findme = { buf, NULL}; + personality_table_struct *found; + static const personality_table_struct personality_table[] = { + {"390", &&case_390}, + {"aix", &&case_aix}, + {"bsd", &&case_bsd}, + {"compaq", &&case_compaq}, + {"debian", &&case_debian}, + {"default", &&case_default}, + {"digital", &&case_digital}, + {"gnu", &&case_gnu}, + {"hp", &&case_hp}, + {"hpux", &&case_hpux}, + {"irix", &&case_irix}, + {"linux", &&case_linux}, + {"old", &&case_old}, + {"os390", &&case_os390}, + {"posix", &&case_posix}, + {"s390", &&case_s390}, + {"sco", &&case_sco}, + {"sgi", &&case_sgi}, + {"solaris2", &&case_solaris2}, + {"sunos4", &&case_sunos4}, + {"svr4", &&case_svr4}, + {"sysv", &&case_sysv}, + {"tru64", &&case_tru64}, + {"unix", &&case_unix}, + {"unix95", &&case_unix95}, + {"unix98", &&case_unix98}, + {"unknown", &&case_unknown} + }; + const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct); + + personality = 0; + prefer_bsd_defaults = 0; + + bsd_j_format = "OL_j"; + bsd_l_format = "OL_l"; + bsd_s_format = "OL_s"; + bsd_u_format = "OL_u"; + bsd_v_format = "OL_v"; + + /* When these are NULL, the code does SysV output modifier logic */ + sysv_f_format = NULL; + sysv_fl_format = NULL; + sysv_j_format = NULL; + sysv_l_format = NULL; + + s = getenv("PS_PERSONALITY"); + if(!s || !*s) s = getenv("CMD_ENV"); + if(!s || !*s) s="unknown"; /* "Do The Right Thing[tm]" */ + if(getenv("I_WANT_A_BROKEN_PS")) s="old"; + sl = strlen(s); + if(sl > 15) return "Environment specified an unknown personality."; + strncpy(buf, s, sl); + buf[sl] = '\0'; + saved_personality_text = strdup(buf); + + found = bsearch(&findme, personality_table, personality_table_count, + sizeof(personality_table_struct), compare_personality_table_structs + ); + + if(!found) return "Environment specified an unknown personality."; + + goto *(found->jump); /* See gcc extension info. :-) */ + + case_bsd: + personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m; + prefer_bsd_defaults = 1; + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_old: + personality = PER_FORCE_BSD | PER_OLD_m; + prefer_bsd_defaults = 1; + return NULL; + + case_debian: /* Toss this? They don't seem to care much. */ + case_gnu: + personality = PER_GOOD_o | PER_OLD_m; + prefer_bsd_defaults = 1; + sysv_f_format = "RD_f"; + /* sysv_fl_format = "RD_fl"; */ /* old Debian ps can't do this! */ + sysv_j_format = "RD_j"; + sysv_l_format = "RD_l"; + return NULL; + + case_linux: + personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER; + return NULL; + + case_default: /* use defaults for ps, ignoring other environment variables */ + return NULL; + + case_unknown: /* defaults, but also check inferior environment variables */ + if( + getenv("UNIX95") /* Irix */ + || getenv("POSIXLY_CORRECT") /* most gnu stuff */ + || (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */ + ) personality = PER_BROKEN_o; + return NULL; + + case_aix: + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_tru64: + case_compaq: + case_digital: + // no PER_NO_DEFAULT_g even though man page claims it + // Reality: the g is a NOP + personality = PER_GOOD_o | PER_BSD_h; + prefer_bsd_defaults = 1; + sysv_f_format = "F5FMT"; + sysv_fl_format = "FL5FMT"; + sysv_j_format = "JFMT"; + sysv_l_format = "L5FMT"; + bsd_j_format = "JFMT"; + bsd_l_format = "LFMT"; + bsd_s_format = "SFMT"; + bsd_u_format = "UFMT"; + bsd_v_format = "VFMT"; + return NULL; + + case_sunos4: + personality = PER_NO_DEFAULT_g; + prefer_bsd_defaults = 1; + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_irix: + case_sgi: + s = getenv("_XPG"); + if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o; + else personality = PER_IRIX_l; + return NULL; + + case_os390: /* IBM's OS/390 OpenEdition on the S/390 mainframe */ + case_s390: + case_390: + sysv_j_format = "J390"; /* don't know what -jl and -jf do */ + return NULL; + + case_hp: + case_hpux: + personality = PER_BROKEN_o | PER_HPUX_x; + return NULL; + + case_svr4: + case_sysv: + case_sco: + personality = PER_BROKEN_o | PER_SVR4_x; + return NULL; + + case_posix: + case_solaris2: + case_unix95: + case_unix98: + case_unix: + personality = PER_BROKEN_o; + return NULL; +} + + +/************ Call this to reinitialize everything ***************/ +void reset_global(void){ + static proc_t p; + reset_selection_list(); + look_up_our_self(&p); + set_screen_size(); + set_personality(); + + all_processes = 0; + bsd_c_option = 0; + bsd_e_option = 0; + cached_euid = geteuid(); + cached_tty = p.tty; +/* forest_prefix must be all zero because of POSIX */ + forest_type = 0; + format_flags = 0; /* -l -f l u s -j... */ + format_list = NULL; /* digested formatting options */ + format_modifiers = 0; /* -c -j -y -P -L... */ + header_gap = -1; /* send lines_to_next_header to -infinity */ + header_type = HEAD_SINGLE; + include_dead_children = 0; + lines_to_next_header = 1; + namelist_file = NULL; + negate_selection = 0; + page_size = getpagesize(); + running_only = 0; + seconds_since_boot = uptime(0,0); + selection_list = NULL; + simple_select = 0; + sort_list = NULL; + thread_flags = 0; + unix_f_option = 0; + user_is_number = 0; + wchan_is_number = 0; +} + +static const char archdefs[] = +#ifdef __alpha__ +" alpha" +#endif +#ifdef __arm__ +" arm" +#endif +#ifdef __hppa__ +" hppa" +#endif +#ifdef __i386__ +" i386" +#endif +#ifdef __ia64__ +" ia64" +#endif +#ifdef __mc68000__ +" mc68000" +#endif +#ifdef __mips64__ +" mips64" +#endif +#ifdef __mips__ +" mips" +#endif +#ifdef __powerpc__ +" powerpc" +#endif +#ifdef __sh3__ +" sh3" +#endif +#ifdef __sh__ +" sh" +#endif +#ifdef __sparc__ +" sparc" +#endif +#ifdef __sparc_v9__ +" sparc_v9" +#endif +#ifdef __x86_64__ +" x86_64" +#endif +""; + +/*********** spew variables ***********/ +void self_info(void){ + fprintf(stderr, + "BSD j %s\n" + "BSD l %s\n" + "BSD s %s\n" + "BSD u %s\n" + "BSD v %s\n" + "SysV -f %s\n" + "SysV -fl %s\n" + "SysV -j %s\n" + "SysV -l %s\n" + "\n", + bsd_j_format ? bsd_j_format : "(none)", + bsd_l_format ? bsd_l_format : "(none)", + bsd_s_format ? bsd_s_format : "(none)", + bsd_u_format ? bsd_u_format : "(none)", + bsd_v_format ? bsd_v_format : "(none)", + sysv_f_format ? sysv_f_format : "(none)", + sysv_fl_format ? sysv_fl_format : "(none)", + sysv_j_format ? sysv_j_format : "(none)", + sysv_l_format ? sysv_l_format : "(none)" + ); + + display_version(); + fprintf(stderr, "Linux version %d.%d.%d\n", + LINUX_VERSION_MAJOR(linux_version_code), + LINUX_VERSION_MINOR(linux_version_code), + LINUX_VERSION_PATCH(linux_version_code) + ); + /* __libc_print_version(); */ /* how can we get the run-time version? */ + fprintf(stderr, "Compiled with: glibc %d.%d, gcc %d.%d\n\n", + __GLIBC__, __GLIBC_MINOR__, __GNUC__, __GNUC_MINOR__ + ); + + fprintf(stderr, + "header_gap=%d lines_to_next_header=%d\n" + "screen_cols=%d screen_rows=%d\n" + "\n", + header_gap, lines_to_next_header, + screen_cols, screen_rows + ); + + fprintf(stderr, + "personality=0x%08x (from \"%s\")\n" + "EUID=%d TTY=%d,%d Hertz=%Ld page_size=%d\n", + personality, saved_personality_text, + cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz, + (int)(page_size) + ); + + fprintf(stderr, + "sizeof(proc_t)=%d sizeof(long)=%d sizeof(KLONG)=%d\n", + (int)sizeof(proc_t), (int)sizeof(long), (int)sizeof(KLONG) + ); + + fprintf(stderr, "archdefs:%s\n", archdefs); + + open_psdb(namelist_file); + fprintf(stderr,"namelist_file=\"%s\"\n",namelist_file?namelist_file:""); +} diff --git a/ps/help.c b/ps/help.c new file mode 100644 index 0000000..2f39a9d --- /dev/null +++ b/ps/help.c @@ -0,0 +1,48 @@ +/* + * Copyright 1998-2004 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* + * The help message must not become longer, because it must fit + * on an 80x24 screen _with_ the error message and command prompt. + */ + +const char *help_message = +"********* simple selection ********* ********* selection by list *********\n" +"-A all processes -C by command name\n" +"-N negate selection -G by real group ID (supports names)\n" +"-a all w/ tty except session leaders -U by real user ID (supports names)\n" +"-d all except session leaders -g by session OR by effective group name\n" +"-e all processes -p by process ID\n" +"T all processes on this terminal -s processes in the sessions given\n" +"a all w/ tty, including other users -t by tty\n" +"g OBSOLETE -- DO NOT USE -u by effective user ID (supports names)\n" +"r only running processes U processes for specified users\n" +"x processes w/o controlling ttys t by tty\n" +"*********** output format ********** *********** long options ***********\n" +"-o,o user-defined -f full --Group --User --pid --cols --ppid\n" +"-j,j job control s signal --group --user --sid --rows --info\n" +"-O,O preloaded -o v virtual memory --cumulative --format --deselect\n" +"-l,l long u user-oriented --sort --tty --forest --version\n" +"-F extra full X registers --heading --no-heading --context\n" +" ********* misc options *********\n" +"-V,V show version L list format codes f ASCII art forest\n" +"-m,m,-L,-T,H threads S children in sum -y change -l format\n" +"-M,Z security data c true command name -c scheduling class\n" +"-w,w wide output n numeric WCHAN,UID -H process hierarchy\n" +; + + + +/* Missing: + * + * -P e k + * + */ diff --git a/ps/it b/ps/it new file mode 100644 index 0000000..07fd6dc --- /dev/null +++ b/ps/it @@ -0,0 +1,35 @@ +From ddainese@dsi.unive.it Sun Apr 18 14:12:27 1999 + +here is a first translation of the text: +--------------------------------------------------------------------- +const char *help_message = +"****** seleziona i processi ******* * seleziona una lista specificando: *\n" +"-A tutti -C il nome del comando\n" +"-N nega la selezione -G il real group ID (supporta i nomi)\n" +"-a con tty, tranne i session leader -U il real user ID (supporta i nomi)\n" +"-d tutti, tranne i session leader -g il session leader OPPURE il gruppo\n" +"-e tutti -p l'ID del processo\n" +"T su questo terminale -s la sessione\n" +"a con tty, di tutti gli utenti -t il tty\n" +"g tutti, anche i leader di gruppo -u l'effective user ID (supporta i nomi)\n" +"r in stato running U una lista di utenti\n" +"x senza tty t il tty\n" +"******** formato dell'output ****** ********** opzioni lunghe **********\n" +"-o,o definito dall'utente --Group --User --pid --cols\n" +"-j,j job s segnali --group --user --sid --rows\n" +"-O,O -o preimpostato v memoria virtuale --cumulative --format --deselect\n" +"-l,l lungo u utenti --sort --tty --forest --version\n" +"-f completo X registri --heading --no-heading\n" +" ******** opzioni varie *********\n" +"-V,V versione L codici di formato f foresta di ASCII\n" +"-m,m vista ad albero S figli in sum -y cambia il formato -l\n" +"-n,N namelist file c nome reale del comando n WCHAN,UID numerici\n" +"-w,w output ampio e mostra l'environment -H gerarchia dei processi\n" +; +--------------------------------------------------------------------- + +Unfortunately it isn't really understandable for a newbie, because +there is too little space for a good translation; to make it more +meaningful, I would need about an entire line for every option, thus +if you really want the help text stays under 22 lines, it must +contains only 22 options. What do you think about it? diff --git a/ps/module.mk b/ps/module.mk new file mode 100755 index 0000000..2902a3a --- /dev/null +++ b/ps/module.mk @@ -0,0 +1,40 @@ +# This file gets included into the main Makefile, in the top directory. + +INSTALL += $(bin)ps $(man1)ps.1 + +# files to remove +CLEAN += ps/ps ps/debug + +# a directory for cleaning +DIRS += ps/ + +# a file to create +ALL += ps/ps + +PS_C := display global help output parser select sortformat +PSNAMES := $(addprefix ps/,$(PS_C)) +PSOBJ := $(addsuffix .o,$(PSNAMES)) +PSSRC := $(addsuffix .c,$(PSNAMES)) + +PS_X := COPYING HACKING TRANSLATION common.h module.mk it p ps.1 regression +TARFILES += $(PSSRC) $(addprefix ps/,$(PS_X)) + +ps/ps: $(PSOBJ) $(LIBPROC) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ $(ldl) + +# This just adds the stacktrace code +ps/debug: $(PSOBJ) stacktrace.o $(LIBPROC) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ -lefence $(ldl) + +$(PSOBJ): %.o: %.c ps/common.h $(LIBPROC) + $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< -o $@ + +ps/stacktrace.o: ps/stacktrace.c + + +$(bin)ps: ps/ps + $(install) --mode a=rx $< $@ + +$(man1)ps.1 : ps/ps.1 + $(install) --mode a=r $< $@ + -rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz diff --git a/ps/output.c b/ps/output.c new file mode 100644 index 0000000..87bf9de --- /dev/null +++ b/ps/output.c @@ -0,0 +1,1969 @@ +/* + * Copyright 1999-2004 by Albert Cahalan; all rights reserved. + * + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* + * This file is really gross, and I know it. I looked into several + * alternate ways to deal with the mess, and they were all ugly. + * + * FreeBSD has a fancy hack using offsets into a struct -- that + * saves code but it is _really_ gross. See the PO macro below. + * + * We could have a second column width for wide output format. + * For example, Digital prints the real-time signals. + */ + + +/* + * Data table idea: + * + * table 1 maps aix to specifier + * table 2 maps shortsort to specifier + * table 3 maps macro to specifiers + * table 4 maps specifier to title,datatype,offset,vendor,helptext + * table 5 maps datatype to justification,width,widewidth,sorting,printing + * + * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty... + * It must be enough to determine printing and sorting. + * + * After the tables, increase width as needed to fit the header. + * + * Table 5 could go in a file with the output functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "../proc/wchan.h" +#include "../proc/procps.h" +#include "../proc/devname.h" +#include "../proc/escape.h" +#include "common.h" + +/* TODO: + * Stop assuming system time is local time. + */ + +#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */ + +static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */ +static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */ + + + +static int wide_signals; /* true if we have room */ + +static unsigned long seconds_since_1970; +static unsigned long time_of_boot; +static unsigned long page_shift; + + +/*************************************************************************/ +/************ Lots of sort functions, starting with the NOP **************/ + +static int sr_nop(const proc_t* a, const proc_t* b){ + (void)a;(void)b; /* shut up gcc */ + return 0; +} + +#define CMP_STR(NAME) \ +static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \ + return strcmp(P->NAME, Q->NAME); \ +} + +#define CMP_INT(NAME) \ +static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ + if (P->NAME < Q->NAME) return -1; \ + if (P->NAME > Q->NAME) return 1; \ + return 0; \ +} + +/* fast version, for values which either: + * a. differ by no more than 0x7fffffff + * b. only need to be grouped same w/ same + */ +#define CMP_SMALL(NAME) \ +static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ + return (int)(P->NAME) - (int)(Q->NAME); \ +} + +CMP_INT(rtprio) +CMP_SMALL(sched) +CMP_INT(cutime) +CMP_INT(cstime) +CMP_SMALL(priority) /* nice */ +CMP_SMALL(nlwp) +CMP_SMALL(nice) /* priority */ +CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */ +CMP_INT(alarm) +CMP_INT(size) /* total pages */ /* vm_size, vsize */ +CMP_INT(resident) /* resident pages */ /* vm_rss, rss */ +CMP_INT(share) /* shared pages */ +CMP_INT(trs) /* executable pages */ +CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */ +CMP_INT(drs) /* other pages (assumed data?) */ +CMP_INT(dt) /* dirty pages */ + +CMP_INT(vm_size) /* kB VM */ /* size, vsize */ +CMP_INT(vm_lock) /* kB locked */ +CMP_INT(vm_rss) /* kB rss */ /* rss, resident */ +CMP_INT(vm_data) /* kB "data" == data-stack */ +CMP_INT(vm_stack) /* kB stack */ +CMP_INT(vm_exe) /* kB "exec" == exec-lib */ +CMP_INT(vm_lib) /* kB "libraries" */ +CMP_INT(vsize) /* pages VM */ /* size, vm_size */ +CMP_INT(rss_rlim) +CMP_SMALL(flags) +CMP_INT(min_flt) +CMP_INT(maj_flt) +CMP_INT(cmin_flt) +CMP_INT(cmaj_flt) +CMP_INT(utime) +CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */ +CMP_INT(start_code) +CMP_INT(end_code) +CMP_INT(start_stack) +CMP_INT(kstk_esp) +CMP_INT(kstk_eip) +CMP_INT(start_time) +CMP_INT(wchan) + +/* CMP_STR(*environ) */ +/* CMP_STR(*cmdline) */ + +CMP_STR(ruser) +CMP_STR(euser) +CMP_STR(suser) +CMP_STR(fuser) +CMP_STR(rgroup) +CMP_STR(egroup) +CMP_STR(sgroup) +CMP_STR(fgroup) +CMP_STR(cmd) +/* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */ + +CMP_INT(ruid) +CMP_INT(rgid) +CMP_INT(euid) +CMP_INT(egid) +CMP_INT(suid) +CMP_INT(sgid) +CMP_INT(fuid) +CMP_INT(fgid) +CMP_SMALL(tid) +CMP_SMALL(tgid) +CMP_SMALL(ppid) +CMP_SMALL(pgrp) +CMP_SMALL(session) +CMP_INT(tty) +CMP_SMALL(tpgid) + +CMP_SMALL(pcpu) + +CMP_SMALL(state) + +/* approximation to: kB of address space that could end up in swap */ +static int sr_swapable(const proc_t* P, const proc_t* Q) { + unsigned long p_swapable = P->vm_data + P->vm_stack; + unsigned long q_swapable = Q->vm_data + Q->vm_stack; + if (p_swapable < q_swapable) return -1; + if (p_swapable > q_swapable) return 1; + return 0; +} + + +/***************************************************************************/ +/************ Lots of format functions, starting with the NOP **************/ + +// so popular it can't be "static" +int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){ + (void)pp; + return snprintf(outbuf, COLWID, "%c", '-'); +} + + +/********* Unix 98 ************/ + +/*** + +Only comm and args are allowed to contain blank characters; all others are +not. Any implementation-dependent variables will be specified in the system +documentation along with the default header and indicating if the field +may contain blank characters. + +Some headers do not have a standardized specifier! + +%CPU pcpu The % of cpu time used recently, with unspecified "recently". +ADDR The address of the process. +C Processor utilisation for scheduling. +CMD The command name, or everything with -f. +COMMAND args Command + args. May chop as desired. May use either version. +COMMAND comm argv[0] +ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss +F Flags (octal and additive) +GROUP group Effective group ID, prefer text over decimal. +NI nice Decimal system scheduling priority, see nice(1). +PGID pgid The decimal value of the process group ID. +PID pid Decimal PID. +PPID ppid Decimal PID. +PRI Priority. Higher numbers mean lower priority. +RGROUP rgroup Real group ID, prefer text over decimal. +RUSER ruser Real user ID, prefer text over decimal. +S The state of the process. +STIME Starting time of the process. +SZ The size in blocks of the core image of the process. +TIME time Cumulative CPU time. [dd-]hh:mm:ss +TT tty Name of tty in format used by who(1). +TTY The controlling terminal for the process. +UID UID, or name when -f +USER user Effective user ID, prefer text over decimal. +VSZ vsz Virtual memory size in decimal kB. +WCHAN Where waiting/sleeping or blank if running. + +The nice value is used to compute the priority. + +For some undefined ones, Digital does: + +F flag Process flags -- but in hex! +PRI pri Process priority +S state Symbolic process status +TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??" +UID uid Process user ID (effective UID) +WCHAN wchan Address of event on which a + +For some undefined ones, Sun does: + +ADDR addr memory address of the process +C c Processor utilization for scheduling (obsolete). +CMD +F f +S s state: OSRZT +STIME start time, printed w/o blanks. If 24h old, months & days +SZ size (in pages) of the swappable process's image in main memory +TTY +UID uid +WCHAN wchan + +For some undefined ones, SCO does: +ADDR addr Virtual address of the process' entry in the process table. +SZ swappable size in kB of the virtual data and stack +STIME stime hms or md time format +***/ + +/* Source & destination are known. Return bytes or screen characters? */ +static int forest_helper(char *restrict const outbuf){ + char *p = forest_prefix; + char *q = outbuf; + int rightward=max_rightward; + if(!*p) return 0; + /* Arrrgh! somebody defined unix as 1 */ + if(forest_type == 'u') goto unixy; + while(*p){ + switch(*p){ + case ' ': strcpy(q, " "); break; + case 'L': strcpy(q, " \\_ "); break; + case '+': strcpy(q, " \\_ "); break; + case '|': strcpy(q, " | "); break; + case '\0': return q-outbuf; /* redundant & not used */ + } + if (rightward-4 < 0) { + *(q+rightward)='\0'; + return max_rightward; + } + q += 4; + rightward -= 4; + p++; + } + return q-outbuf; /* gcc likes this here */ +unixy: + while(*p){ + switch(*p){ + case ' ': strcpy(q, " "); break; + case 'L': strcpy(q, " "); break; + case '+': strcpy(q, " "); break; + case '|': strcpy(q, " "); break; + case '\0': return q-outbuf; /* redundant & not used */ + } + if (rightward-2 < 0) { + *(q+rightward)='\0'; + return max_rightward; + } + q += 2; + rightward -= 2; + p++; + } + return q-outbuf; /* gcc likes this here */ +} + + +/* XPG4-UNIX, according to Digital: +The "args" and "command" specifiers show what was passed to the command. +Modifications to the arguments are not shown. +*/ + +/* + * pp->cmd short accounting name (comm & ucomm) + * pp->cmdline long name with args (args & command) + * pp->environ environment + */ + +// FIXME: some of these may hit the guard page in forest mode + +/* "command" is the same thing: long unless c */ +static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){ + char *endp = outbuf; + unsigned flags; + int rightward=max_rightward; + + if(forest_prefix){ + int fh = forest_helper(outbuf); + endp += fh; + rightward -= fh; + } + if(bsd_c_option) flags = ESC_DEFUNCT; + else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; + endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags); + + if(bsd_e_option && rightward>1){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + rightward--; + endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward); + } + } + //return endp - outbuf; + return max_rightward-rightward; +} + +/* "ucomm" is the same thing: short unless -f */ +static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){ + char *endp = outbuf; + unsigned flags; + int rightward=max_rightward; + + if(forest_prefix){ + int fh = forest_helper(outbuf); + endp += fh; + rightward -= fh; + } + if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; + else flags = ESC_DEFUNCT; + endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags); + + if(bsd_e_option && rightward>1){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + rightward--; + endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward); + } + } + //return endp - outbuf; + return max_rightward-rightward; +} +/* Non-standard, from SunOS 5 */ +static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){ + char *endp = outbuf; + int rightward = max_rightward; + + if(forest_prefix){ + int fh = forest_helper(outbuf); + endp += fh; + rightward -= fh; + } + if (rightward>8) /* 8=default, but forest maybe feeds more */ + rightward = 8; + + endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward); + //return endp - outbuf; + return max_rightward-rightward; +} + +/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */ +static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long t; + unsigned dd,hh,mm,ss; + char *cp = outbuf; + t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz); + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + dd = t; + cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 ); + cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 ); + cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ; + return (int)(cp-outbuf); +} + +/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */ +static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long total_time; /* jiffies used by this process */ + unsigned pcpu = 0; /* scaled %cpu, 99 means 99% */ + unsigned long long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = seconds_since_boot - pp->start_time / Hertz; + if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds; + if (pcpu > 99U) pcpu = 99U; + return snprintf(outbuf, COLWID, "%2u", pcpu); +} +/* normal %CPU in ##.# format. */ +static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long total_time; /* jiffies used by this process */ + unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned long long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = seconds_since_boot - pp->start_time / Hertz; + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999U) + return snprintf(outbuf, COLWID, "%u", pcpu/10U); + return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U); +} +/* this is a "per-mill" format, like %cpu with no decimal point */ +static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long total_time; /* jiffies used by this process */ + unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned long long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = seconds_since_boot - pp->start_time / Hertz ; + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999U) pcpu = 999U; + return snprintf(outbuf, COLWID, "%3u", pcpu); +} + +static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->pgrp); +} +static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->tgid); +} +static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->ppid); +} + + +/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */ +static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long t; + unsigned dd,hh,mm,ss; + int c; + t = (pp->utime + pp->stime) / Hertz; + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + dd = t; + c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 ); + c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) ); + return c; +} + +/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages. + * Unix98 requires "vsz" to be kB. + * Tru64 does both vsize and vsz like "1.23M" + * + * Our pp->vm_size is kB and our pp->vsize is pages. + * + * TODO: add flag for "1.23M" behavior, on this and other columns. + */ +static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%lu", pp->vm_size); +} + +////////////////////////////////////////////////////////////////////////////////////// + +// "PRI" is created by "opri", or by "pri" when -c is used. +// +// Unix98 only specifies that a high "PRI" is low priority. +// Sun and SCO add the -c behavior. Sun defines "pri" and "opri". +// Linux may use "priority" for historical purposes. +// +// According to the kernel's fs/proc/array.c and kernel/sched.c source, +// the kernel reports it in /proc via this: +// p->prio - MAX_RT_PRIO +// such that "RT tasks are offset by -200. Normal tasks are centered +// around 0, value goes from -16 to +15" but who knows if that is +// before or after the conversion... +// +// says: +// MAX_RT_PRIO is currently 100. (so we see 0 in /proc) +// RT tasks have a p->prio of 0 to 99. (so we see -100 to -1) +// non-RT tasks are from 100 to 139. (so we see 0 to 39) +// Lower values have higher priority, as in the UNIX standard. +// +// In any case, pp->priority+100 should get us back to what the kernel +// has for p->prio. +// +// Test results with the "yes" program on a 2.6.x kernel: +// +// # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm +// PRI PRI PRI PRI NI %CPU PID COMMAND +// 0 99 99 39 19 10.6 8686 19 +// 34 65 65 5 -20 94.7 8687 _20 +// +// Grrr. So the UNIX standard "PRI" must NOT be from "pri". +// Either of the others will do. We use "opri" for this. +// (and use "pri" when the "-c" option is used) +// Probably we should have Linux-specific "pri_for_l" and "pri_for_lc" +// +// sched_get_priority_min.2 says the Linux static priority is +// 1..99 for RT and 0 for other... maybe 100 is kernel-only? +// +// A nice range would be -99..0 for RT and 1..40 for normal, +// which is pp->priority+1. (3-digit max, positive is normal, +// negative or 0 is RT, and meets the standard for PRI) +// + +// legal as UNIX "PRI" +// "priority" (was -20..20, now -100..39) +static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){ /* -20..20 */ + return snprintf(outbuf, COLWID, "%ld", pp->priority); +} + +// legal as UNIX "PRI" +// "intpri" and "opri" (was 39..79, now -40..99) +static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 39..79 */ + return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority); +} + +// legal as UNIX "PRI" +// "pri_foo" -- match up w/ nice values of sleeping processes (-120..19) +static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->priority - 20); +} + +// legal as UNIX "PRI" +// "pri_bar" -- makes RT pri show as negative (-99..40) +static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->priority + 1); +} + +// legal as UNIX "PRI" +// "pri_baz" -- the kernel's ->prio value, as of Linux 2.6.8 (1..140) +static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->priority + 100); +} + + +// not legal as UNIX "PRI" +// "pri" (was 20..60, now 0..139) +static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 20..60 */ + return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority); +} + +// not legal as UNIX "PRI" +// "pri_api" -- match up w/ RT API (-40..99) +static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority); +} + +static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%ld", pp->nice); +} + +// HP-UX "cls": RT RR RR2 ???? HPUX FIFO KERN +// Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant) +// FIFO+RR share RT; FIFO has Inf quant +// IA=interactive; FX=fixed; TS=timeshare; SYS=system +// FSS=fairshare; INTS=interrupts +// Tru64 "policy": FF RR TS +// IRIX "class": RT TS B BC WL GN +// RT=real-time; TS=time-share; B=batch; BC=batch-critical +// WL=weightless; GN=gang-scheduled +// see miser(1) for this; PRI has some letter codes too +static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){ + switch(pp->sched){ + case -1: return snprintf(outbuf, COLWID, "-"); // not reported + case 0: return snprintf(outbuf, COLWID, "TS"); // SCHED_OTHER SCHED_NORMAL + case 1: return snprintf(outbuf, COLWID, "FF"); // SCHED_FIFO + case 2: return snprintf(outbuf, COLWID, "RR"); // SCHED_RR + case 3: return snprintf(outbuf, COLWID, "B"); // SCHED_BATCH + case 4: return snprintf(outbuf, COLWID, "ISO"); // reserved for SCHED_ISO (Con Kolivas) + case 5: return snprintf(outbuf, COLWID, "IDL"); // SCHED_IDLE + case 6: return snprintf(outbuf, COLWID, "#6"); // + case 7: return snprintf(outbuf, COLWID, "#7"); // + case 8: return snprintf(outbuf, COLWID, "#8"); // + case 9: return snprintf(outbuf, COLWID, "#9"); // + default: return snprintf(outbuf, COLWID, "?"); // unknown value + } +} +// Based on "type", FreeBSD would do: +// REALTIME "real:%u", prio +// NORMAL "normal" +// IDLE "idle:%u", prio +// default "%u:%u", type, prio +// We just print the priority, and have other keywords for type. +static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%ld", pp->rtprio); +} +static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->sched==-1) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%ld", pp->sched); +} + +//////////////////////////////////////////////////////////////////////////////// + +static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){ +/* + * Unix98 says "blank if running" and also "no blanks"! :-( + * Unix98 also says to use '-' if something is meaningless. + * Digital uses both '*' and '-', with undocumented differences. + * (the '*' for -1 (rare) and the '-' for 0) + * Sun claims to use a blank AND use '-', in the same man page. + * Perhaps "blank" should mean '-'. + * + * AIX uses '-' for running processes, the location when there is + * only one thread waiting in the kernel, and '*' when there is + * more than one thread waiting in the kernel. + * + * The output should be truncated to maximal columns width -- overflow + * is not supported for the "wchan". + */ + const char *w; + size_t len; + if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1; + if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu); + w = lookup_wchan(pp->wchan, pp->XXXID); + len = strlen(w); + if(len>max_rightward) len=max_rightward; + memcpy(outbuf, w, len); + outbuf[len] = '\0'; + return len; +} + +static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){ +/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too. + * + * We use '-' for running processes, the location when there is + * only one thread waiting in the kernel, and '*' when there is + * more than one thread waiting in the kernel. + * + * The output should be truncated to maximal columns width -- overflow + * is not supported for the "wchan". + */ + const char *w; + size_t len; + if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1; + w = lookup_wchan(pp->wchan, pp->XXXID); + len = strlen(w); + if(len>max_rightward) len=max_rightward; + memcpy(outbuf, w, len); + outbuf[len] = '\0'; + return len; +} + +static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){ + if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1; + return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu); +} + +/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */ +/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */ +static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS); +} + +/* Unix98: format is unspecified, but must match that used by who(1). */ +static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV); +} + +#if 0 +/* This BSD state display may contain spaces, which is illegal. */ +static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%s", status(pp)); +} +#endif + +// This state display is Unix98 compliant and has lots of info like BSD. +static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){ + int end = 0; + outbuf[end++] = pp->state; +// if(pp->rss==0 && pp->state!='Z') outbuf[end++] = 'W'; // useless "swapped out" + if(pp->nice < 0) outbuf[end++] = '<'; + if(pp->nice > 0) outbuf[end++] = 'N'; +// In this order, NetBSD would add: +// traced 'X' +// systrace 'x' +// exiting 'E' (not printed for zombies) +// vforked 'V' +// system 'K' (and do not print 'L' too) + if(pp->vm_lock) outbuf[end++] = 'L'; + if(pp->session == pp->tgid) outbuf[end++] = 's'; // session leader + if(pp->nlwp > 1) outbuf[end++] = 'l'; // multi-threaded + if(pp->pgrp == pp->tpgid) outbuf[end++] = '+'; // in foreground process group + outbuf[end] = '\0'; + return end; +} + +/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */ +static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){ + outbuf[0] = pp->state; + outbuf[1] = '\0'; + return 1; +} + +static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){ + /* Unix98 requires octal flags */ + /* this user-hostile and volatile junk gets 1 character */ + return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>6U)&0x7U); +} + +// plus these: euid,ruid,egroup,rgroup (elsewhere in this file) + +/*********** non-standard ***********/ + +/*** BSD +sess session pointer +(SCO has:Process session leader ID as a decimal value. (SESSION)) +jobc job control count +cpu short-term cpu usage factor (for scheduling) +sl sleep time (in seconds; 127 = infinity) +re core residency time (in seconds; 127 = infinity) +pagein pageins (same as majflt) +lim soft memory limit +tsiz text size (in Kbytes) +***/ + +static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack)); +} + +static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp)); +} + +static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip)); +} + +/* This function helps print old-style time formats */ +static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) { + if(!t) return snprintf(dst, COLWID, " -"); + if(t == ~0ULL) return snprintf(dst, COLWID, " xx"); + if((long long)(t-=rel) < 0) t=0ULL; + if(t>9999ULL) return snprintf(dst, COLWID, "%5Lu", t/100ULL); + else return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U); +} + +static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long t; + unsigned u; + t = pp->utime + pp->stime; + if(include_dead_children) t += (pp->cutime + pp->cstime); + u = t / Hertz; + return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U); +} + +static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){ + time_t start; + time_t seconds_ago; + start = time_of_boot + pp->start_time / Hertz; + seconds_ago = seconds_since_1970 - start; + if(seconds_ago < 0) seconds_ago=0; + if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4); + else strcpy(outbuf, ctime(&start)+10); + outbuf[6] = '\0'; + return 6; +} + +static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){ + return old_time_helper(outbuf, pp->alarm, 0ULL); +} + +/* HP-UX puts this in pages and uses "vsz" for kB */ +static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024)); +} + + +/* + * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss + * I suspect some/all of those are broken. They seem to have been + * inherited by Linux and AIX from early BSD systems. FreeBSD only + * retains tsiz. The prefixed versions come from Debian. + * Sun and Digital have none of this crap. The code here comes + * from an old Linux ps, and might not be correct for ELF executables. + * + * AIX TRS size of resident-set (real memory) of text + * AIX TSIZ size of text (shared-program) image + * FreeBSD tsiz text size (in Kbytes) + * 4.3BSD NET/2 trss text resident set size (in Kbytes) + * 4.3BSD NET/2 tsiz text size (in Kbytes) + */ + +/* kB data size. See drs, tsiz & trs. */ +static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){ + long dsiz = 0; + if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", dsiz); +} + +/* kB text (code) size. See trs, dsiz & drs. */ +static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){ + long tsiz = 0; + if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", tsiz); +} + +/* kB _resident_ data size. See dsiz, tsiz & trs. */ +static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){ + long drs = 0; + if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", drs); +} + +/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */ +static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){ + long trs = 0; + if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", trs); +} + +/* approximation to: kB of address space that could end up in swap */ +static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack); +} + +/* nasty old Debian thing */ +static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->size); +} + + +static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){ + long flt = pp->min_flt; + if(include_dead_children) flt += pp->cmin_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){ + long flt = pp->maj_flt; + if(include_dead_children) flt += pp->cmaj_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->rss_rlim == RLIM_INFINITY){ + outbuf[0] = 'x'; + outbuf[1] = 'x'; + outbuf[2] = '\0'; + return 2; + } + return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10); +} + +/* should print leading tilde ('~') if process is bound to the CPU */ +static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->processor); +} + +static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%lu", pp->vm_rss); +} + +/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */ +static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long pmem = 0; + pmem = pp->vm_rss * 1000ULL / kb_main_total; + if (pmem > 999) pmem = 999; + return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10)); +} + +static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){ + time_t t; + t = time_of_boot + pp->start_time / Hertz; + return snprintf(outbuf, COLWID, "%24.24s", ctime(&t)); +} + +/* Unix98 specifies a STIME header for a column that shows the start + * time of the process, but does not specify a format or format specifier. + * From the general Unix98 rules, we know there must not be any spaces. + * Most systems violate that rule, though the Solaris documentation + * claims to print the column without spaces. (NOT!) + * + * So this isn't broken, but could be renamed to u98_std_stime, + * as long as it still shows as STIME when using the -f option. + */ +static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){ + struct tm *proc_time; + struct tm *our_time; + time_t t; + const char *fmt; + int tm_year; + int tm_yday; + our_time = localtime(&seconds_since_1970); /* not reentrant */ + tm_year = our_time->tm_year; + tm_yday = our_time->tm_yday; + t = time_of_boot + pp->start_time / Hertz; + proc_time = localtime(&t); /* not reentrant, this corrupts our_time */ + fmt = "%H:%M"; /* 03:02 23:59 */ + if(tm_yday != proc_time->tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */ + if(tm_year != proc_time->tm_year) fmt = "%Y"; /* 1991 2001 */ + return strftime(outbuf, 42, fmt, proc_time); +} + +static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){ + time_t t; + char *str; + t = time_of_boot + pp->start_time / Hertz; + str = ctime(&t); + if(str[8]==' ') str[8]='0'; + if(str[11]==' ') str[11]='0'; + if((unsigned long)t+60*60*24 > seconds_since_1970) + return snprintf(outbuf, COLWID, "%8.8s", str+11); + return snprintf(outbuf, COLWID, " %6.6s", str+4); +} + + +#ifdef SIGNAL_STRING +static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){ + long len = 0; + len = strlen(sig); + if(wide_signals){ + if(len>8) return snprintf(outbuf, COLWID, "%s", sig); + return snprintf(outbuf, COLWID, "00000000%s", sig); + } + if(len-strspn(sig,"0") > 8) + return snprintf(outbuf, COLWID, "<%s", sig+len-8); + return snprintf(outbuf, COLWID, "%s", sig+len-8); +} +#else +static int help_pr_sig(unsigned long long sig){ + if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig); + if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL); + return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL); +} +#endif + +// This one is always thread-specific pending. (from Dragonfly BSD) +static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->_sigpnd); +} +// This one is (wrongly?) thread-specific when printing thread lines, +// but process-pending otherwise. +static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->signal); +} +static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->blocked); +} +static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->sigignore); +} +static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->sigcatch); +} + + +//////////////////////////////////////////////////////////////////////////////// + +/* + * internal terms: ruid euid suid fuid + * kernel vars: uid euid suid fsuid + * command args: ruid uid svuid n/a + */ + +static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->egid); +} +static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->rgid); +} +static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->sgid); +} +static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->fgid); +} + +static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->euid); +} +static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->ruid); +} +static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->suid); +} +static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->fuid); +} + +// The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition) +// requires that user and group names print as decimal numbers if there is +// not enough room in the column, so tough luck if you don't like it. +// +// The UNIX and POSIX way to change column width is to rename it: +// ps -o pid,user=CumbersomeUserNames -o comm +// The easy way is to directly specify the desired width: +// ps -o pid,user:19,comm +// +static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){ + if(!user_is_number){ + int rightward = OUTBUF_SIZE; /* max cells */ + int len; /* real cells */ + + escape_str(outbuf, name, OUTBUF_SIZE, &rightward); + len = OUTBUF_SIZE-rightward; + + if(len <= (int)max_rightward) + return len; /* returns number of cells */ + } + return snprintf(outbuf, COLWID, "%u", u); +} + +static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->ruser, pp->ruid); +} +static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->euser, pp->euid); +} +static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->fuser, pp->fuid); +} +static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->suser, pp->suid); +} + +static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->egroup, pp->egid); +} +static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->rgroup, pp->rgid); +} +static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->fgroup, pp->fgid); +} +static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->sgroup, pp->sgid); +} + +////////////////////////////////////////////////////////////////////////////////// + +// TID tid LWP lwp SPID spid +static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->tid); +} +// thcount THCNT +static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->nlwp); +} + +static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->session); +} +static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->tpgid); +} + + +/* SGI uses "cpu" to print the processor ID with header "P" */ +static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){ /* FIXME */ + if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor); + return snprintf(outbuf, COLWID, "*"); +} + + +/****************** FLASK & seLinux security stuff **********************/ +// move the bulk of this to libproc sometime + +static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){ + char filename[48]; + size_t len; + ssize_t num_read; + int fd; + +// wchan file is suitable for testing +//snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid); +snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid); + + fd = open(filename, O_RDONLY, 0); + if(likely(fd==-1)) goto fail; + num_read = read(fd, outbuf, 666); + close(fd); + if(unlikely(num_read<=0)) goto fail; + outbuf[num_read] = '\0'; + + len = 0; + while(outbuf[len]>' ' && outbuf[len]<='~') len++; + outbuf[len] = '\0'; + if(len) return len; + +fail: + outbuf[0] = '-'; + outbuf[1] = '\0'; + return 1; +} + +#if 0 +// This needs more study, considering: +// 1. the static linking option (maybe disable this in that case) +// 2. the -z and -Z option issue +// 3. width of output +static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){ + static int (*ps_getpidcon)(pid_t pid, char **context) = 0; + static int tried_load = 0; + size_t len; + char *context; + + if(!ps_getpidcon && !tried_load){ + void *handle = dlopen("libselinux.so.1", RTLD_NOW); + if(handle){ + dlerror(); + ps_getpidcon = dlsym(handle, "getpidcon"); + if(dlerror()) + ps_getpidcon = 0; + } + tried_load++; + } + if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){ + size_t max_len = OUTBUF_SIZE-1; + len = strlen(context); + if(len > max_len) len = max_len; + memcpy(outbuf, context, len); + outbuf[len] = '\0'; + free(context); + }else{ + outbuf[0] = '-'; + outbuf[1] = '\0'; + len = 1; + } + return len; +} +#endif + + +////////////////////////////// Test code ///////////////////////////////// + +// like "args" +static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"[123456789-12345] ","ps","123456789-123456"}; + (void)pp; + snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]); + return strlen(outbuf); +} +static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"unlimited", "[123456789-12345] ","ps","123456789-123456"}; + (void)pp; + snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]); + return strlen(outbuf); +} + +// like "etime" +static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]); +} +static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]); +} + +// like "tty" +static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]); +} +static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]); +} + +/***************************************************************************/ +/*************************** other stuff ***********************************/ + +/* + * Old header specifications. + * + * short Up " PID TTY STAT TIME COMMAND" + * long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND + * user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND + * jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND + * sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND + * vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND + * m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND + * regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND + */ + +/* + * Unix98 requires that the heading for tty is TT, though XPG4, Digital, + * and BSD use TTY. The Unix98 headers are: + * args,comm,etime,group,nice,pcpu,pgid + * pid,ppid,rgroup,ruser,time,tty,user,vsz + * + * BSD c: "command" becomes accounting name ("comm" or "ucomm") + * BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number) + */ + +/* Justification control for flags field. */ +#define USER CF_USER // left if text, right if numeric +#define LEFT CF_LEFT +#define RIGHT CF_RIGHT +#define UNLIMITED CF_UNLIMITED +#define WCHAN CF_WCHAN // left if text, right if numeric +#define SIGNAL CF_SIGNAL // right in 9, or 16 if room +#define PIDMAX CF_PIDMAX +#define TO CF_PRINT_THREAD_ONLY +#define PO CF_PRINT_PROCESS_ONLY +#define ET CF_PRINT_EVERY_TIME +#define AN CF_PRINT_AS_NEEDED // no idea + +/* short names to save space */ +#define MEM PROC_FILLMEM /* read statm */ +#define ARG PROC_FILLARG /* read cmdline (cleared if c option) */ +#define COM PROC_FILLCOM /* read cmdline (cleared if not -f option) */ +#define ENV PROC_FILLENV /* read environ */ +#define USR PROC_FILLUSR /* uid_t -> user names */ +#define GRP PROC_FILLGRP /* gid_t -> group names */ +#define WCH PROC_FILLWCHAN /* do WCHAN lookup */ + + +/* TODO + * pull out annoying BSD aliases into another table (to macro table?) + * add sorting functions here (to unify names) + */ + +/* temporary hack -- mark new stuff grabbed from Debian ps */ +#define LNx LNX + +/* there are about 211 listed */ + +/* Many of these are placeholders for unsupported options. */ +static const format_struct format_array[] = { +/* code header print() sort() width need vendor flags */ +{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, ET|RIGHT}, /*pcpu*/ +{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, PO|RIGHT}, /*pmem*/ +{"_left", "LLLLLLLL", pr_t_left, sr_nop, 8, 0, TST, ET|LEFT}, +{"_left2", "L2L2L2L2", pr_t_left2, sr_nop, 8, 0, TST, ET|LEFT}, +{"_right", "RRRRRRRRRRR", pr_t_right, sr_nop, 11, 0, TST, ET|RIGHT}, +{"_right2", "R2R2R2R2R2R", pr_t_right2, sr_nop, 11, 0, TST, ET|RIGHT}, +{"_unlimited","U", pr_t_unlimited, sr_nop, 16, 0, TST, ET|UNLIMITED}, +{"_unlimited2","U2", pr_t_unlimited2, sr_nop, 16, 0, TST, ET|UNLIMITED}, +{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, /*acflg*/ +{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*acflag*/ +{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, +{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT}, +{"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT}, +{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT}, +{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/ +{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */ +{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/ +{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT}, +{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT}, +{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT}, +{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT}, +{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/ +{"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT}, +{"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/ +{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT}, +{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/ +{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT}, +{"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT}, +{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/ +{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/ +{"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT}, +{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/ +{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */ +{"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide! +{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/ +{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT}, +{"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts? +{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, +{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, AN|RIGHT}, +{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, AN|LEFT}, +{"drs", "DRS", pr_drs, sr_drs, 5, MEM, LNX, PO|RIGHT}, +{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, PO|RIGHT}, +{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, ET|RIGHT}, +{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, GRP, LNX, ET|USER}, +{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, TO|RIGHT}, +{"emul", "EMUL", pr_nop, sr_nop, 13, 0, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */ +{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT}, +{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED}, +{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT}, +{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, ET|RIGHT}, /* was 7 wide */ +{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT}, +{"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER}, +{"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/ +{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT}, +{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER}, +{"flag", "F", pr_flag, sr_flags, 1, 0, DEC, ET|RIGHT}, +{"flags", "F", pr_flag, sr_flags, 1, 0, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */ +{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, PO|LEFT}, +{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT}, +{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER}, +{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT}, +{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER}, +{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT}, +{"fuser", "FUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER}, +{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, ET|RIGHT}, +{"group", "GROUP", pr_egroup, sr_egroup, 8, GRP, U98, ET|USER}, +{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigignore*/ +{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/ +{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/ +{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT}, +{"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT}, +{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, +{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"label", "LABEL", pr_context, sr_nop, 31, 0, SGI, ET|LEFT}, +{"lastcpu", "C", pr_psr, sr_nop, 3, 0, BSD, TO|RIGHT}, // DragonFly +{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT}, +{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */ +{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/ +{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT}, +{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, ET|RIGHT}, +{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, ET|RIGHT}, /* login ID */ +{"luser", "LUSER", pr_nop, sr_nop, 8, USR, LNX, ET|USER}, /* login USER */ +{"lwp", "LWP", pr_thread, sr_tid, 5, 0, SUN, TO|PIDMAX|RIGHT}, +{"m_drs", "DRS", pr_drs, sr_drs, 5, MEM, LNx, PO|RIGHT}, +{"m_dt", "DT", pr_nop, sr_dt, 4, MEM, LNx, PO|RIGHT}, +{"m_lrs", "LRS", pr_nop, sr_lrs, 5, MEM, LNx, PO|RIGHT}, +{"m_resident", "RES", pr_nop, sr_resident, 5,MEM, LNx, PO|RIGHT}, +{"m_share", "SHRD", pr_nop, sr_share, 5, MEM, LNx, PO|RIGHT}, +{"m_size", "SIZE", pr_size, sr_size, 5, MEM, LNX, PO|RIGHT}, +{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, PO|RIGHT}, +{"m_trs", "TRS", pr_trs, sr_trs, 5, MEM, LNx, PO|RIGHT}, +{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, AN|RIGHT}, +{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT}, +{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT}, +{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT}, +{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT}, +{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT}, +{"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */ +{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/ +{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/ +{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, +{"nlwp", "NLWP", pr_nlwp, sr_nlwp, 4, 0, SUN, PO|RIGHT}, +{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*nsigs*/ +{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*nsignals*/ +{"nswap", "NSWAP", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, +{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, +{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, TO|RIGHT}, +{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, TO|RIGHT}, +{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, PO|RIGHT}, +{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*oublock*/ +{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*oublk*/ +{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT}, +{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT}, +{"pagein", "PAGEIN", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT}, +{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, ET|RIGHT}, /*%cpu*/ +{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /*sig*/ +{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT}, +{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT}, +{"pid", "PID", pr_pid, sr_tgid, 5, 0, U98, PO|PIDMAX|RIGHT}, +{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, PO|RIGHT}, /*%mem*/ +{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT}, +{"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT}, +{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, PO|PIDMAX|RIGHT}, +{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, TO|RIGHT}, +{"pri_api", "API", pr_pri_api, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"pri_bar", "BAR", pr_pri_bar, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"pri_baz", "BAZ", pr_pri_baz, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"pri_foo", "FOO", pr_pri_foo, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, TO|RIGHT}, +{"prmgrp", "PRMGRP", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT}, +{"prmid", "PRMID", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT}, +{"project", "PROJECT", pr_nop, sr_nop, 12, 0, SUN, PO|LEFT}, // see prm* andctid +{"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT}, +{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT}, +{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT}, +{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, +{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, +{"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT}, +{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT}, +{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */ +{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */ +{"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/ +{"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/ +{"rtprio", "RTPRIO", pr_rtprio, sr_rtprio, 6, 0, BSD, TO|RIGHT}, +{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, ET|RIGHT}, +{"ruser", "RUSER", pr_ruser, sr_ruser, 8, USR, U98, ET|USER}, +{"s", "S", pr_s, sr_state, 1, 0, SUN, TO|LEFT}, /*stat,state*/ +{"sched", "SCH", pr_sched, sr_sched, 3, 0, AIX, TO|RIGHT}, +{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, AN|RIGHT}, /* man page misspelling of scount? */ +{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */ +{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, +{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, PO|PIDMAX|RIGHT}, +{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, TO|RIGHT}, /* "cpu" number */ +{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, PO|LEFT}, /* SZ:RSS */ +{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, ET|RIGHT}, +{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER}, +{"share", "-", pr_nop, sr_share, 1, MEM, LNX, PO|RIGHT}, +{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */ +{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */ +{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, TO|SIGNAL}, +{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, TO|SIGNAL}, +{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, TO|SIGNAL}, +{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, ET|SIGNAL}, +{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/ +{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/ +{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/ +{"size", "SZ", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT}, +{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT}, +{"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT}, +{"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/ +{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, ET|RIGHT}, +{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, PO|RIGHT}, +{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*stackp*/ +{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, ET|RIGHT}, +{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, TO|LEFT}, /*state,s*/ +{"state", "S", pr_s, sr_state, 1, 0, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */ +{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, +{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */ +{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT}, +{"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER}, +{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT}, +{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER}, +{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, ET|RIGHT}, +{"svuser", "SVUSER", pr_suser, sr_suser, 8, USR, LNX, ET|USER}, +{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, ET|RIGHT}, +{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, PO|RIGHT}, +{"taskid", "TASKID", pr_nop, sr_nop, 5, 0, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID? +{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, +{"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT}, +{"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT}, +{"time", "TIME", pr_time, sr_nop, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */ +{"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era +{"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era +{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT}, +{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, PO|PIDMAX|RIGHT}, +{"trs", "TRS", pr_trs, sr_trs, 4, MEM, AIX, PO|RIGHT}, +{"trss", "TRSS", pr_trs, sr_trs, 4, MEM, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */ +{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT}, +{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, PO|PIDMAX|RIGHT}, +{"tsid", "TSID", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT}, +{"tsig", "PENDING", pr_tsig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */ +{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, PO|RIGHT}, +{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, PO|LEFT}, +{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */ +{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT}, +{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT}, +{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, +{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/ +{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/ +{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT}, +{"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER}, +{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, +{"uname", "USER", pr_euser, sr_euser, 8, USR, DEC, ET|USER}, /* man page misspelling of user? */ +{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/ +{"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */ +{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT}, +{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/ +{"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C" +{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT}, +{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT}, +{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT}, +{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT}, +{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, PO|RIGHT}, +{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, PO|RIGHT}, +{"vsize", "VSZ", pr_vsz, sr_vsize, 6, 0, DEC, PO|RIGHT}, /*vsz*/ +{"vsz", "VSZ", pr_vsz, sr_vm_size, 6, 0, U98, PO|RIGHT}, /*vsize*/ +{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, WCH, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */ +{"wname", "WCHAN", pr_wname, sr_nop, 6, WCH, SGI, TO|WCHAN}, /* opposite of nwchan */ +{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, +{"zone", "ZONE", pr_context, sr_nop, 31, 0, SUN, ET|LEFT}, // Solaris zone == Linux context? +{"zoneid", "ZONEID", pr_nop, sr_nop, 31, 0, SUN, ET|RIGHT},// Linux only offers context names +{"~", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT} /* NULL would ruin alphabetical order */ +}; + +#undef USER +#undef LEFT +#undef RIGHT +#undef UNLIMITED +#undef WCHAN +#undef SIGNAL +#undef PIDMAX +#undef PO +#undef TO +#undef AN +#undef ET + +static const int format_array_count = sizeof(format_array)/sizeof(format_struct); + + +/****************************** Macro formats *******************************/ +/* First X field may be NR, which is p->start_code>>26 printed with %2ld */ +/* That seems useless though, and Debian already killed it. */ +/* The ones marked "Digital" have the name defined, not just the data. */ +static const macro_struct macro_array[] = { +{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */ +{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */ +{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */ +{"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */ +{"END_SYS5", "state,tname,time,command"}, /* trailer for -O */ +{"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */ + +{"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */ +{"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */ +{"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */ +{"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */ +{"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */ + +{"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */ +{"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */ +{"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */ +{"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */ +{"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */ +{"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */ + +{"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */ + +{"FLASK_context", "pid,context,command"}, /* Flask Linux context, --context */ + +{"HP_", "pid,tty,time,comm"}, /* HP default */ +{"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */ +{"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */ +{"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */ + +{"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */ +{"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */ +{"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */ +{"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */ + +{"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */ +{"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */ +{"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */ +{"OL_m", "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */ +{"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */ +{"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */ +{"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */ + +{"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */ +{"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */ +{"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */ +{"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */ +{"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */ +{"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */ + +{"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */ +{"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */ +{"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */ + +{"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */ +{"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */ +{"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */ + +{"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */ +{"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */ +{"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */ +{"~", "~"} /* NULL would ruin alphabetical order */ +}; + +static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct); + + +/*************************** AIX formats ********************/ +/* Convert AIX format codes to normal format specifiers. */ +static const aix_struct aix_array[] = { +{'C', "pcpu", "%CPU"}, +{'G', "group", "GROUP"}, +{'P', "ppid", "PPID"}, +{'U', "user", "USER"}, +{'a', "args", "COMMAND"}, +{'c', "comm", "COMMAND"}, +{'g', "rgroup", "RGROUP"}, +{'n', "nice", "NI"}, +{'p', "pid", "PID"}, +{'r', "pgid", "PGID"}, +{'t', "etime", "ELAPSED"}, +{'u', "ruser", "RUSER"}, +{'x', "time", "TIME"}, +{'y', "tty", "TTY"}, +{'z', "vsz", "VSZ"}, +{'~', "~", "~"} /* NULL would ruin alphabetical order */ +}; +static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct); + + +/********************* sorting ***************************/ +/* Convert short sorting codes to normal format specifiers. */ +static const shortsort_struct shortsort_array[] = { +{'C', "pcpu" }, +{'G', "tpgid" }, +{'J', "cstime" }, +/* {'K', "stime" }, */ /* conflict, system vs. start time */ +{'M', "maj_flt" }, +{'N', "cmaj_flt" }, +{'P', "ppid" }, +{'R', "resident" }, +{'S', "share" }, +{'T', "start_time" }, +{'U', "uid" }, /* euid */ +{'c', "cmd" }, +{'f', "flags" }, +{'g', "pgrp" }, +{'j', "cutime" }, +{'k', "utime" }, +{'m', "min_flt" }, +{'n', "cmin_flt" }, +{'o', "session" }, +{'p', "pid" }, +{'r', "rss" }, +{'s', "size" }, +{'t', "tty" }, +{'u', "user" }, +{'v', "vsize" }, +{'y', "priority" }, /* nice */ +{'~', "~" } /* NULL would ruin alphabetical order */ +}; +static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct); + + +/*********** print format_array **********/ +/* called by the parser in another file */ +void print_format_specifiers(void){ + const format_struct *walk = format_array; + while(*(walk->spec) != '~'){ + if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head); + walk++; + } +} + +/************ comparison functions for bsearch *************/ + +static int compare_format_structs(const void *a, const void *b){ + return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec); +} + +static int compare_macro_structs(const void *a, const void *b){ + return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec); +} + +/******** look up structs as needed by the sort & format parsers ******/ + +const shortsort_struct *search_shortsort_array(const int findme){ + const shortsort_struct *walk = shortsort_array; + while(walk->desc != '~'){ + if(walk->desc == findme) return walk; + walk++; + } + return NULL; +} + +const aix_struct *search_aix_array(const int findme){ + const aix_struct *walk = aix_array; + while(walk->desc != '~'){ + if(walk->desc == findme) return walk; + walk++; + } + return NULL; +} + +const format_struct *search_format_array(const char *findme){ + format_struct key; + key.spec = findme; + return bsearch(&key, format_array, format_array_count, + sizeof(format_struct), compare_format_structs + ); +} + +const macro_struct *search_macro_array(const char *findme){ + macro_struct key; + key.spec = findme; + return bsearch(&key, macro_array, macro_array_count, + sizeof(macro_struct), compare_macro_structs + ); +} + +static unsigned int active_cols; /* some multiple of screen_cols */ + +/***** Last chance, avoid needless trunctuation. */ +static void check_header_width(void){ + format_node *walk = format_list; + unsigned int total = 0; + int was_normal = 0; + unsigned int i = 0; + unsigned int sigs = 0; + while(walk){ + switch((walk->flags) & CF_JUST_MASK){ + default: + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case CF_SIGNAL: + sigs++; + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case CF_UNLIMITED: /* could chop this a bit */ + if(walk->next) total += walk->width; + else total += 3; /* not strlen(walk->name) */ + total += was_normal; + was_normal = 1; + break; + case 0: /* AIX */ + total += walk->width; + was_normal = 0; + break; + } + walk = walk->next; + } + for(;;){ + i++; + active_cols = screen_cols * i; + if(active_cols>=total) break; + if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */ + } + wide_signals = (total+sigs*7 <= active_cols); +} + + +/********** show one process (NULL proc prints header) **********/ + +//#define SPACE_AMOUNT page_size +#define SPACE_AMOUNT 144 + +static char *saved_outbuf; + +void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){ + /* unknown: maybe set correct & actual to 1, remove +/- 1 below */ + int correct = 0; /* screen position we should be at */ + int actual = 0; /* screen position we are at */ + int amount = 0; /* amount of text that this data is */ + int leftpad = 0; /* amount of space this column _could_ need */ + int space = 0; /* amount of space we actually need to print */ + int dospace = 0; /* previous column determined that we need a space */ + int legit = 0; /* legitimately stolen extra space */ + int sz = 0; /* real size of data in outbuffer */ + int tmpspace = 0; + char *restrict const outbuf = saved_outbuf; + static int did_stuff = 0; /* have we ever printed anything? */ + + if(unlikely(-1==(long)p)){ /* true only once, at the end */ + if(did_stuff) return; + /* have _never_ printed anything, but might need a header */ + if(!--lines_to_next_header){ + lines_to_next_header = header_gap; + show_one_proc(NULL,fmt); + } + /* fprintf(stderr, "No processes available.\n"); */ /* legal? */ + exit(1); + } + if(likely(p)){ /* not header, maybe we should call ourselves for it */ + if(unlikely(!--lines_to_next_header)){ + lines_to_next_header = header_gap; + show_one_proc(NULL,fmt); + } + } + did_stuff = 1; + if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n"); + + /* print row start sequence */ + for(;;){ + legit = 0; + /* set width suggestion which might be ignored */ +// if(likely(fmt->next)) max_rightward = fmt->width; +// else max_rightward = active_cols-((correct>actual) ? correct : actual); + + if(likely(fmt->next)){ + max_rightward = fmt->width; + tmpspace = 0; + }else{ + tmpspace = correct-actual; + if (tmpspace<1){ + tmpspace = dospace; + max_rightward = active_cols-actual-tmpspace; + }else{ + max_rightward = active_cols - ( (correct>actual) ? correct : actual ); + } + } + max_leftward = fmt->width + actual - correct; /* TODO check this */ + +// fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n", +// active_cols, max_rightward, max_leftward, actual, correct); + + /* prepare data and calculate leftpad */ + if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p); + else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */ + + switch((fmt->flags) & CF_JUST_MASK){ + case 0: /* for AIX, assigned outside this file */ + leftpad = 0; + break; + case CF_LEFT: /* bad */ + leftpad = 0; + break; + case CF_RIGHT: /* OK */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + case CF_SIGNAL: + /* if the screen is wide enough, use full 16-character output */ + if(wide_signals){ + leftpad = 16 - amount; + legit = 7; + }else{ + leftpad = 9 - amount; + } + if(leftpad < 0) leftpad = 0; + break; + case CF_USER: /* bad */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + if(!user_is_number) leftpad = 0; + break; + case CF_WCHAN: /* bad */ + if(wchan_is_number){ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + }else{ + if ((active_cols-actual-tmpspace)<1) + outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */ + leftpad = 0; + break; + } + case CF_UNLIMITED: + { + if(active_cols-actual-tmpspace < 1) + outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */ + leftpad = 0; + break; + } + default: + fprintf(stderr, "bad alignment code\n"); + break; + } + /* At this point: + * + * correct from previous column + * actual from previous column + * amount not needed (garbage due to chopping) + * leftpad left padding for this column alone (not make-up or gap) + * space not needed (will recalculate now) + * dospace if we require space between this and the prior column + * legit space we were allowed to steal, and thus did steal + */ + space = correct - actual + leftpad; + if(space<1) space=dospace; + if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT; // only so much available + + /* real size -- don't forget in 'amount' is number of cells */ + sz = strlen(outbuf); + + /* print data, set x position stuff */ + if(unlikely(!fmt->next)){ + /* Last column. Write padding + data + newline all together. */ + outbuf[sz] = '\n'; + fwrite(outbuf-space, space+sz+1, 1, stdout); + break; + } + /* Not the last column. Write padding + data together. */ + fwrite(outbuf-space, space+sz, 1, stdout); + actual += space+amount; + correct += fmt->width; + correct += legit; /* adjust for SIGNAL expansion */ + if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */ + correct++; + dospace = 1; + }else{ + dospace = 0; + } + fmt = fmt->next; + /* At this point: + * + * correct screen position we should be at + * actual screen position we are at + * amount not needed + * leftpad not needed + * space not needed + * dospace if have determined that we need a space next time + * legit not needed + */ + } +} + + +#ifdef TESTING +static void sanity_check(void){ + format_struct *fs = format_array; + while((fs->spec)[0] != '~'){ + if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec); + fs++; + } +} +#endif + + +void init_output(void){ + int outbuf_pages; + char *outbuf; + + switch(page_size){ + case 65536: page_shift = 16; break; + case 32768: page_shift = 15; break; + case 16384: page_shift = 14; break; + case 8192: page_shift = 13; break; + default: fprintf(stderr, "Unknown page size! (assume 4096)\n"); + case 4096: page_shift = 12; break; + case 2048: page_shift = 11; break; + case 1024: page_shift = 10; break; + } + + // add page_size-1 to round up + outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size; + outbuf = mmap( + 0, + page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0 + ); + memset(outbuf, ' ', SPACE_AMOUNT); + if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ); + mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page + saved_outbuf = outbuf + SPACE_AMOUNT; + // available space: page_size*outbuf_pages-SPACE_AMOUNT + + seconds_since_1970 = time(NULL); + time_of_boot = seconds_since_1970 - seconds_since_boot; + + meminfo(); + + check_header_width(); +} diff --git a/ps/p b/ps/p new file mode 100755 index 0000000..10064fa --- /dev/null +++ b/ps/p @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with: ps "pid,user pcpu,pmem" +# The "$@" won't break that into 2 arguments. +# +LD_LIBRARY_PATH=../proc exec ./ps "$@" diff --git a/ps/parser.c b/ps/parser.c new file mode 100644 index 0000000..5ad9035 --- /dev/null +++ b/ps/parser.c @@ -0,0 +1,1246 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* Ought to have debug print stuff like this: + * #define Print(fmt, args...) printf("Debug: " fmt, ## args) + */ + +#include +#include +#include + +/* username lookups */ +#include +#include +#include + +#include +#include + +#include "common.h" +#include "../proc/version.h" + +#define ARG_GNU 0 +#define ARG_END 1 +#define ARG_PGRP 2 +#define ARG_SYSV 3 +#define ARG_PID 4 +#define ARG_BSD 5 +#define ARG_FAIL 6 +#define ARG_SESS 7 + +static int w_count = 0; + +static int ps_argc; /* global argc */ +static char **ps_argv; /* global argv */ +static int thisarg; /* index into ps_argv */ +static char *flagptr; /* current location in ps_argv[thisarg] */ +static int not_pure_unix = 0; /* set by BSD and GNU options */ +static int force_bsd = 0; /* set when normal parsing fails */ + +#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x))\ + return "The " x " option is exclusive." + + +/********** utility functions **********/ + +/* + * Both "-Oppid" and "-O ppid" should be legal, though Unix98 + * does not require it. BSD and Digital Unix allow both. + * Return the argument or NULL; + */ +static const char *get_opt_arg(void){ + if(*(flagptr+1)){ /* argument is part of ps_argv[thisarg] */ + not_pure_unix = 1; + return flagptr+1; + } + if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */ + /* argument follows ps_argv[thisarg] */ + if(*(ps_argv[thisarg+1]) == '\0') return NULL; + return ps_argv[++thisarg]; +} + +/********** parse lists (of UID, tty, GID, PID...) **********/ + +static const char *parse_pid(char *str, sel_union *ret){ + char *endp; + unsigned long num; + static const char pidrange[] = "Process ID out of range."; + static const char pidsyntax[] = "Process ID list syntax error."; + num = strtoul(str, &endp, 0); + if(*endp != '\0') return pidsyntax; + if(num<1) return pidrange; + if(num > 0x7fffffffUL) return pidrange; + ret->pid = num; + return 0; +} + +static const char *parse_uid(char *str, sel_union *ret){ + struct passwd *passwd_data; + char *endp; + unsigned long num; + static const char uidrange[] = "User ID out of range."; + static const char uidexist[] = "User name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + passwd_data = getpwnam(str); + if(!passwd_data) return uidexist; + num = passwd_data->pw_uid; + } + if(num > 0xfffffffeUL) return uidrange; + ret->uid = num; + return 0; +} + +static const char *parse_gid(char *str, sel_union *ret){ + struct group *group_data; + char *endp; + unsigned long num; + static const char gidrange[] = "Group ID out of range."; + static const char gidexist[] = "Group name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + group_data = getgrnam(str); + if(!group_data) return gidexist; + num = group_data->gr_gid; + } + if(num > 0xfffffffeUL) return gidrange; + ret->gid = num; + return 0; +} + +static const char *parse_cmd(char *str, sel_union *ret){ + strncpy(ret->cmd, str, sizeof ret->cmd); // strncpy pads to end + return 0; +} + +static const char *parse_tty(char *str, sel_union *ret){ + struct stat sbuf; + static const char missing[] = "TTY could not be found."; + static const char not_tty[] = "List member was not a TTY."; + char path[4096]; + if(str[0]=='/'){ + if(stat(str, &sbuf) >= 0) goto found_it; + return missing; + } +#define lookup(p) \ + snprintf(path,4096,p,str); \ + if(stat(path, &sbuf) >= 0) goto found_it + + lookup("/dev/pts/%s"); /* New Unix98 ptys go first */ + lookup("/dev/%s"); + lookup("/dev/tty%s"); + lookup("/dev/pty%s"); + lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */ + if(!strcmp(str,"-")){ /* "-" means no tty (from AIX) */ + ret->tty = 0; /* processes w/o tty */ + return 0; + } + if(!strcmp(str,"?")){ /* "?" means no tty, which bash eats (Reno BSD?) */ + ret->tty = 0; /* processes w/o tty */ + return 0; + } + if(!*(str+1) && (stat(str,&sbuf)>=0)){ /* Kludge! Assume bash ate '?'. */ + ret->tty = 0; /* processes w/o tty */ + return 0; + } +#undef lookup + return missing; +found_it: + if(!S_ISCHR(sbuf.st_mode)) return not_tty; + ret->tty = sbuf.st_rdev; + return 0; +} + +/* + * Used to parse lists in a generic way. (function pointers) + */ +static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){ + selection_node *node; + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t," */ + char *walk; + int items; + int need_item; + const char *err; /* error code that could or did happen */ + /*** prepare to operate ***/ + node = malloc(sizeof(selection_node)); + node->u = malloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */ + node->n = 0; + buf = malloc(strlen(arg)+1); + strcpy(buf, arg); + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + err = "Improper list."; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\0': + if(need_item) goto parse_error; + need_item=1; + break; + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); + if(need_item) goto parse_error; + node->n = items; + /*** actually parse the list ***/ + walk = buf; + while(items--){ + sep_loc = strpbrk(walk," ,\t"); + if(sep_loc) *sep_loc = '\0'; + if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + node->next = selection_list; + selection_list = node; + return NULL; +parse_error: + free(buf); + free(node->u); + free(node); + return err; +} + +/***************** parse SysV options, including Unix98 *****************/ +static const char *parse_sysv_option(void){ + const char *arg; + const char *err; + + flagptr = ps_argv[thisarg]; + while(*++flagptr){ + // Find any excuse to ignore stupid Unix98 misfeatures. + // + // This list of options is ONLY for those defined by the + // "IEEE Std 1003.1, 2004 Edition", "ISO/IEC 9945:2003", + // or "Version 2 of the Single Unix Specification". + // + // It may be time to re-think the existance of this list. + // In the meantime, please do not add to it. The list is + // intended to ONLY contain flags defined by the POSIX and UNIX + // standards published by The Open Group, IEEE, and ISO. + if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX + + switch(*flagptr){ + case 'A': + trace("-A selects all processes.\n"); + all_processes = 1; + break; + case 'C': /* end */ + trace("-C select by process name.\n"); /* Why only HP/UX and us? */ + arg=get_opt_arg(); + if(!arg) return "List of command names must follow -C."; + err=parse_list(arg, parse_cmd); + if(err) return err; + selection_list->typecode = SEL_COMM; + return NULL; /* can't have any more options */ + case 'F': /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */ + trace("-F does fuller listing\n"); + format_modifiers |= FM_F; + format_flags |= FF_Uf; + unix_f_option = 1; /* does this matter? */ + break; + case 'G': /* end */ + trace("-G select by RGID (supports names)\n"); + arg=get_opt_arg(); + if(!arg) return "List of real groups must follow -G."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_RGID; + return NULL; /* can't have any more options */ + case 'H': /* another nice HP/UX feature */ + trace("-H Process hierarchy (like ASCII art forest option)\n"); + forest_type = 'u'; + break; +#if 0 + case 'J': // specify list of job IDs in hex (IRIX) -- like HP "-R" maybe? + trace("-J select by job ID\n"); // want a JID ("jid") for "-j" too + arg=get_opt_arg(); + if(!arg) return "List of jobs must follow -J."; + err=parse_list(arg, parse_jid); + if(err) return err; + selection_list->typecode = SEL_JID; + return NULL; /* can't have any more options */ +#endif + case 'L': /* */ + /* In spite of the insane 2-level thread system, Sun appears to + * have made this option Linux-compatible. If a process has N + * threads, ps will produce N lines of output. (not N+1 lines) + * Zombies are the only exception, with NLWP==0 and 1 output line. + * SCO UnixWare uses -L too. + */ + trace("-L Print LWP (thread) info.\n"); + thread_flags |= TF_U_L; +// format_modifiers |= FM_L; + break; + case 'M': // typically the SE Linux context + trace("-M Print security label for Mandatory Access Control.\n"); + format_modifiers |= FM_M; + break; + case 'N': + trace("-N negates.\n"); + negate_selection = 1; + break; + case 'O': /* end */ + trace("-O is preloaded -o.\n"); + arg=get_opt_arg(); + if(!arg) return "Format or sort specification must follow -O."; + defer_sf_option(arg, SF_U_O); + return NULL; /* can't have any more options */ + case 'P': /* SunOS 5 "psr" or unknown HP/UX feature */ + trace("-P adds columns of PRM info (HP-UX), PSR (SunOS), or capabilities (IRIX)\n"); + format_modifiers |= FM_P; + break; +#if 0 + case 'R': // unknown HP/UX feature, like IRIX "-J" maybe? + trace("-R select by PRM group\n"); + arg=get_opt_arg(); + if(!arg) return "List of PRM groups must follow -R."; + err=parse_list(arg, parse_prm); + if(err) return err; + selection_list->typecode = SEL_PRM; + return NULL; /* can't have any more options */ +#endif + case 'T': + /* IRIX 6.5 docs suggest POSIX threads get shown individually. + * This would make -T be like -L, -m, and m. (but an extra column) + * Testing (w/ normal processes) shows 1 line/process, not 2. + * Also, testing shows PID==SPID for all normal processes. + */ + trace("-T adds strange SPID column (old sproc() threads?)\n"); + thread_flags |= TF_U_T; +// format_modifiers |= FM_T; + break; + case 'U': /* end */ + trace("-U select by RUID (supports names).\n"); + arg=get_opt_arg(); + if(!arg) return "List of real groups must follow -U."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_RUID; + return NULL; /* can't have any more options */ + case 'V': /* single */ + trace("-V prints version.\n"); + exclusive("-V"); + display_version(); + exit(0); + // This must be verified against SVR4-MP. (UnixWare or Powermax) + // Leave it undocumented until that problem is solved. + case 'Z': /* full Mandatory Access Control level info */ + trace("-Z shows full MAC info\n"); + format_modifiers |= FM_M; + break; + case 'a': + trace("-a select all with a tty, but omit session leaders.\n"); + simple_select |= SS_U_a; + break; + case 'c': + /* HP-UX and SunOS 5 scheduling info modifier */ + trace("-c changes scheduling info.\n"); + format_modifiers |= FM_c; + break; + case 'd': + trace("-d select all, but omit session leaders.\n"); + simple_select |= SS_U_d; + break; + case 'e': + trace("-e selects all processes.\n"); + all_processes = 1; + break; + case 'f': + trace("-f does full listing\n"); + format_flags |= FF_Uf; + unix_f_option = 1; /* does this matter? */ + break; + case 'g': /* end */ + trace("-g selects by session leader OR by group name\n"); + arg=get_opt_arg(); + if(!arg) return "List of session leaders OR effective group names must follow -g."; + err=parse_list(arg, parse_pid); + if(!err){ + selection_list->typecode = SEL_SESS; + return NULL; /* can't have any more options */ + } + err=parse_list(arg, parse_gid); + if(!err){ + selection_list->typecode = SEL_EGID; + return NULL; /* can't have any more options */ + } + return "List of session leaders OR effective group IDs was invalid."; + case 'j': + trace("-j jobs format.\n"); + /* old Debian used RD_j and Digital uses JFMT */ + if(sysv_j_format) format_flags |= FF_Uj; + else format_modifiers |= FM_j; + break; + case 'l': + trace("-l long format.\n"); + format_flags |= FF_Ul; + break; + case 'm': + trace("-m shows threads.\n"); + /* note that AIX shows 2 lines for a normal process */ + thread_flags |= TF_U_m; + break; + case 'n': /* end */ + trace("-n sets namelist file.\n"); + arg=get_opt_arg(); + if(!arg) return "Alternate System.map file must follow -n."; + namelist_file = arg; + return NULL; /* can't have any more options */ + case 'o': /* end */ + /* Unix98 has gross behavior regarding this. From the following: */ + /* ps -o pid,nice=NICE,tty=TERMINAL,comm */ + /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm" */ + /* Yes, the second column has the name "NICE,tty=TERMINAL,comm" */ + /* This parser looks for any excuse to ignore that braindamage. */ + trace("-o user-defined format.\n"); + arg=get_opt_arg(); + if(!arg) return "Format specification must follow -o."; + not_pure_unix |= defer_sf_option(arg, SF_U_o); + return NULL; /* can't have any more options */ + case 'p': /* end */ + trace("-p select by PID.\n"); + arg=get_opt_arg(); + if(!arg) return "List of process IDs must follow -p."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ +#if 0 + case 'r': + trace("-r some Digital Unix thing about warnings...\n"); + trace(" or SCO's option to chroot() for new /proc and /dev.\n"); + return "The -r option is reserved."; + break; +#endif + case 's': /* end */ + trace("-s Select processes belonging to the sessions given.\n"); + arg=get_opt_arg(); + if(!arg) return "List of session IDs must follow -s."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_SESS; + return NULL; /* can't have any more options */ + case 't': /* end */ + trace("-t select by tty.\n"); + arg=get_opt_arg(); + if(!arg) return "List of terminals (pty, tty...) must follow -t."; + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; /* can't have any more options */ + case 'u': /* end */ + trace("-u select by user ID (the EUID?) (supports names).\n"); + arg=get_opt_arg(); + if(!arg) return "List of users must follow -u."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; /* can't have any more options */ + case 'w': + trace("-w wide output.\n"); + w_count++; + break; + case 'x': /* behind personality until "ps -ax" habit is uncommon */ + if(personality & PER_SVR4_x){ + // Same as -y, but for System V Release 4 MP + trace("-x works like Sun Solaris & SCO Unixware -y option\n"); + format_modifiers |= FM_y; + break; + } + if(personality & PER_HPUX_x){ + trace("-x extends the command line\n"); + w_count += 2; + unix_f_option = 1; + break; + } + return "Must set personality to get -x option."; + case 'y': /* Sun's -l hack (also: Irix "lnode" resource control info) */ + trace("-y Print lnone info in UID/USER column or do Sun -l hack.\n"); + format_modifiers |= FM_y; + break; +#if 0 + // This must be verified against SVR4-MP (UnixWare or Powermax) + case 'z': /* alias of Mandatory Access Control level info */ + trace("-z shows aliased MAC info\n"); + format_modifiers |= FM_M; + break; + // Solaris 10 does this + case 'z': /* select by zone */ + trace("-z secects by zone\n"); + arg=get_opt_arg(); + if(!arg) return "List of zones (contexts, labels, whatever?) must follow -z."; + err=parse_list(arg, parse_zone); + if(err) return err; + selection_list->typecode = SEL_ZONE; + return NULL; /* can't have any more options */ +#endif + case '-': + return "Embedded '-' among SysV options makes no sense."; + break; + case '\0': + return "Please report the \"SysV \\0 can't happen\" bug."; + break; + default: + return "Unsupported SysV option."; + } /* switch */ + } /* while */ + return NULL; +} + +/************************* parse BSD options **********************/ +static const char *parse_bsd_option(void){ + const char *arg; + const char *err; + + flagptr = ps_argv[thisarg]; /* assume we _have_ a '-' */ + if(flagptr[0]=='-'){ + if(!force_bsd) return "Can't happen! Problem #1."; + }else{ + flagptr--; /* off beginning, will increment before use */ + if(personality & PER_FORCE_BSD){ + if(!force_bsd) return "Can't happen! Problem #2."; + }else{ + if(force_bsd) return "2nd chance parse failed, not BSD or SysV."; + } + } + + while(*++flagptr){ + switch(*flagptr){ + case '0' ... '9': /* end */ + trace("0..9 Old BSD-style select by process ID\n"); + arg=flagptr; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ +#if 0 + case 'A': + /* maybe this just does a larger malloc() ? */ + trace("A Increases the argument space (Digital Unix)\n"); + return "Option A is reserved."; + break; + case 'C': + /* should divide result by 1-(e**(foo*log(bar))) */ + trace("C Use raw CPU time for %%CPU instead of decaying ave\n"); + return "Option C is reserved."; + break; +#endif + case 'H': // The FreeBSD way (NetBSD:s OpenBSD:k FreeBSD:H -- NIH???) + trace("H Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n"); + thread_flags |= TF_B_H; + //format_modifiers |= FM_L; // FIXME: determine if we need something like this + break; + case 'L': /* single */ + trace("L List all format specifiers\n"); + exclusive("L"); + print_format_specifiers(); + exit(0); + case 'M': // undocumented for now: these are proliferating! + trace("M MacOS X thread display, like AIX/Tru64\n"); + thread_flags |= TF_B_m; + break; + case 'N': /* end */ + trace("N Specify namelist file\n"); + arg=get_opt_arg(); + if(!arg) return "Alternate System.map file must follow N."; + namelist_file = arg; + return NULL; /* can't have any more options */ + case 'O': /* end */ + trace("O Like o + defaults, add new columns after PID. Also sort.\n"); + arg=get_opt_arg(); + if(!arg) return "Format or sort specification must follow O."; + defer_sf_option(arg, SF_B_O); + return NULL; /* can't have any more options */ + break; + case 'S': + trace("S include dead kids in sum\n"); + include_dead_children = 1; + break; + case 'T': + trace("T Select all processes on this terminal\n"); + /* put our tty on a tiny list */ + { + selection_node *node; + node = malloc(sizeof(selection_node)); + node->u = malloc(sizeof(sel_union)); + node->u[0].tty = cached_tty; + node->typecode = SEL_TTY; + node->n = 1; + node->next = selection_list; + selection_list = node; + } + break; + case 'U': /* end */ + trace("U Select processes for specified users.\n"); + arg=get_opt_arg(); + if(!arg) return "List of users must follow U."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; /* can't have any more options */ + case 'V': /* single */ + trace("V show version info\n"); + exclusive("V"); + display_version(); + exit(0); + case 'W': + trace("W N/A get swap info from ... not /dev/drum.\n"); + return "Obsolete W option not supported. (You have a /dev/drum?)"; + break; + case 'X': + trace("X Old Linux i386 register format\n"); + format_flags |= FF_LX; + break; + case 'Z': /* FreeBSD does MAC like SGI's Irix does it */ + trace("Z Print security label for Mandatory Access Control.\n"); + format_modifiers |= FM_M; + break; + case 'a': + trace("a Select all w/tty, including other users\n"); + simple_select |= SS_B_a; + break; + case 'c': + trace("c true command name\n"); + bsd_c_option = 1; + break; + case 'e': + trace("e environment\n"); + bsd_e_option = 1; + break; + case 'f': + trace("f ASCII art forest\n"); + forest_type = 'b'; + break; + case 'g': + trace("g _all_, even group leaders!.\n"); + simple_select |= SS_B_g; + break; + case 'h': + trace("h Repeat header... yow.\n"); + if(header_type) return "Only one heading option may be specified."; + if(personality & PER_BSD_h) header_type = HEAD_MULTI; + else header_type = HEAD_NONE; + break; + case 'j': + trace("j job control format\n"); + format_flags |= FF_Bj; + break; + case 'k': + // OpenBSD: don't hide "kernel threads" -- like the swapper? + // trace("k Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n"); + + // NetBSD, and soon (?) FreeBSD: sort-by-keyword + trace("k Specify sorting keywords.\n"); + arg=get_opt_arg(); + if(!arg) return "Long sort specification must follow 'k'."; + defer_sf_option(arg, SF_G_sort); + return NULL; /* can't have any more options */ + case 'l': + trace("l Display long format\n"); + format_flags |= FF_Bl; + break; + case 'm': + trace("m all threads, sort on mem use, show mem info\n"); + if(personality & PER_OLD_m){ + format_flags |= FF_Lm; + break; + } + if(personality & PER_BSD_m){ + defer_sf_option("pmem", SF_B_m); + break; + } + thread_flags |= TF_B_m; + break; + case 'n': + trace("n Numeric output for WCHAN, and USER replaced by UID\n"); + wchan_is_number = 1; + user_is_number = 1; + /* TODO add tty_is_number too? */ + break; + case 'o': /* end */ + trace("o Specify user-defined format\n"); + arg=get_opt_arg(); + if(!arg) return "Format specification must follow o."; + defer_sf_option(arg, SF_B_o); + return NULL; /* can't have any more options */ + case 'p': /* end */ + trace("p Select by process ID\n"); + arg=get_opt_arg(); + if(!arg) return "List of process IDs must follow p."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ + case 'r': + trace("r Select running processes\n"); + running_only = 1; + break; + case 's': + trace("s Display signal format\n"); + format_flags |= FF_Bs; + break; + case 't': /* end */ + trace("t Select by tty.\n"); + /* List of terminals (tty, pty...) _should_ follow t. */ + arg=get_opt_arg(); + if(!arg){ + /* Wow, obsolete BSD syntax. Put our tty on a tiny list. */ + selection_node *node; + node = malloc(sizeof(selection_node)); + node->u = malloc(sizeof(sel_union)); + node->u[0].tty = cached_tty; + node->typecode = SEL_TTY; + node->n = 1; + node->next = selection_list; + selection_list = node; + return NULL; + } + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; /* can't have any more options */ + case 'u': + trace("u Display user-oriented\n"); + format_flags |= FF_Bu; + break; + case 'v': + trace("v Display virtual memory\n"); + format_flags |= FF_Bv; + break; + case 'w': + trace("w wide output\n"); + w_count++; + break; + case 'x': + trace("x Select processes without controlling ttys\n"); + simple_select |= SS_B_x; + break; + case '-': + return "Embedded '-' among BSD options makes no sense."; + break; + case '\0': + return "Please report the \"BSD \\0 can't happen\" bug."; + break; + default: + return "Unsupported option (BSD syntax)"; + } /* switch */ + } /* while */ + return NULL; +} + +/*************** gnu long options **********************/ + +/* + * Return the argument or NULL + */ +static const char *grab_gnu_arg(void){ + switch(*flagptr){ /* argument is part of ps_argv[thisarg] */ + default: + return NULL; /* something bad */ + case '=': case ':': + if(*++flagptr) return flagptr; /* found it */ + return NULL; /* empty '=' or ':' */ + case '\0': /* try next argv[] */ + ; + } + if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */ + /* argument follows ps_argv[thisarg] */ + if(*(ps_argv[thisarg+1]) == '\0') return NULL; + return ps_argv[++thisarg]; +} + +typedef struct gnu_table_struct { + const char *name; /* long option name */ + const void *jump; /* See gcc extension info. :-) */ +} gnu_table_struct; + +static int compare_gnu_table_structs(const void *a, const void *b){ + return strcmp(((const gnu_table_struct*)a)->name,((const gnu_table_struct*)b)->name); +} + +/* Option arguments are after ':', after '=', or in argv[n+1] */ +static const char *parse_gnu_option(void){ + const char *arg; + const char *err; + char *s; + size_t sl; + char buf[16]; + gnu_table_struct findme = { buf, NULL}; + gnu_table_struct *found; + static const gnu_table_struct gnu_table[] = { + {"Group", &&case_Group}, /* rgid */ + {"User", &&case_User}, /* ruid */ + {"cols", &&case_cols}, + {"columns", &&case_columns}, + {"context", &&case_context}, + {"cumulative", &&case_cumulative}, + {"deselect", &&case_deselect}, /* -N */ + {"forest", &&case_forest}, /* f -H */ + {"format", &&case_format}, + {"group", &&case_group}, /* egid */ + {"header", &&case_header}, + {"headers", &&case_headers}, + {"heading", &&case_heading}, + {"headings", &&case_headings}, + {"help", &&case_help}, + {"info", &&case_info}, + {"lines", &&case_lines}, + {"no-header", &&case_no_header}, + {"no-headers", &&case_no_headers}, + {"no-heading", &&case_no_heading}, + {"no-headings", &&case_no_headings}, + {"noheader", &&case_noheader}, + {"noheaders", &&case_noheaders}, + {"noheading", &&case_noheading}, + {"noheadings", &&case_noheadings}, + {"pid", &&case_pid}, + {"ppid", &&case_ppid}, + {"rows", &&case_rows}, + {"sid", &&case_sid}, + {"sort", &&case_sort}, + {"tty", &&case_tty}, + {"user", &&case_user}, /* euid */ + {"version", &&case_version}, + {"width", &&case_width}, + }; + const int gnu_table_count = sizeof(gnu_table)/sizeof(gnu_table_struct); + + s = ps_argv[thisarg]+2; + sl = strcspn(s,":="); + if(sl > 15) return "Unknown gnu long option."; + strncpy(buf, s, sl); + buf[sl] = '\0'; + flagptr = s+sl; + + found = bsearch(&findme, gnu_table, gnu_table_count, + sizeof(gnu_table_struct), compare_gnu_table_structs + ); + + if(!found) return "Unknown gnu long option."; + + goto *(found->jump); /* See gcc extension info. :-) */ + + case_Group: + trace("--Group\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of real groups must follow --Group."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_RGID; + return NULL; + case_User: + trace("--User\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of real users must follow --User."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_RUID; + return NULL; + case_cols: + case_width: + case_columns: + trace("--cols\n"); + arg = grab_gnu_arg(); + if(arg && *arg){ + long t; + char *endptr; + t = strtol(arg, &endptr, 0); + if(!*endptr && (t>0) && (t<2000000000)){ + screen_cols = (int)t; + return NULL; + } + } + return "Number of columns must follow --cols, --width, or --columns."; + case_cumulative: + trace("--cumulative\n"); + if(s[sl]) return "Option --cumulative does not take an argument."; + include_dead_children = 1; + return NULL; + case_deselect: + trace("--deselect\n"); + if(s[sl]) return "Option --deselect does not take an argument."; + negate_selection = 1; + return NULL; + case_no_header: + case_no_headers: + case_no_heading: + case_no_headings: + case_noheader: + case_noheaders: + case_noheading: + case_noheadings: + trace("--noheaders\n"); + if(s[sl]) return "Option --no-heading does not take an argument."; + if(header_type) return "Only one heading option may be specified."; + header_type = HEAD_NONE; + return NULL; + case_header: + case_headers: + case_heading: + case_headings: + trace("--headers\n"); + if(s[sl]) return "Option --heading does not take an argument."; + if(header_type) return "Only one heading option may be specified."; + header_type = HEAD_MULTI; + return NULL; + case_forest: + trace("--forest\n"); + if(s[sl]) return "Option --forest does not take an argument."; + forest_type = 'g'; + return NULL; + case_format: + trace("--format\n"); + arg=grab_gnu_arg(); + if(!arg) return "Format specification must follow --format."; + defer_sf_option(arg, SF_G_format); + return NULL; + case_group: + trace("--group\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of effective groups must follow --group."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_EGID; + return NULL; + case_help: + trace("--help\n"); + exclusive("--help"); + fwrite(help_message,1,strlen(help_message),stdout); + exit(0); + return NULL; + case_info: + trace("--info\n"); + exclusive("--info"); + self_info(); + exit(0); + return NULL; + case_pid: + trace("--pid\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of process IDs must follow --pid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; + case_ppid: + trace("--ppid\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of process IDs must follow --ppid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PPID; + return NULL; + case_rows: + case_lines: + trace("--rows\n"); + arg = grab_gnu_arg(); + if(arg && *arg){ + long t; + char *endptr; + t = strtol(arg, &endptr, 0); + if(!*endptr && (t>0) && (t<2000000000)){ + screen_rows = (int)t; + return NULL; + } + } + return "Number of rows must follow --rows or --lines."; + case_sid: + trace("--sid\n"); + arg = grab_gnu_arg(); + if(!arg) return "Some sid thing(s) must follow --sid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_SESS; + return NULL; + case_sort: + trace("--sort\n"); + arg=grab_gnu_arg(); + if(!arg) return "Long sort specification must follow --sort."; + defer_sf_option(arg, SF_G_sort); + return NULL; + case_tty: + trace("--tty\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of ttys must follow --tty."; + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; + case_user: + trace("--user\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of effective users must follow --user."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; + case_version: + trace("--version\n"); + exclusive("--version"); + display_version(); + exit(0); + return NULL; + case_context: + trace("--context\n"); + format_flags |= FF_Fc; + return NULL; +} + +/*************** process trailing PIDs **********************/ +static const char *parse_trailing_pids(void){ + selection_node *pidnode; /* pid */ + selection_node *grpnode; /* process group */ + selection_node *sidnode; /* session */ + char **argp; /* pointer to pointer to text of PID */ + const char *err; /* error code that could or did happen */ + int i; + + i = ps_argc - thisarg; /* how many trailing PIDs, SIDs, PGRPs?? */ + argp = ps_argv + thisarg; + thisarg = ps_argc - 1; /* we must be at the end now */ + + pidnode = malloc(sizeof(selection_node)); + pidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + pidnode->n = 0; + + grpnode = malloc(sizeof(selection_node)); + grpnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + grpnode->n = 0; + + sidnode = malloc(sizeof(selection_node)); + sidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + sidnode->n = 0; + + while(i--){ + char *data; + data = *(argp++); + switch(*data){ + default: err = parse_pid( data, pidnode->u + pidnode->n++); break; + case '-': err = parse_pid(++data, grpnode->u + grpnode->n++); break; + case '+': err = parse_pid(++data, sidnode->u + sidnode->n++); break; + } + if(err) return err; /* the node gets freed with the list */ + } + + if(pidnode->n){ + pidnode->next = selection_list; + selection_list = pidnode; + selection_list->typecode = SEL_PID; + } /* else free both parts */ + + if(grpnode->n){ + grpnode->next = selection_list; + selection_list = grpnode; + selection_list->typecode = SEL_PGRP; + } /* else free both parts */ + + if(sidnode->n){ + sidnode->next = selection_list; + selection_list = sidnode; + selection_list->typecode = SEL_SESS; + } /* else free both parts */ + + return NULL; +} + +/************** misc stuff ***********/ + +static void reset_parser(void){ + w_count = 0; +} + +static int arg_type(const char *str){ + int tmp = str[0]; + if((tmp>='a') && (tmp<='z')) return ARG_BSD; + if((tmp>='A') && (tmp<='Z')) return ARG_BSD; + if((tmp>='0') && (tmp<='9')) return ARG_PID; + if(tmp=='+') return ARG_SESS; + if(tmp!='-') return ARG_FAIL; + tmp = str[1]; + if((tmp>='a') && (tmp<='z')) return ARG_SYSV; + if((tmp>='A') && (tmp<='Z')) return ARG_SYSV; + if((tmp>='0') && (tmp<='9')) return ARG_PGRP; + if(tmp!='-') return ARG_FAIL; + tmp = str[2]; + if((tmp>='a') && (tmp<='z')) return ARG_GNU; + if((tmp>='A') && (tmp<='Z')) return ARG_GNU; + if(tmp=='\0') return ARG_END; + return ARG_FAIL; +} + +/* First assume sysv, because that is the POSIX and Unix98 standard. */ +static const char *parse_all_options(void){ + const char *err = NULL; + int at; + while(++thisarg < ps_argc){ + trace("parse_all_options calling arg_type for \"%s\"\n", ps_argv[thisarg]); + at = arg_type(ps_argv[thisarg]); + trace("ps_argv[thisarg] is %s\n", ps_argv[thisarg]); + if(at != ARG_SYSV) not_pure_unix = 1; + switch(at){ + case ARG_GNU: + err = parse_gnu_option(); + break; + case ARG_SYSV: + if(!force_bsd){ /* else go past case ARG_BSD */ + err = parse_sysv_option(); + break; + case ARG_BSD: + if(force_bsd && !(personality & PER_FORCE_BSD)) return "way bad"; + } + prefer_bsd_defaults = 1; + err = parse_bsd_option(); + break; + case ARG_PGRP: + case ARG_SESS: + case ARG_PID: + prefer_bsd_defaults = 1; + err = parse_trailing_pids(); + break; + case ARG_END: + case ARG_FAIL: + trace(" FAIL/END on [%s]\n",ps_argv[thisarg]); + return "Garbage option."; + break; + default: + printf(" ? %s\n",ps_argv[thisarg]); + return "Something broke."; + } /* switch */ + if(err) return err; + } /* while */ + return NULL; +} + +static void choose_dimensions(void){ + if(w_count && (screen_cols<132)) screen_cols=132; + if(w_count>1) screen_cols=OUTBUF_SIZE; + /* perhaps --html and --null should set unlimited width */ +} + +static const char *thread_option_check(void){ + if(!thread_flags){ + thread_flags = TF_show_proc; + return NULL; + } + + if(forest_type){ + return "Thread display conflicts with forest display."; + } + //thread_flags |= TF_no_forest; + + if((thread_flags&TF_B_H) && (thread_flags&(TF_B_m|TF_U_m))) + return "Thread flags conflict; can't use H with m or -m."; + if((thread_flags&TF_B_m) && (thread_flags&TF_U_m)) + return "Thread flags conflict; can't use both m and -m."; + if((thread_flags&TF_U_L) && (thread_flags&TF_U_T)) + return "Thread flags conflict; can't use both -L and -T."; + + if(thread_flags&TF_B_H) thread_flags |= (TF_show_proc|TF_loose_tasks); + if(thread_flags&(TF_B_m|TF_U_m)) thread_flags |= (TF_show_proc|TF_show_task|TF_show_both); + + if(thread_flags&(TF_U_T|TF_U_L)){ + if(thread_flags&(TF_B_m|TF_U_m|TF_B_H)){ + // Got a thread style, so format modification is a requirement? + // Maybe -T/-L has H thread style though. (sorting interaction?) + //return "Huh? Tell procps-feedback@lists.sf.net what you expected."; + thread_flags |= TF_must_use; + }else{ + // using -L/-T thread style, so format from elsewhere is OK + thread_flags |= TF_show_task; // or like the H option? + //thread_flags |= TF_no_sort; + } + } + + return NULL; +} + +int arg_parse(int argc, char *argv[]){ + const char *err = NULL; + const char *err2 = NULL; + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + + if(personality & PER_FORCE_BSD) goto try_bsd; + + err = parse_all_options(); + if(err) goto try_bsd; + err = thread_option_check(); + if(err) goto try_bsd; + err = process_sf_options(!not_pure_unix); + if(err) goto try_bsd; + err = select_bits_setup(); + if(err) goto try_bsd; + + choose_dimensions(); + return 0; + +try_bsd: + trace("--------- now try BSD ------\n"); + + reset_global(); + reset_parser(); + reset_sortformat(); + format_flags = 0; + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + /* no need to reset flagptr */ + not_pure_unix=1; + force_bsd=1; + prefer_bsd_defaults=1; + if(!( (PER_OLD_m|PER_BSD_m) & personality )) /* if default m setting... */ + personality |= PER_OLD_m; /* Prefer old Linux over true BSD. */ + /* Do not set PER_FORCE_BSD! It is tested below. */ + + err2 = parse_all_options(); + if(err2) goto total_failure; + err2 = thread_option_check(); + if(err2) goto total_failure; + err2 = process_sf_options(!not_pure_unix); + if(err2) goto total_failure; + err2 = select_bits_setup(); + if(err2) goto total_failure; + + // Feel a need to patch this out? First of all, read the FAQ. + // Second of all, talk to me. Without this warning, people can + // get seriously confused. Ask yourself if users would freak out + // about "ps -aux" suddenly changing behavior if a user "x" were + // added to the system. + // + // Also, a "-x" option is coming. It's already there in fact, + // for some non-default personalities. So "ps -ax" will parse + // as SysV options... and you're screwed if you've been patching + // out the friendly warning. Cut-over is likely to be in 2005. + if(!(personality & PER_FORCE_BSD)) + fprintf(stderr, "Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html\n"); + // Remember: contact albert@users.sf.net or procps-feedback@lists.sf.net + // if you should feel tempted. Be damn sure you understand all + // the issues. The same goes for other stuff too, BTW. Please ask. + // I'm happy to justify various implementation choices. + + choose_dimensions(); + return 0; + +total_failure: + reset_parser(); + if(personality & PER_FORCE_BSD) fprintf(stderr, "ERROR: %s\n", err2); + else fprintf(stderr, "ERROR: %s\n", err); + fwrite(help_message,1,strlen(help_message),stderr); + exit(1); + /* return 1; */ /* useless */ +} diff --git a/ps/ps.1 b/ps/ps.1 new file mode 100644 index 0000000..64953d5 --- /dev/null +++ b/ps/ps.1 @@ -0,0 +1,1521 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" Man page for ps. +.\" Quick hack conversion by Albert Cahalan, 1998. +.\" Licensed under version 2 of the Gnu General Public License. +.\" +.TH PS 1 "July 28, 2004" "Linux" "Linux User's Manual" +.\" +.\" To render this page: +.\" groff -t -b -man -X -P-resolution -P100 -Tps ps.1 & +.\" groff -t -b -man -X -TX100 ps.1 & +.\" tbl ps.1 | troff -Ww -man -z +.\" groff -t -man -Tps ps.1 | ps2pdf - - > ps.pdf +.\" +.\" The '70s called. They want their perfect justification, +.\" hyphenation, and double-spaced sentences back. +.na +.nh +.if n .ss 12 0 +.\" +.\" See /usr/share/groff/current/tmac/an-old.tmac for what these do. +.\" Setting them to zero provides extra space, but only do that for +.\" plain text output. PostScript and such will remain indented. +.if n .nr IN 0n +.if n .nr an-prevailing-indent 0n +.\" +.\" +.\" ColSize is used for the format spec table. +.\" It's the left margin, minus the right, minus +.\" the space needed for the 1st two columns. +.\" Making it messy: inches, ens, points, scaled points... +.\" +.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-20u)n) +.\" +.\" This is for command options +.nr OptSize (16u) +.\" +.\" l=\n(.l +.\" i=\n(.i +.\" o=\n(.o +.\" H=\n(.H +.\" s=\n(.s +.\" ColSize=\n[ColSize] +.\" +.\" Macro for easy option formatting: .opt \-x +.de opt +. TP \\n[OptSize] +. BI \\$* +.. +.\" +.SH NAME +ps \- report a snapshot of the current processes. +.SH SYNOPSIS +\fBps\fR [\fIoptions\fR] +.PP +.PP +.SH DESCRIPTION +.B ps +displays information about a selection of the active processes. +If you want a repetitive update of the selection and the +displayed information, use\ \fItop\fR(1) instead. +.P +This version of \fBps\fR accepts several kinds of options: +.PD 0 +.IP 1 4 +UNIX options, which may be grouped and must be preceded by a dash. +.IP 2 4 +BSD options, which may be grouped and must not be used with a dash. +.IP 3 4 +GNU long options, which are preceded by two dashes. +.PD +.PP +Options of different types may be freely mixed, but conflicts can appear. +There are some synonymous options, which are functionally identical, due +to the many standards and \fBps\fR implementations that this \fBps\fR is +compatible with. +.P +Note that "\fBps\ \-aux\fR" is distinct from "\fBps\ aux\fR". +The POSIX and UNIX standards require that "\fBps\ \-aux\fR" print all +processes owned by a user named "x", as well as printing all processes +that would be selected by the \fB\-a\fR option. If the user named "x" does +not exist, this \fBps\fR may interpret the command as "\fBps\ aux\fR" +instead and print a warning. This behavior is intended to aid in +transitioning old scripts and habits. It is fragile, subject to change, +and thus should not be relied upon. +.P +By default, \fBps\fR selects all processes +with the same effective user ID (euid=EUID) as the current user +and +associated with the same terminal as the invoker. +It displays the process ID (pid=PID), +the terminal associated with the process (tname=TTY), +the cumulated CPU time in [dd\-]hh:mm:ss format (time=TIME), +and the executable name (ucmd=CMD). +Output is unsorted by default. +.P +The use of BSD\-style options will add process state (stat=STAT) to the +default display and show the command args (args=COMMAND) instead of the +executable name. You can override this with the \fBPS_FORMAT\fR +environment variable. The use of BSD\-style options will also change the +process selection to include processes on other terminals (TTYs) that +are owned by you; alternately, this may be described as setting the +selection to be the set of all processes filtered to exclude +processes owned by other users or not on a terminal. These effects +are not considered when options are described as being "identical" below, +so \fB\-M\fR will be considered identical to \fBZ\fR and so on. +.P +Except as described below, process selection options are additive. +The default selection is discarded, and then the selected processes +are added to the set of processes to be displayed. +A\ process will thus be shown if it meets any of the given +selection criteria. +.PP +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.SH "EXAMPLES" +.TP 3 +To see every process on the system using standard syntax: +.B ps\ \-e +.br +.B ps\ \-ef +.br +.B ps\ \-eF +.br +.B ps\ \-ely +.TP +To see every process on the system using BSD syntax: +.B ps\ ax +.br +.B ps\ axu +.TP +To print a process tree: +.B ps\ -ejH +.br +.B ps\ axjf +.TP +To get info about threads: +.B ps\ -eLf +.br +.B ps\ axms +.TP +To get security info: +.B ps\ -eo euser,ruser,suser,fuser,f,comm,label +.br +.B ps\ axZ +.br +.B ps\ -eM +.TP +To see every process running as root (real\ &\ effective\ ID) in user format: +.B ps\ \-U\ root\ \-u\ root\ u +.TP +To see every process with a user\-defined format: +.B ps\ \-eo\ pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm +.br +.B ps\ axo\ stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm +.br +.B ps\ \-eopid,tt,user,fname,tmout,f,wchan +.TP +Print only the process IDs of syslogd: +.B ps\ \-C\ syslogd\ \-o\ pid= +.TP +Print only the name of PID 42: +.B ps\ \-p\ 42\ \-o\ comm= +.PP +.PP +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.SH "SIMPLE PROCESS SELECTION" +.opt \-A +Select all processes. Identical to \fB\-e\fR. + +.opt \-N +Select all processes except those that fulfill the specified conditions. +(negates the selection) Identical to \fB\-\-deselect\fR. + +.opt T +Select all processes associated with this terminal. Identical to the +\fBt\fR option without any argument. + +.opt \-a +Select all processes except both session leaders (see \fIgetsid\fR(2)) and +processes not associated with a terminal. + +.opt a +Lift the BSD\-style "only yourself" restriction, which is imposed upon +the set of all processes when some BSD\-style (without\ "\-") options +are used or when the \fBps\fR personality setting is BSD\-like. +The set of processes selected in this manner is +in addition to the set of processes selected by other means. +An alternate description is that this option causes \fBps\fR to +list all processes with a terminal (tty), +or to list all processes when used together with the \fBx\fR option. + +.opt \-d +Select all processes except session leaders. + +.opt \-e +Select all processes. Identical to \fB\-A\fR. + +.\" Current "g" behavior: add in the session leaders, which would +.\" be excluded in the sunos4 personality. Supposed "g" behavior: +.\" add in the group leaders -- at least according to the SunOS 4 +.\" man page on the FreeBSD site. Uh oh. I think I had tested SunOS +.\" though, so maybe the code is correct. +.opt g +Really all, even session leaders. This flag is obsolete and may be +discontinued in a future release. It is normally implied by the \fBa\fR flag, +and is only useful when operating in the sunos4 personality. + +.opt r +Restrict the selection to only running processes. + +.opt x +Lift the BSD\-style "must have a tty" restriction, which is imposed upon +the set of all processes when some BSD\-style (without\ "\-") options +are used or when the \fBps\fR personality setting is BSD\-like. +The set of processes selected in this manner is +in addition to the set of processes selected by other means. +An alternate description is that this option causes \fBps\fR to +list all processes owned by you (same EUID as \fBps\fR), +or to list all processes when used together with the \fBa\fR option. + +.opt \-\-deselect +Select all processes except those that fulfill the specified conditions. +(negates the selection) Identical to \fB\-N\fR. + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "PROCESS SELECTION BY LIST" +These options accept a single argument in the form of a blank\-separated +or comma\-separated list. They can be used multiple times. +For\ example:\ \fBps\ \-p\ "1\ 2"\ \-p\ 3,4\fR +.P +.opt \-C \ cmdlist +Select by command name. +.br +This selects the processes whose executable name is given in +\fIcmdlist\fR. + +.opt \-G \ grplist +Select by real group ID (RGID) or name. +.br +This selects the processes whose real group name or ID is in the +\fIgrplist\fR list. The real group ID identifies the group of the user +who created the process, see \fIgetgid\fR(2). + +.opt U \ userlist +Select by effective user ID (EUID) or name. +.br +This selects the processes whose effective user name +or ID is in \fIuserlist\fR. +The effective user\ ID describes the user whose file +access permissions are used by the process +(see\ \fIgeteuid\fR(2)). +Identical to \fB\-u\fR and\ \fB\-\-user\fR. + +.opt \-U \ userlist +select by real user ID (RUID) or name. +.br +It selects the processes whose real user name or ID is in the +\fIuserlist\fR list. +The real user ID identifies the user who created the process, +see\ \fIgetuid\fR(2). + +.opt \-g \ grplist +Select by session OR by effective group name. +.br +Selection by session is specified by many standards, +but selection by effective group is the logical behavior that +several other operating systems use. +This \fBps\fR will select by session when the list +is completely numeric (as\ sessions\ are). +Group ID numbers will work only when some group names are also specified. +See the \fB\-s\fR and \fB\-\-group\fR options. + +.opt p \ pidlist +Select by process ID. Identical to \fB\-p\fR and\ \fB\-\-pid\fR. + +.opt \-p \ pidlist +Select by PID. +.br +This selects the processes whose process ID numbers appear in +\fIpidlist\fR. Identical to \fBp\fR and\ \fB\-\-pid\fR. + +.opt \-s \ sesslist +Select by session ID. +.br +This selects the processes +with a session ID specified in\ \fIsesslist\fR. + +.opt t \ ttylist +Select by tty. Nearly identical to \fB\-t\fR and \fB\-\-tty\fR, +but can also be used with an empty \fIttylist\fR to indicate +the terminal associated with \fBps\fR. +Using the \fBT\fR option is considered cleaner than using \fBT\fR with +an\ empty\ \fIttylist\fR. + +.opt \-t \ ttylist +Select by tty. +.br +This selects the processes associated with the terminals +given in \fIttylist\fR. +Terminals (ttys, or screens for text output) can be specified in several +forms: /dev/ttyS1, ttyS1, S1. +A\ plain "\-" may be used to select processes not attached to any terminal. + +.opt \-u \ userlist +Select by effective user ID (EUID) or name. +.br +This selects the processes whose effective user name or ID is in +\fIuserlist\fR. The effective user ID describes the user whose file +access permissions are used by the process (see\ \fIgeteuid\fR(2)). +Identical to \fBU\fR and \fB\-\-user\fR. + +.opt \-\-Group \ grplist +Select by real group ID (RGID) or name. Identical to \fB\-G\fR. + +.opt \-\-User \ userlist +Select by real user ID (RUID) or name. Identical to \fB\-U\fR. + +.opt \-\-group \ grplist +Select by effective group ID (EGID) or name. +.br +This selects the processes whose effective group name or ID is in +\fIgrouplist\fR. The effective group ID describes the group whose file +access permissions are used by the process (see\ \fIgeteuid\fR(2)). +The \fB\-g\fR option is often an alternative to\ \fB\-\-group\fR. + +.opt \-\-pid \ pidlist +Select by process\ ID. Identical to \fB\-p\fR\ and\ \fBp\fR. + +.opt \-\-ppid \ pidlist +Select by parent process\ ID. +This selects the processes +with a parent\ process\ ID in \fRpidlist\fR. +That\ is, it selects processes that are children +of those listed in \fRpidlist\fR. + +.opt \-\-sid \ sesslist +Select by session\ ID. Identical to\ \fB\-s\fR. + +.opt \-\-tty \ ttylist +Select by terminal. Identical to \fB\-t\fR and\ \fBt\fR. + +.opt \-\-user \ userlist +Select by effective user ID (EUID) or name. +Identical to \fB\-u\fR and\ \fBU\fR. + +.opt \-\fI123\fR +Identical to \fB\-\-sid\ \fI123\fR. + +.opt \fI123\fR +Identical to \fB\-\-pid\ \fI123\fR. + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "OUTPUT FORMAT CONTROL" +These options are used to choose the information displayed by \fBps\fR. +The output may differ by personality. +.PP + +.opt \-F +extra full format. See the \fB\-f\fR option, which \fB\-F\fR implies. + +.opt \-O \ format +is like \fB\-o\fR, but preloaded with some default columns. +Identical to \fB\-o\ pid,\fIformat\fB,state,tname,time,command\fR +or \fB\-o\ pid,\fIformat\fB,tname,time,cmd\fR, see\ \fB\-o\fR\ below. + +.opt O \ format +is preloaded \fBo\fR (overloaded). +.br +The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output +format with some common fields predefined) or can be used to specify +sort order. Heuristics are used to determine the behavior of this +option. To ensure that the desired behavior is obtained (sorting or +formatting), specify the option in some other way +(e.g. with \fB\-O\fR or \fB\-\-sort\fR). +When used as a formatting option, it is identical to \fB\-O\fR, with the +BSD\ personality. + +.opt \-M +Add a column of security data. Identical to \fBZ\fR. (for\ SE\ Linux) + +.opt X +Register format. + +.opt Z +Add a column of security data. Identical to \fB\-M\fR. (for\ SE\ Linux) + +.opt \-c +Show different scheduler information for the \fB\-l\fR option. + +.opt \-f +does full\-format listing. This option can be combined with many +other UNIX\-style options to add additional columns. It also causes +the command arguments to be printed. When used with \fB\-L\fR, the +NLWP (number of threads) and LWP (thread ID) columns will be added. +See the \fBc\fR option, the format keyword \fBargs\fR, and the +format keyword \fBcomm\fR. + +.opt j +BSD job control format. + +.opt \-j +jobs format + +.opt l +display BSD long format. + +.opt \-l +long format. The \fB\-y\fR option is often useful with this. + +.opt o \ format +specify user\-defined format. Identical to \fB\-o\fR and +\fB\-\-format\fR. + +.opt \-o \ format +user\-defined format. +.br +\fIformat\fR is a single argument in the form of a +blank\-separated or comma\-separated list, which offers +a way to specify individual output columns. +The recognized keywords are described in the \fBSTANDARD FORMAT +SPECIFIERS\fR section below. +Headers may be +renamed (\fBps\ \-o\ pid,ruser=RealUser\ \-o\ comm=Command\fR) as desired. +If all column headers are empty (\fBps\ \-o\ pid=\ \-o\ comm=\fR) then the +header line will not be output. Column width will increase as +needed for wide headers; this may be used to widen up columns +such as WCHAN (\fBps\ \-o\ pid,wchan=WIDE\-WCHAN\-COLUMN\ \-o\ comm\fR). +Explicit width control (\fBps\ opid,wchan:42,cmd\fR) is offered too. +The behavior of \fBps\ \-o\ pid=X,comm=Y\fR varies with personality; +output may be one column named "X,comm=Y" or two columns +named "X" and "Y". Use multiple \fB\-o\fR options when in doubt. +Use the \fBPS_FORMAT\fR environment variable to specify a default +as desired; DefSysV and DefBSD are macros that may be used to +choose the default UNIX or BSD columns. + +.opt s +display signal format + +.opt u +display user\-oriented format + +.opt v +display virtual memory format + +.opt \-y +Do not show flags; show rss in place of addr. +This option can only be used with \fB\-l\fR. + +.opt \-\-format \ format +user\-defined format. Identical to \fB\-o\fR and \fBo\fR. + +.opt \-\-context +Display security context format. (for\ SE\ Linux) + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "OUTPUT MODIFIERS" + +.\" .TP +.\" .B C +.\" use raw CPU time for %CPU instead of decaying average + +.opt \-H +show process hierarchy (forest) + +.opt N \ namelist +Specify namelist file. Identical to \fB\-n\fR, see \fB\-n\fR above. + +.opt O \ order +Sorting order. (overloaded) +.br +The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output +format with some common fields predefined) or can be used to specify +sort order. Heuristics are used to determine the behavior of this +option. To ensure that the desired behavior is obtained (sorting or +formatting), specify the option in some other way (e.g. with \fB\-O\fR +or \fB\-\-sort\fR). + +For sorting, obsolete BSD \fBO\fR option syntax is +\fBO\fR[\fB+\fR|\fB\-\fR]\fIk1\fR[,[\fB+\fR|\fB\-\fR]\fIk2\fR[,...]]. +It orders the processes listing according to the multilevel sort specified by +the sequence of one\-letter short keys \fIk1\fR, \fIk2\fR, ... described +in the \fBOBSOLETE SORT KEYS\fR section below. +The\ "+" is currently optional, +merely re\-iterating the default direction on a key, +but may help to distinguish an \fBO\fR sort from an \fBO\fR format. +The\ "\-" reverses direction only on the key it precedes. + +.opt S +Sum up some information, such as CPU usage, from dead child processes +into their parent. This is useful for examining a system where a +parent process repeatedly forks off short\-lived children to do work. + +.opt c +Show the true command name. This is derived from the name of the +executable file, rather than from the argv value. Command arguments +and any modifications to them (see\ \fIsetproctitle\fR(3)) are +thus not shown. This option +effectively turns the \fBargs\fR format keyword into the \fBcomm\fR +format keyword; it is useful with the \fB\-f\fR format option and with +the various BSD\-style format options, which all normally +display the command arguments. +See the \fB\-f\fR option, the format keyword \fBargs\fR, and the +format keyword \fBcomm\fR. + +.opt e +Show the environment after the command. + +.opt f +ASCII\-art process hierarchy (forest) + +.opt h +No header. (or, one header per screen in the BSD personality) +.br +The \fBh\fR option is problematic. Standard BSD \fBps\fR uses +this option to print a header on each page of output, but older +Linux \fBps\fR uses this option to totally disable the header. +This version of \fBps\fR follows the Linux usage of not printing +the header unless the BSD personality has been selected, in which +case it prints a header on each page of output. Regardless of the +current personality, you can use the long options \fB\-\-headers\fR +and \fB\-\-no\-headers\fR to enable printing headers each page or +disable headers entirely, respectively. + +.opt k \ spec +specify sorting order. Sorting syntax is +[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]] +Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section. +The\ "+" is optional since default direction is increasing numerical or +lexicographic order. Identical to \fB\-\-sort\fR. Examples: +.br +\fBps\ jaxkuid,\-ppid,+pid\fR +.br +\fBps\ axk\ comm\ o\ comm,args\fR +.br +\fBps\ kstart_time\ \-ef\fR + +.opt \-n \ namelist +set namelist file. Identical to \fBN\fR. +.br +The namelist file is needed for a proper WCHAN display, and must match +the current Linux kernel exactly for correct output. +Without this option, the default search path for the namelist is: + + $PS_SYSMAP +.br + $PS_SYSTEM_MAP +.br + /proc/*/wchan +.br + /boot/System.map\-\`uname\ \-r\` +.br + /boot/System.map +.br + /lib/modules/\`uname\ \-r\`/System.map +.br + /usr/src/linux/System.map +.br + /System.map + +.opt n +Numeric output for WCHAN and USER. (including all types of UID and GID) + +.opt \-w +Wide output. Use this option twice for unlimited width. + +.opt w +Wide output. Use this option twice for unlimited width. + +.opt \-\-cols \ n +set screen width + +.opt \-\-columns \ n +set screen width + +.opt \-\-cumulative +include some dead child process data (as a sum with the parent) + +.opt \-\-forest +ASCII art process tree + +.opt \-\-headers +repeat header lines, one per page of output + +.opt \-\-no\-headers +print no header line at all + +.opt \-\-lines \ n +set screen height + +.opt \-\-rows \ n +set screen height + +.opt \-\-sort \ spec +specify sorting order. Sorting syntax is +[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]] +Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section. +The\ "+" is optional since default direction is increasing numerical or +lexicographic order. Identical to\ \fBk\fR. +For example: \fBps\ jax\ \-\-sort=uid,\-ppid,+pid\fR + +.opt \-\-width \ n +set screen width + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "THREAD DISPLAY" +.PD 0 + +.opt H +Show threads as if they were processes + +.opt \-L +Show threads, possibly with LWP and NLWP columns + +.opt \-T +Show threads, possibly with SPID column + +.opt m +Show threads after processes + +.opt \-m +Show threads after processes + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "OTHER INFORMATION" +.PD 0 + +.opt L +List all format specifiers. + +.opt \-V +Print the procps version. + +.opt V +Print the procps version. + +.opt \-\-help +Print a help message. + +.opt \-\-info +Print debugging info. + +.opt \-\-version +Print the procps version. + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH NOTES +This \fBps\fR works by reading the virtual files in\ /proc. +This \fBps\fR does not need to be setuid kmem or have any privileges to run. +Do not give this \fBps\fR any special permissions. + +This \fBps\fR needs access to namelist data for proper WCHAN display. +For kernels prior to 2.6, the System.map file must be installed. + +CPU usage is currently expressed as the percentage of time spent +running during the entire lifetime of a process. +This is not ideal, and\ it does not conform to the +standards that \fBps\fR otherwise conforms\ to. +CPU\ usage is unlikely to add up to exactly\ 100%. + +The SIZE and RSS fields don't count some parts of a process including the +page tables, kernel stack, struct thread_info, and struct task_struct. +This is usually at least 20\ KiB of memory that is always resident. +SIZE is the virtual size of the process (code+data+stack). + +Processes marked are dead processes (so\-called\ "zombies") that +remain because their parent has not destroyed them properly. These processes +will be destroyed by \fIinit\fR(8) if the parent process exits. + + +.SH "PROCESS FLAGS" +The sum of these values is displayed in the "F" column, +which is provided by the \fBflags\fR output specifier. +.PD 0 +.TP 5 +1 +forked but didn't exec +.TP +4 +used super\-user privileges +.PD +.PP +.SH "PROCESS STATE CODES" +Here are the different values that the \fBs\fR, \fBstat\fR and +\fBstate\fR output specifiers (header\ "STAT"\ or\ "S") will display to +describe the state of a process. +.PD 0 +.TP 5 +D +Uninterruptible sleep (usually\ IO) +.TP +R +Running or runnable (on\ run\ queue) +.TP +S +Interruptible sleep (waiting for an event to complete) +.TP +T +Stopped, either by a job control signal or because it is being traced. +.TP +W +paging (not valid since the 2.6.xx kernel) +.TP +X +dead (should never be seen) +.TP +Z +Defunct ("zombie") process, terminated but not reaped by its parent. +.PD +.PP +For BSD formats and when the \fBstat\fR keyword is used, additional +characters may be displayed: +.PD 0 +.TP 5 +< +high\-priority (not nice to other users) +.TP +N +low\-priority (nice to other users) +.TP +L +has pages locked into memory (for real\-time and custom\ IO) +.TP +s +is a session leader +.TP +l +is multi-threaded (using CLONE_THREAD, like NPTL pthreads\ do) +.TP ++ +is in the foreground process group +.PD +.PP +.PP +.SH "OBSOLETE SORT KEYS" +These keys are used by the BSD \fBO\fR option (when it is used for +sorting). The GNU \fB\-\-sort\fR option doesn't use these keys, but the +specifiers described below in the \fBSTANDARD FORMAT SPECIFIERS\fR +section. Note that the values used in sorting are the internal +values \fBps\fR uses and not the "cooked" values used in some of +the output format fields (e.g. sorting on tty will sort into +device number, not according to the terminal name displayed). +Pipe \fBps\fR output into the \fIsort\fR(1) command if you want +to sort the cooked values. + +.TS +l l lw(3i). +\fBKEY LONG DESCRIPTION\fR +c cmd simple name of executable +C pcpu cpu utilization +f flags flags as in long format F field +g pgrp process group ID +G tpgid controlling tty process group ID +j cutime cumulative user time +J cstime cumulative system time +k utime user time +m min_flt number of minor page faults +M maj_flt number of major page faults +n cmin_flt cumulative minor page faults +N cmaj_flt cumulative major page faults +o session session ID +p pid process ID +P ppid parent process ID +r rss resident set size +R resident resident pages +s size memory size in kilobytes +S share amount of shared pages +t tty the device number of the controlling tty +T start_time time process was started +U uid user ID number +u user user name +v vsize total VM size in kB +y priority kernel scheduling priority +.\"K stime system time (conflict, system vs. start time) +.TE +.PP +.PP +.SH "AIX FORMAT DESCRIPTORS" +This \fBps\fR supports AIX format descriptors, which work somewhat like the +formatting codes of \fIprintf\fR(1) and \fIprintf\fR(3). For example, the normal +default output can be produced with this: \fBps\ \-eo\ "%p\ %y\ %x\ %c"\fR. +The\ \fBNORMAL\fR codes are described in the next section. +.TS +l l l. +\fBCODE NORMAL HEADER\fR +%C pcpu %CPU +%G group GROUP +%P ppid PPID +%U user USER +%a args COMMAND +%c comm COMMAND +%g rgroup RGROUP +%n nice NI +%p pid PID +%r pgid PGID +%t etime ELAPSED +%u ruser RUSER +%x time TIME +%y tty TTY +%z vsz VSZ +.TE + +.SH "STANDARD FORMAT SPECIFIERS" +Here are the different keywords that may be used to control the output +format (e.g. with option \fB\-o\fR) or to sort the selected processes +with the GNU\-style \fB\-\-sort\fR option. + +For example: \fBps\ \-eo\ pid,user,args\ \-\-sort\ user\fR + +This version of \fBps\fR tries to recognize most of the keywords used in +other implementations of \fBps\fR. + +The following user\-defined format specifiers may contain +spaces: \fBargs\fR, \fBcmd\fR, \fBcomm\fR, \fBcommand\fR, \fBfname\fR, +\fBucmd\fR, \fBucomm\fR, +\fBlstart\fR, \fBbsdstart\fR, \fBstart\fR. + +Some keywords may not be available for sorting. + +.\" ####################################################################### +.\" lB1 lB1 lB1 lB1 s s s +.\" lB1 l1 l1 l1 s s s. +.\" +.\" lB1 lB1 lBw(5.5i) +.\" lB1 l1 l. +.\" +.TS +expand; +lB1 lB1 lBw(\n[ColSize]n) +lB1 l1 l. +CODE HEADER DESCRIPTION + +%cpu %CPU T{ +cpu utilization of the process in "##.#" format. Currently, it is the CPU time +used divided by the time the process has been running (cputime/realtime +ratio), expressed as a percentage. It will not add up to 100% unless you +are lucky. (alias\ \fBpcpu\fR). +T} + +%mem %MEM T{ +ratio of the process's resident set size to the physical memory on +the machine, expressed as a percentage. (alias\ \fBpmem\fR). +T} + +args COMMAND T{ +command with all its arguments as a string. Modifications to the arguments +may be shown. The output in this column may contain spaces. +A\ process marked is partly dead, waiting +to be fully destroyed by its parent. Sometimes the process args +will be unavailable; when this happens, \fBps\fR will instead +print the executable name in brackets. +(alias\ \fBcmd\fR,\ \fBcommand\fR). See also the \fBcomm\fR format +keyword, the \fB\-f\fR option, and the \fBc\fR option. +.br +When specified last, this column will extend to the edge of the display. +If \fBps\fR can not determine display width, as when output is redirected +(piped) into a file or another command, the output width is undefined. +(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on) +The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may +be used to exactly determine the width in this case. +The \fBw\fR or \fB\-w\fR option may be also be used to adjust width. +T} + +blocked BLOCKED T{ +mask of the blocked signals, see \fIsignal\fR(7). +According to the width of the field, +a\ 32\-bit or 64\-bit mask in hexadecimal format is displayed. +(alias\ \fBsig_block\fR,\ \fBsigmask\fR). +T} + +bsdstart START T{ +time the command started. If the process was started less +than 24 hours ago, the output format is "\ HH:MM", +else it is "mmm\ dd" +(where mmm is the three letters of the month). +See also \fBlstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR. +T} + +bsdtime TIME T{ +accumulated cpu time, user\ +\ system. The display format is usually +"MMM:SS", but can be shifted to the right if the process used more than 999 +minutes of cpu time. +T} + +c C T{ +processor utilization. Currently, this is the integer value of +the percent usage over the lifetime of the process. (see\ \fB%cpu\fR). +T} + +caught CAUGHT T{ +mask of the caught signals, see \fIsignal\fR(7). According to the +width of the field, a 32 or 64 bits mask in hexadecimal format is +displayed. (alias\ \fBsig_catch\fR,\ \fBsigcatch\fR). +T} + +class CLS T{ +scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBcls\fR). +Field's possible values are: +.br +\- not reported +.br +TS SCHED_OTHER +.br +FF SCHED_FIFO +.br +RR SCHED_RR +.br +B SCHED_BATCH +.br +ISO SCHED_ISO +.br +IDL SCHED_IDLE +.br +? unknown value +T} + +cls CLS T{ +scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBclass\fR). +Field's possible values are: +.br +\- not reported +.br +TS SCHED_OTHER +.br +FF SCHED_FIFO +.br +RR SCHED_RR +.br +B SCHED_BATCH +.br +ISO SCHED_ISO +.br +IDL SCHED_IDLE +.br +? unknown value +T} + +cmd CMD T{ +see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcommand\fR). +T} + +comm COMMAND T{ +command name (only\ the executable\ name). Modifications to the command +name will not be shown. A\ process marked is partly dead, waiting +to be fully destroyed by its parent. The output in this +column may contain spaces. (alias\ \fBucmd\fR,\ \fBucomm\fR). +See also the \fBargs\fR format +keyword, the \fB\-f\fR option, and the \fBc\fR option. +.br +When specified last, this column will extend to the edge of the display. +If \fBps\fR can not determine display width, as when output is redirected +(piped) into a file or another command, the output width is undefined. +(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on) +The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may +be used to exactly determine the width in this case. +The \fBw\fR or \fB\-w\fR option may be also be used to adjust width. +T} + +command COMMAND T{ +see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcmd\fR). +T} + +cp CP T{ +per\-mill (tenths of a percent) CPU usage. (see\ \fB%cpu\fR). +T} + +cputime TIME T{ +cumulative CPU time, "[dd\-]hh:mm:ss" format. (alias\ \fBtime\fR). +T} + +egid EGID T{ +effective group ID number of the process as a decimal integer. +(alias\ \fBgid\fR). +T} + +egroup EGROUP T{ +effective group ID of the process. This will be the textual group ID, +if it can be obtained and the field width permits, or a decimal +representation otherwise. (alias\ \fBgroup\fR). +T} + +eip EIP T{ +instruction pointer. +T} + +esp ESP T{ +stack pointer. +T} + +etime ELAPSED T{ +elapsed time since the process was started, +in\ the form\ [[dd\-]hh:]mm:ss. +T} + +euid EUID T{ +effective user\ ID. (alias\ \fBuid\fR). +T} + +euser EUSER T{ +effective user\ name. This will be the textual +user\ ID, if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +The\ \fBn\fR\ option can be used +to force the decimal representation. +(alias\ \fBuname\fR,\ \fBuser\fR). +T} + +f F T{ +flags associated with the process, see the \fBPROCESS FLAGS\fR section. +(alias\ \fBflag\fR,\ \fBflags\fR). +T} + +fgid FGID T{ +filesystem access group\ ID. (alias\ \fBfsgid\fR). +T} + +fgroup FGROUP T{ +filesystem access group\ ID. +This will be the textual user\ ID, if\ it can be obtained +and the field width permits, +or\ a\ decimal representation otherwise. +(alias\ \fBfsgroup\fR). +T} + +flag F T{ +see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflags\fR). +T} + +flags F T{ +see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflag\fR). +T} + +fname COMMAND T{ +first 8 bytes of the base name of the process's executable file. +The output in this column may contain spaces. +T} + +fuid FUID T{ +filesystem access user\ ID. (alias\ \fBfsuid\fR). +T} + +fuser FUSER T{ +filesystem access user\ ID. This will be the textual user\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +gid GID T{ +see\ \fBegid\fR. (alias\ \fBegid\fR). +T} + +group GROUP T{ +see\ \fBegroup\fR. (alias\ \fBegroup\fR). +T} + +ignored IGNORED T{ +mask of the ignored signals, see \fIsignal\fR(7). According to the +width of the field, a\ 32\-bit or 64\-bit mask in hexadecimal format +is displayed. (alias \fBsig_ignore\fR, \fBsigignore\fR). +T} + +label LABEL T{ +security label, most commonly used for SE\ Linux context data. +This is for the \fIMandatory Access Control\fR ("MAC") found on +high\-security systems. +T} + +lstart STARTED T{ +time the command started. +See also \fBbsdstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR. +T} + +lwp LWP T{ +lwp (light weight process, or thread) ID of the lwp being reported. +(alias\ \fBspid\fR,\ \fBtid\fR). +T} + +ni NI T{ +nice value. This ranges from 19 (nicest) to \-20 (not\ nice to\ others), +see\ \fInice\fR(1). (alias\ \fBnice\fR). +T} + +nice NI T{ +see\ \fBni\fR. (alias\ \fBni\fR). +T} + +nlwp NLWP T{ +number of lwps (threads) in the process. (alias\ \fBthcount\fR). +T} + +nwchan WCHAN T{ +address of the kernel function where the process is sleeping +(use \fBwchan\fR if you want the kernel function name). +Running tasks will display a dash ('\-') in this column. +T} + +pcpu %CPU T{ +see\ \fB%cpu\fR. (alias\ \fB%cpu\fR). +T} + +pending PENDING T{ +mask of the pending signals. See\ \fIsignal\fR(7). Signals pending on +the process are distinct from signals pending on individual threads. +Use the \fBm\fR option or the \fB\-m\fR option to see both. +According to the width of the field, a\ 32\-bit or 64\-bit mask in +hexadecimal format is displayed. (alias\ \fBsig\fR). +T} + +pgid PGID T{ +process group\ ID or, equivalently, the process\ ID of the +process group leader. (alias\ \fBpgrp\fR). +T} + +pgrp PGRP T{ +see\ \fBpgid\fR. (alias\ \fBpgid\fR). +T} + +pid PID T{ +process\ ID number of the process. +T} + +pmem %MEM T{ +see\ \fB%mem\fR. (alias\ \fB%mem\fR). +T} + +policy POL T{ +scheduling class of the process. (alias\ \fBclass\fR,\ \fBcls\fR). +Possible values are: +.br +\- not reported +.br +TS SCHED_OTHER +.br +FF SCHED_FIFO +.br +RR SCHED_RR +.br +B SCHED_BATCH +.br +ISO SCHED_ISO +.br +IDL SCHED_IDLE +.br +? unknown value +T} + +ppid PPID T{ +parent process ID. +T} + +psr PSR T{ +processor that process is currently assigned to. +T} + +rgid RGID T{ +real group ID. +T} + +rgroup RGROUP T{ +real group name. This will be the textual group\ ID, if\ it can be +obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +rss RSS T{ +resident set size, the non\-swapped physical memory that +a task has used (in\ kiloBytes). +(alias\ \fBrssize\fR,\ \fBrsz\fR). +T} + +rssize RSS T{ +see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrsz\fR). +T} + +rsz RSZ T{ +see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrssize\fR). +T} + +rtprio RTPRIO T{ +realtime priority. +T} + +ruid RUID T{ +real user\ ID. +T} + +ruser RUSER T{ +real user\ ID. This will be the textual user\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +s S T{ +minimal state display (one\ character). +See\ section \fBPROCESS STATE CODES\fR for the different values. +See\ also \fBstat\fR if you want additional +information displayed. (alias\ \fBstate\fR). +T} + +sched SCH T{ +scheduling policy of the process. The policies SCHED_OTHER (SCHED_NORMAL), +SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_ISO, and SCHED_IDLE are respectively +displayed as 0,\ 1,\ 2,\ 3,\ 4,\ and\ 5. +T} + +sess SESS T{ +session\ ID or, equivalently, the process\ ID of the session\ leader. +(alias\ \fBsession\fR,\ \fBsid\fR). +T} + +sgi_p P T{ +processor that the process is currently executing on. +Displays "*" if the process is not currently running or runnable. +T} + +sgid SGID T{ +saved group\ ID. +(alias\ \fBsvgid\fR). +T} + +sgroup SGROUP T{ +saved group\ name. This will be the textual group\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +sid SID T{ +see\ \fBsess\fR. (alias\ \fBsess\fR,\ \fBsession\fR). +T} + +sig PENDING T{ +see\ \fBpending\fR. (alias\ \fBpending\fR,\ \fBsig_pend\fR). +T} + +sigcatch CAUGHT T{ +see\ \fBcaught\fR. (alias\ \fBcaught\fR,\ \fBsig_catch\fR). +T} + +sigignore IGNORED T{ +see\ \fBignored\fR. (alias\ \fBignored\fR,\ \fBsig_ignore\fR). +T} + +sigmask BLOCKED T{ +see\ \fBblocked\fR. (alias\ \fBblocked\fR,\ \fBsig_block\fR). +T} + +size SZ T{ +approximate amount of swap space that would be required +if the process were to dirty all writable pages and then +be swapped out. +This number is very\ rough! +T} + +spid SPID T{ +see \fBlwp\fR. (alias\ \fBlwp\fR,\ \fBtid\fR). +T} + +stackp STACKP T{ +address of the bottom (start) of stack for the process. +T} + +start STARTED T{ +time the command started. +If the process was started less than 24 hours ago, +the output format is "HH:MM:SS", +else it is "\ \ mmm\ dd" +(where mmm is a three\-letter month\ name). +See also \fBlstart\fR, \fBbsdstart\fR, \fBstart_time\fR, and \fBstime\fR. +T} + +start_time START T{ +starting time or date of the process. +Only the year will be displayed if the process was not +started the same year \fBps\fR was invoked, +or\ "mmmdd" if\ it was not started the same day, +or\ "HH:MM" otherwise. +See also \fBbsdstart\fR, \fBstart\fR, \fBlstart\fR, and \fBstime\fR. +T} + +stat STAT T{ +multi\-character process state. +See\ section \fBPROCESS STATE CODES\fR +for the different values meaning. +See also \fBs\fR and \fBstate\fR if you just want +the first character displayed. +T} + +state S T{ +see\ \fBs\fR. (alias\ \fBs\fR). +T} + +suid SUID T{ +saved user\ ID. (alias\ \fBsvuid\fR). +T} + +suser SUSER T{ +saved user name. This will be the textual user\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +(alias\ \fBsvuser\fR). +T} + +svgid SVGID T{ +see\ \fBsgid\fR. (alias\ \fBsgid\fR). +T} + +svuid SVUID T{ +see\ \fBsuid\fR. (alias\ \fBsuid\fR). +T} + +sz SZ T{ +size in physical pages of the core image of the process. +This includes text, data, and stack space. +Device mappings are currently excluded; this is subject to change. +See \fBvsz\fR and \fBrss\fR. +T} + +thcount THCNT T{ +see \fBnlwp\fR. (alias\ \fBnlwp\fR). +number of kernel threads owned by the process. +T} + +tid TID T{ +see\ \fBlwp\fR. (alias\ \fBlwp\fR). +T} + +time TIME T{ +cumulative CPU\ time, "[dd\-]hh:mm:ss" format. (alias\ \fBcputime\fR). +T} + +tname TTY T{ +controlling tty (terminal). +(alias\ \fBtt\fR,\ \fBtty\fR). +T} + +tpgid TPGID T{ +ID of the foreground process group on the tty (terminal) that +the process is connected to, or \-1 if the process is not connected +to a tty. +T} + +tt TT T{ +controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtty\fR). +T} + +tty TT T{ +controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtt\fR). +T} + +ucmd CMD T{ +see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucomm\fR). +T} + +ucomm COMMAND T{ +see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucmd\fR). +T} + +uid UID T{ +see \fBeuid\fR. (alias\ \fBeuid\fR). +T} + +uname USER T{ +see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuser\fR). +T} + +user USER T{ +see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuname\fR). +T} + +vsize VSZ T{ +see \fBvsz\fR. (alias\ \fBvsz\fR). +T} + +vsz VSZ T{ +virtual memory size of the process in KiB (1024\-byte\ units). +Device mappings are currently excluded; this is subject to change. +(alias\ \fBvsize\fR). +T} + +wchan WCHAN T{ +name of the kernel function in which the process is sleeping, +a\ "\-"\ if the process is running, +or a "*"\ if the process is multi\-threaded and +\fBps\fR is not displaying threads. +T} +.TE +.\" ####################################################################### +.PP +.PP +.SH "ENVIRONMENT VARIABLES" +The following environment variables could affect \fBps\fR: +.TP 3 +.B COLUMNS +Override default display width. +.TP +.B LINES +Override default display height. +.TP +.B PS_PERSONALITY +Set to one of posix, old, linux, bsd, sun, digital... +(see\ section\ \fBPERSONALITY\fR\ below). +.TP +.B CMD_ENV +Set to one of posix, old, linux, bsd, sun, digital... +(see\ section\ \fBPERSONALITY\fR\ below). +.TP +.B I_WANT_A_BROKEN_PS +Force obsolete command line interpretation. +.TP +.B LC_TIME +Date format. +.TP +.B PS_COLORS +Not currently supported. +.TP +.B PS_FORMAT +Default output format override. You may set this to a format +string of the type used for the \fB\-o\fR option. +The \fBDefSysV\fR and \fBDefBSD\fR values are particularly useful. +.TP +.B PS_SYSMAP +Default namelist (System.map) location. +.TP +.B PS_SYSTEM_MAP +Default namelist (System.map) location. +.TP +.B POSIXLY_CORRECT +Don't find excuses to ignore bad "features". +.TP +.B POSIX2 +When set to "on", acts as \fBPOSIXLY_CORRECT\fR. +.TP +.B UNIX95 +Don't find excuses to ignore bad "features". +.TP +.B _XPG +Cancel \fBCMD_ENV\fI=irix\fR non\-standard behavior. +.PP +In general, it\ is a bad idea to set these variables. +The one exception is \fBCMD_ENV\fR or \fBPS_PERSONALITY\fR, +which could be set to Linux for normal systems. +Without that setting, +\fBps\fR follows the useless and bad parts of the Unix98 standard. +.PP +.PP +.SH "PERSONALITY" +.TS +l l. +390 like the S/390 OpenEdition \fBps\fR +aix like AIX \fBps\fR +bsd like FreeBSD \fBps\fR (totally\ non\-standard) +compaq like Digital Unix \fBps\fR +debian like the old Debian \fBps\fR +digital like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR +gnu like the old Debian \fBps\fR +hp like HP\-UX \fBps\fR +hpux like HP\-UX \fBps\fR +irix like Irix \fBps\fR +linux ***** RECOMMENDED ***** +old like the original Linux \fBps\fR (totally\ non\-standard) +os390 like OS/390 Open Edition \fBps\fR +posix standard +s390 like OS/390 Open Edition \fBps\fR +sco like SCO \fBps\fR +sgi like Irix \fBps\fR +solaris2 like Solaris 2+ (SunOS 5) \fBps\fR +sunos4 like SunOS 4 (Solaris 1) \fBps\fR (totally\ non\-standard) +svr4 standard +sysv standard +tru64 like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR +unix standard +unix95 standard +unix98 standard +.TE +.PP +.PP +.SH "SEE ALSO" +\fItop\fR(1), \fIpgrep\fR(1), \fIpstree\fR(1), \fIproc\fR(5). +.PP +.PP +.SH STANDARDS +This \fBps\fR conforms to: +.PP +.PD 0 +.IP 1 4 +Version 2 of the Single Unix Specification +.IP 2 4 +The Open Group Technical Standard Base Specifications, Issue\ 6 +.IP 3 4 +IEEE Std 1003.1, 2004\ Edition +.IP 4 4 +X/Open System Interfaces Extension [UP\ XSI] +.IP 5 4 +ISO/IEC 9945:2003 +.PD +.PP +.SH AUTHOR +\fBps\fR was originally written by Branko Lankester . Michael +K. Johnson re\-wrote it significantly to use the proc +filesystem, changing a few things in the process. Michael Shields + added the pid\-list feature. Charles Blake + added multi\-level sorting, the dirent\-style library, the +device name\-to\-number mmaped database, the approximate binary search +directly on System.map, and many code and documentation cleanups. David +Mossberger\-Tang wrote the generic BFD support for psupdate. Albert Cahalan + rewrote ps for full Unix98 and BSD support, along with +some ugly hacks for obsolete and foreign syntax. + +Please send bug reports to . +No\ subscription is required or suggested. diff --git a/ps/regression b/ps/regression new file mode 100644 index 0000000..c71c826 --- /dev/null +++ b/ps/regression @@ -0,0 +1,26 @@ +-u 500 -o pid,ppid,fname,comm,args # right margin trouble +-u 500 -o pid,ppid,fname,comm,args,wchan,wchan,wchan,wchan,wchan,nice,wchan +-u 500 -o pid,pid,pid,pid,user,user,user,args # had trouble +-u 500 -o user,user,user,pid,pid,pid,pid,args # no trouble! + +Test with each type of field (RIGHT,LEFT,UNLIMITED...) hanging off the +edge of the screen and each type of field to the left of the one that +hangs off the edge. + +Test "ps ef" as _both_ normal user and root. Especially after su! + +On a 108-col screen, try "ps alx" and "ps alx | cat" + +These ought to be the same: +CMD_ENV=old ps -m +CMD_ENV=old ps m + +These ought to be the same: +CMD_ENV=old ps -X +CMD_ENV=old ps X +ps X +ps -X # needs to be a non-SysV option + +This should fail: +ps x -x + diff --git a/ps/select.c b/ps/select.c new file mode 100644 index 0000000..2c52d02 --- /dev/null +++ b/ps/select.c @@ -0,0 +1,146 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "../proc/readproc.h" +#include "../proc/procps.h" + +//#define process_group_leader(p) ((p)->pgid == (p)->tgid) +//#define some_other_user(p) ((p)->euid != cached_euid) +#define has_our_euid(p) ((unsigned)(p)->euid == (unsigned)cached_euid) +#define on_our_tty(p) ((unsigned)(p)->tty == (unsigned)cached_tty) +#define running(p) (((p)->state=='R')||((p)->state=='D')) +#define session_leader(p) ((p)->session == (p)->tgid) +#define without_a_tty(p) (!(p)->tty) + +static unsigned long select_bits = 0; + +/***** prepare select_bits for use */ +const char *select_bits_setup(void){ + int switch_val = 0; + /* don't want a 'g' screwing up simple_select */ + if(!simple_select && !prefer_bsd_defaults){ + select_bits = 0xaa00; /* the STANDARD selection */ + return NULL; + } + /* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */ + if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) ) + switch_val = simple_select|SS_B_g; + else + switch_val = simple_select; + switch(switch_val){ + /* UNIX options */ + case SS_U_a | SS_U_d: select_bits = 0x3f3f; break; /* 3333 or 3f3f */ + case SS_U_a: select_bits = 0x0303; break; /* 0303 or 0f0f */ + case SS_U_d: select_bits = 0x3333; break; + /* SunOS 4 only (others have 'g' enabled all the time) */ + case 0: select_bits = 0x0202; break; + case SS_B_a: select_bits = 0x0303; break; + case SS_B_x : select_bits = 0x2222; break; + case SS_B_x | SS_B_a: select_bits = 0x3333; break; + /* General BSD options */ + case SS_B_g : select_bits = 0x0a0a; break; + case SS_B_g | SS_B_a: select_bits = 0x0f0f; break; + case SS_B_g | SS_B_x : select_bits = 0xaaaa; break; + case SS_B_g | SS_B_x | SS_B_a: /* convert to -e instead of using 0xffff */ + all_processes = 1; + simple_select = 0; + break; + default: + return "Process selection options conflict."; + break; + } + return NULL; +} + +/***** selected by simple option? */ +static int table_accept(proc_t *buf){ + unsigned proc_index; + proc_index = (has_our_euid(buf) <<0) + | (session_leader(buf) <<1) + | (without_a_tty(buf) <<2) + | (on_our_tty(buf) <<3); + return (select_bits & (1<typecode){ + default: + printf("Internal error in ps! Please report this bug.\n"); + +#define return_if_match(foo,bar) \ + i=sn->n; while(i--) \ + if((unsigned)(buf->foo) == (unsigned)(*(sn->u+i)).bar) \ + return 1 + + break; case SEL_RUID: return_if_match(ruid,uid); + break; case SEL_EUID: return_if_match(euid,uid); + break; case SEL_SUID: return_if_match(suid,uid); + break; case SEL_FUID: return_if_match(fuid,uid); + + break; case SEL_RGID: return_if_match(rgid,gid); + break; case SEL_EGID: return_if_match(egid,gid); + break; case SEL_SGID: return_if_match(sgid,gid); + break; case SEL_FGID: return_if_match(fgid,gid); + + break; case SEL_PGRP: return_if_match(pgrp,pid); + break; case SEL_PID : return_if_match(tgid,pid); + break; case SEL_PPID: return_if_match(ppid,ppid); + break; case SEL_TTY : return_if_match(tty,tty); + break; case SEL_SESS: return_if_match(session,pid); + + break; case SEL_COMM: i=sn->n; while(i--) + if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 15 )) return 1; + + + +#undef return_if_match + + } + sn = sn->next; + } + return 0; +} + + +/***** This must satisfy Unix98 and as much BSD as possible */ +int want_this_proc(proc_t *buf){ + int accepted_proc = 1; /* assume success */ + /* elsewhere, convert T to list, U sets x implicitly */ + + /* handle -e -A */ + if(all_processes) goto finish; + + /* use table for -a a d g x */ + if((simple_select || !selection_list)) + if(table_accept(buf)) goto finish; + + /* search lists */ + if(proc_was_listed(buf)) goto finish; + + /* fail, fall through to loose ends */ + accepted_proc = 0; + + /* do r N */ +finish: + if(running_only && !running(buf)) accepted_proc = 0; + if(negate_selection) return !accepted_proc; + return accepted_proc; +} diff --git a/ps/sortformat.c b/ps/sortformat.c new file mode 100644 index 0000000..80427e5 --- /dev/null +++ b/ps/sortformat.c @@ -0,0 +1,955 @@ +/* + * Copyright 1998-2004 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include + +/* username lookups */ +#include +#include +#include + +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "common.h" + +static sf_node *sf_list = NULL; /* deferred sorting and formatting */ +static int broken; /* use gross Unix98 parsing? */ +static int have_gnu_sort = 0; /* if true, "O" must be format */ +static int already_parsed_sort = 0; /* redundantly set in & out of fn */ +static int already_parsed_format = 0; + + +/**************** Parse single format specifier *******************/ +static format_node *do_one_spec(const char *spec, const char *override){ + const format_struct *fs; + const macro_struct *ms; + + fs = search_format_array(spec); + if(fs){ + int w1, w2; + format_node *thisnode; + thisnode = malloc(sizeof(format_node)); + if(fs->flags & CF_PIDMAX){ + w1 = (int)get_pid_digits(); + w2 = strlen(fs->head); + if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing + }else{ + w1 = fs->width; + } + if(override){ + w2 = strlen(override); + thisnode->width = (w1>w2)?w1:w2; + thisnode->name = malloc(strlen(override)+1); + strcpy(thisnode->name, override); + }else{ + thisnode->width = w1; + thisnode->name = malloc(strlen(fs->head)+1); + strcpy(thisnode->name, fs->head); + } + thisnode->pr = fs->pr; + thisnode->need = fs->need; + thisnode->vendor = fs->vendor; + thisnode->flags = fs->flags; + thisnode->next = NULL; + return thisnode; + } + + /* That failed, so try it as a macro. */ + ms = search_macro_array(spec); + if(ms){ + format_node *list = NULL; + format_node *newnode; + const char *walk; + int dist; + char buf[16]; /* trust strings will be short (from above, not user) */ + walk = ms->head; + while(*walk){ + dist = strcspn(walk, ", "); + strncpy(buf,walk,dist); + buf[dist] = '\0'; + newnode = do_one_spec(buf,override); /* call self, assume success */ + newnode->next = list; + list = newnode; + walk += dist; + if(*walk) walk++; + } + return list; + } + return NULL; /* bad, spec not found */ +} + + +/************ must wrap user format in default *************/ +static void O_wrap(sf_node *sfn, int otype){ + format_node *fnode; + format_node *endp; + const char *trailer; + + trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ; + + fnode = do_one_spec("pid",NULL); + if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n"); + endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */ + endp->next = fnode; + + fnode = do_one_spec(trailer,NULL); + if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n"); + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; +} + +/****************************************************************** + * Used to parse option AIX field descriptors. + * Put each completed format_node onto the list starting at ->f_cooked + */ +static const char *aix_format_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *walk; + int items; + + /*** sanity check and count items ***/ + items = 0; + walk = sfn->sf; + /* state machine */ { + int c; + initial: + c = *walk++; + if(c=='%') goto get_desc; + if(!c) goto looks_ok; + /* get_text: */ + items++; + get_more_text: + c = *walk++; + if(c=='%') goto get_desc; + if(c) goto get_more_text; + goto looks_ok; + get_desc: + items++; + c = *walk++; + if(c) goto initial; + return "Improper AIX field descriptor."; + looks_ok: + ; + } + + /*** sanity check passed ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + walk = sfn->sf; + + while(items--){ + format_node *fnode; /* newly allocated */ + format_node *endp; /* for list manipulation */ + + if(*walk == '%'){ + const aix_struct *aix; + walk++; + if(*walk == '%') goto double_percent; + aix = search_aix_array(*walk); + walk++; + if(!aix){ + free(buf); + return "Unknown AIX field descriptor."; + } + fnode = do_one_spec(aix->spec, aix->head); + if(!fnode){ + free(buf); + return "AIX field descriptor processing bug."; + } + } else { + int len; + len = strcspn(walk, "%"); + memcpy(buf,walk,len); + if(0){ +double_percent: + len = 1; + buf[0] = '%'; + } + buf[len] = '\0'; + walk += len; + fnode = malloc(sizeof(format_node)); + fnode->width = len; + fnode->name = malloc(len+1); + strcpy(fnode->name, buf); + fnode->pr = NULL; /* checked for */ + fnode->need = 0; + fnode->vendor = AIX; + fnode->flags = CF_PRINT_EVERY_TIME; + fnode->next = NULL; + } + + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; + } + free(buf); + already_parsed_format = 1; + return NULL; +} + +/*************************************************************** + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + * Put each completed format_node onto the list starting at ->f_cooked + */ +static const char *format_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t,\n" */ + char *walk; + const char *err; /* error code that could or did happen */ + format_node *fnode; + int items; + int need_item; + static char errbuf[80]; /* for variable-text error message */ + + /*** prepare to operate ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\n': case '\0': + /* Linux extension: allow \t and \n as delimiters */ + if(need_item){ + free(buf); + goto improper; + } + need_item=1; + break; + case '=': + if(broken) goto out; + /* fall through */ + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); +out: + if(!items){ + free(buf); + goto empty; + } +#ifdef STRICT_LIST + if(need_item){ /* can't have trailing deliminator */ + free(buf); + goto improper; + } +#else + if(need_item){ /* allow 1 trailing deliminator */ + *--walk='\0'; /* remove the trailing deliminator */ + } +#endif + /*** actually parse the list ***/ + walk = buf; + while(items--){ + format_node *endp; + char *equal_loc; + char *colon_loc; + sep_loc = strpbrk(walk," ,\t\n"); + /* if items left, then sep_loc is not in header override */ + if(items && sep_loc) *sep_loc = '\0'; + equal_loc = strpbrk(walk,"="); + if(equal_loc){ /* if header override */ + *equal_loc = '\0'; + equal_loc++; + } + colon_loc = strpbrk(walk,":"); + if(colon_loc){ /* if width override */ + *colon_loc = '\0'; + colon_loc++; + if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc){ + free(buf); + goto badwidth; + } + } + fnode = do_one_spec(walk,equal_loc); + if(!fnode){ + if(!*errbuf){ /* if didn't already create an error string */ + snprintf( + errbuf, + sizeof(errbuf), + "Unknown user-defined format specifier \"%s\".", + walk + ); + } + free(buf); + goto unknown; + } + if(colon_loc){ + if(fnode->next){ + free(buf); + goto notmacro; + } + // FIXME: enforce signal width to 8, 9, or 16 (grep: SIGNAL wide_signals) + fnode->width = atoi(colon_loc); // already verified to be a number + } + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + already_parsed_format = 1; + return NULL; + + /* errors may cause a retry looking for AIX format codes */ + if(0) unknown: err=errbuf; + if(0) empty: err="Empty format list."; + if(0) improper: err="Improper format list."; + if(0) badwidth: err="Column widths must be unsigned decimal numbers."; + if(0) notmacro: err="Can't set width for a macro (multi-column) format specifier."; + if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn); + return err; +} + +/**************** Parse single sort specifier *******************/ +static sort_node *do_one_sort_spec(const char *spec){ + const format_struct *fs; + int reverse = 0; + if(*spec == '-'){ + reverse = 1; + spec++; + } else if(*spec == '+'){ + spec++; + } + fs = search_format_array(spec); + if(fs){ + sort_node *thisnode; + thisnode = malloc(sizeof(sort_node)); + thisnode->sr = fs->sr; + thisnode->need = fs->need; + thisnode->reverse = reverse; + thisnode->next = NULL; + return thisnode; + } + return NULL; /* bad, spec not found */ +} + + +/************************************************************** + * Used to parse long sorting options. + * Put each completed sort_node onto the list starting at ->s_cooked + */ +static const char *long_sort_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t,\n" */ + char *walk; + sort_node *snode; + int items; + int need_item; + + /*** prepare to operate ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\n': case '\0': + if(need_item){ + free(buf); + return "Improper sort list"; + } + need_item=1; + break; + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); + if(!items){ + free(buf); + return "Empty sort list."; + } +#ifdef STRICT_LIST + if(need_item){ /* can't have trailing deliminator */ + free(buf); + return "Improper sort list."; + } +#else + if(need_item){ /* allow 1 trailing deliminator */ + *--walk='\0'; /* remove the trailing deliminator */ + } +#endif + /*** actually parse the list ***/ + walk = buf; + while(items--){ + sort_node *endp; + sep_loc = strpbrk(walk," ,\t\n"); + if(sep_loc) *sep_loc = '\0'; + snode = do_one_sort_spec(walk); + if(!snode){ + free(buf); + return "Unknown sort specifier."; + } + endp = snode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->s_cooked; + sfn->s_cooked = snode; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + already_parsed_sort = 1; + return NULL; +} + + + + + + +/************ pre-parse short sorting option *************/ +/* Errors _must_ be detected so that the "O" option can try to + * reparse as formatting codes. + */ +static const char *verify_short_sort(const char *arg){ + const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-"; + char checkoff[256]; + int i; + const char *walk; + int tmp; + if(strspn(arg,all) != strlen(arg)) return "Bad sorting code."; + for(i=256; i--;) checkoff[i] = 0; + walk = arg; + for(;;){ + tmp = *walk; + switch(tmp){ + case '\0': + return NULL; /* looks good */ + case '+': + case '-': + tmp = *(walk+1); + if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code."; + break; + case 'P': + if(forest_type) return "PPID sort and forest output conflict."; + /* fall through */ + default: + if(checkoff[tmp]) return "Bad sorting code."; /* repeated */ + /* ought to check against already accepted sort options */ + checkoff[tmp] = 1; + break; + } + walk++; + } +} + + + +/************ parse short sorting option *************/ +static const char *short_sort_parse(sf_node *sfn){ + int direction = 0; + const char *walk; + int tmp; + sort_node *snode; + sort_node *endp; + const struct shortsort_struct *ss; + walk = sfn->sf; + for(;;){ + tmp = *walk; + switch(tmp){ + case '\0': + already_parsed_sort = 1; + return NULL; + case '+': + direction = 0; + break; + case '-': + direction = 1; + break; + default: + ss = search_shortsort_array(tmp); + if(!ss) return "Unknown sort specifier."; + snode = do_one_sort_spec(ss->spec); + if(!snode) return "Unknown sort specifier."; + snode->reverse = direction; + endp = snode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->s_cooked; + sfn->s_cooked = snode; + direction = 0; + break; + } + walk++; + } +} + +/******************* high-level below here *********************/ + + +/* + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + * Recursion is to preserve original order. + */ +static const char *parse_O_option(sf_node *sfn){ + const char *err; /* error code that could or did happen */ + + if(sfn->next){ + err = parse_O_option(sfn->next); + if(err) return err; + } + + switch(sfn->sf_code){ + case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/ + err = format_parse(sfn); + if(!err) already_parsed_format = 1; + break; + case SF_U_O: /*** format ***/ + /* Can have -l -f f u... set already_parsed_format like DEC does */ + if(already_parsed_format) return "option -O can not follow other format options."; + err = format_parse(sfn); + if(err) return err; + already_parsed_format = 1; + O_wrap(sfn,'u'); /* must wrap user format in default */ + break; + case SF_B_O: /*** both ***/ + if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options."; + else err = verify_short_sort(sfn->sf); + if(!err){ /* success as sorting code */ + short_sort_parse(sfn); + already_parsed_sort = 1; + return NULL; + } + if(already_parsed_format){ + err = "option O is neither first format nor sort order."; + break; + } + if(!format_parse(sfn)){ /* if success as format code */ + already_parsed_format = 1; + O_wrap(sfn,'b'); /* must wrap user format in default */ + return NULL; + } + break; + case SF_G_sort: case SF_B_m: /*** sort ***/ + if(already_parsed_sort) err = "Multiple sort options."; + else err = long_sort_parse(sfn); + already_parsed_sort = 1; + break; + default: /*** junk ***/ + return "Bug: parse_O_option got weirdness!"; + } + return err; /* could be NULL */ +} + + +/************ Main parser calls this to save lists for later **********/ +/* store data for later and return 1 if arg looks non-standard */ +int defer_sf_option(const char *arg, int source){ + sf_node *sfn; + char buf[16]; + int dist; + const format_struct *fs; + int need_item = 1; + + sfn = malloc(sizeof(sf_node)); + sfn->sf = malloc(strlen(arg)+1); + strcpy(sfn->sf, arg); + sfn->sf_code = source; + sfn->s_cooked = NULL; + sfn->f_cooked = NULL; + sfn->next = sf_list; + sf_list = sfn; + + if(source == SF_G_sort) have_gnu_sort = 1; + + /* Now try to find an excuse to ignore broken Unix98 parsing. */ + if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */ + do{ + switch(*arg){ + case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */ + if(need_item) return 1; /* something wrong */ + need_item=1; + break; + case '=': + if(need_item) return 1; /* something wrong */ + return 0; /* broken Unix98 parsing is required */ + default: + if(!need_item) break; + need_item=0; + dist = strcspn(arg,", ="); + if(dist>15) return 1; /* something wrong, sort maybe? */ + strncpy(buf,arg,dist); /* no '\0' on end */ + buf[dist] = '\0'; /* fix that problem */ + fs = search_format_array(buf); + if(!fs) return 1; /* invalid spec, macro or sort maybe? */ + if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */ + } + } while (*++arg); + + return 0; /* boring, Unix98 is no change */ +} + +/***** Since ps is not long-lived, the memory leak can be ignored. ******/ +void reset_sortformat(void){ + sf_list = NULL; /* deferred sorting and formatting */ + format_list = NULL; /* digested formatting options */ + sort_list = NULL; /* digested sorting options (redundant?) */ + have_gnu_sort = 0; + already_parsed_sort = 0; + already_parsed_format = 0; +} + + +/***** Search format_list for findme, then insert putme after findme. ****/ +static int fmt_add_after(const char *findme, format_node *putme){ + format_node *walk; + if(!strcmp(format_list->name, findme)){ + putme->next = format_list->next; + format_list->next = putme; + return 1; /* success */ + } + walk = format_list; + while(walk->next){ + if(!strcmp(walk->next->name, findme)){ + putme->next = walk->next->next; + walk->next->next = putme; + return 1; /* success */ + } + walk = walk->next; + } + return 0; /* fail */ +} + +/******* Search format_list for findme, then delete it. ********/ +static int fmt_delete(const char *findme){ + format_node *walk; + format_node *old; + if(!strcmp(format_list->name, findme)){ + old = format_list; + format_list = format_list->next; + free(old); + return 1; /* success */ + } + walk = format_list; + while(walk->next){ + if(!strcmp(walk->next->name, findme)){ + old = walk->next; + walk->next = walk->next->next; + free(old); + return 1; /* success */ + } + walk = walk->next; + } + return 0; /* fail */ +} + + +/************ Build a SysV format backwards. ***********/ +#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn) +static const char *generate_sysv_list(void){ + format_node *fn; + if((format_modifiers & FM_y) && !(format_flags & FF_Ul)) + return "Modifier -y without format -l makes no sense."; + if(prefer_bsd_defaults){ + if(format_flags) PUSH("cmd"); + else PUSH("args"); + PUSH("bsdtime"); + if(!(format_flags & FF_Ul)) PUSH("stat"); + }else{ + if(format_flags & FF_Uf) PUSH("cmd"); + else PUSH("ucmd"); + PUSH("time"); + } + PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */ + if(format_flags & FF_Uf) PUSH("stime"); + /* avoid duplicate columns from -FP and -Fly */ + if(format_modifiers & FM_F){ + /* if -FP take the Sun-style column instead (sorry about "sgi_p") */ + if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */ + /* if -Fly take the ADDR-replacement RSS instead */ + if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss"); + } + if(format_flags & FF_Ul){ + PUSH("wchan"); + } + /* since FM_y adds RSS anyway, don't do this hack when that is true */ + if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){ + if(personality & PER_IRIX_l){ /* add "rss" then ':' here */ + PUSH("sgi_rss"); + fn = malloc(sizeof(format_node)); + fn->width = 1; + fn->name = malloc(2); + strcpy(fn->name, ":"); + fn->pr = NULL; /* checked for */ + fn->need = 0; + fn->vendor = AIX; /* yes, for SGI weirdness */ + fn->flags = CF_PRINT_EVERY_TIME; + fn->next = format_list; + format_list=fn; + } + } + if((format_modifiers & FM_F) || (format_flags & FF_Ul)){ + PUSH("sz"); + } + if(format_flags & FF_Ul){ + if(format_modifiers & FM_y) PUSH("rss"); + else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p"); + else PUSH("addr_1"); + } + if(format_modifiers & FM_c){ + PUSH("pri"); PUSH("class"); + }else if(format_flags & FF_Ul){ + PUSH("ni"); + if(personality & PER_IRIX_l) PUSH("priority"); + else /* is this good? */ PUSH("opri"); + } + + // FIXME TODO XXX -- this is a serious problem + // These somehow got flipped around. + // The bug is in procps-3.1.1, procps-990211, prior too? + if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp"); + if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c"); + + if(format_modifiers & FM_P) PUSH("psr"); + if(thread_flags & TF_U_L) PUSH("lwp"); + if(format_modifiers & FM_j){ + PUSH("sid"); + PUSH("pgid"); + } + if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid"); + if(thread_flags & TF_U_T) PUSH("spid"); + PUSH("pid"); + if(format_flags & FF_Uf){ + if(personality & PER_SANE_USER) PUSH("user"); + else PUSH("uid_hack"); + }else if(format_flags & FF_Ul){ + PUSH("uid"); + } + if(format_flags & FF_Ul){ + PUSH("s"); + if(!(format_modifiers & FM_y)) PUSH("f"); + } + if(format_modifiers & FM_M){ + PUSH("label"); /* Mandatory Access Control */ + } + return NULL; +} + + +/************************************************************************** + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + */ +const char *process_sf_options(int localbroken){ + sf_node *sf_walk; + + if(personality & PER_BROKEN_o) localbroken = 1; + if(personality & PER_GOOD_o) localbroken = 0; + broken = localbroken; + if(sf_list){ + const char *err; + err = parse_O_option(sf_list); + if(err) return err; + } + + if(format_list) printf("Bug: must reset the list first!\n"); + + /* merge formatting info of sf_list into format_list here */ + sf_walk = sf_list; + while(sf_walk){ + format_node *fmt_walk; + fmt_walk = sf_walk->f_cooked; + sf_walk->f_cooked = NULL; + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + sf_walk = sf_walk->next; + } + + /* merge sorting info of sf_list into sort_list here */ + sf_walk = sf_list; + while(sf_walk){ + sort_node *srt_walk; + srt_walk = sf_walk->s_cooked; + sf_walk->s_cooked = NULL; + while(srt_walk){ /* put any nodes onto sort_list in opposite way */ + sort_node *travler; + travler = srt_walk; + srt_walk = srt_walk->next; + travler->next = sort_list; + sort_list = travler; + } + sf_walk = sf_walk->next; + } + + // Get somebody to explain how -L/-T is supposed to interact + // with sorting. Do the threads remain grouped, with sorting + // by process, or do the threads get sorted by themselves? + if(sort_list && (thread_flags&TF_no_sort)){ + return "Tell procps-feedback@lists.sf.net what you expected."; + } + + // If nothing else, try to use $PS_FORMAT before the default. + if(!format_flags && !format_modifiers && !format_list){ + char *tmp; + tmp = getenv("PS_FORMAT"); /* user override kills default */ + if(tmp && *tmp){ + const char *err; + sf_node sfn; + if(thread_flags&TF_must_use) return "Tell procps-feedback@sf.net what you want. (-L/-T, -m/m/H, and $PS_FORMAT)"; + sfn.sf = tmp; + sfn.f_cooked = NULL; + err = format_parse(&sfn); + if(!err){ + format_node *fmt_walk; + fmt_walk = sfn.f_cooked; + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + return NULL; + } + // FIXME: prove that this won't be hit on valid bogus-BSD options + fprintf(stderr, "Warning: $PS_FORMAT ignored. (%s)\n", err); + } + } + + if(format_list){ + if(format_flags) return "Conflicting format options."; + if(format_modifiers) return "Can't use output modifiers with user-defined output"; + if(thread_flags&TF_must_use) return "-L/-T with H/m/-m and -o/-O/o/O is nonsense"; + return NULL; + } + + do{ + const char *spec; + switch(format_flags){ + + default: return "Conflicting format options."; + + /* These can be NULL, which enables SysV list generation code. */ + case 0: spec=NULL; break; + case FF_Uf | FF_Ul: spec=sysv_fl_format; break; + case FF_Uf: spec=sysv_f_format; break; + case FF_Ul: spec=sysv_l_format; break; + + /* These are NOT REACHED for normal -j processing. */ + case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */ + case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */ + case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */ + + /* These are true BSD options. */ + case FF_Bj: spec=bsd_j_format; break; + case FF_Bl: spec=bsd_l_format; break; + case FF_Bs: spec=bsd_s_format; break; + case FF_Bu: spec=bsd_u_format; break; + case FF_Bv: spec=bsd_v_format; break; + + /* These are old Linux options. Option m is overloaded. */ + case FF_LX: spec="OL_X"; break; + case FF_Lm: spec="OL_m"; break; + + /* This is the sole FLASK security option. */ + case FF_Fc: spec="FLASK_context"; break; + + } /* end switch(format_flags) */ + + // not just for case 0, since sysv_l_format and such may be NULL + if(!spec) return generate_sysv_list(); + + do{ + format_node *fmt_walk; + fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */ + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + }while(0); + }while(0); + + do{ + format_node *fn; + if(format_modifiers & FM_j){ + fn = do_one_spec("pgid", NULL); + if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn)) + return "Internal error, no PID or PPID for -j option."; + fn = do_one_spec("sid", NULL); + if(!fmt_add_after("PGID", fn)) return "Lost my PGID!"; + } + if(format_modifiers & FM_y){ + /* TODO: check for failure to do something, and complain if so */ + fmt_delete("F"); + fn = do_one_spec("rss", NULL); + if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR"); + } + if(format_modifiers & FM_c){ + fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C"); + fmt_delete("NI"); + fn = do_one_spec("class", NULL); + if(!fmt_add_after("PRI", fn)) + return "Internal error, no PRI for -c option."; + fmt_delete("PRI"); /* we want a different one */ + fn = do_one_spec("pri", NULL); + if(!fmt_add_after("CLS", fn)) return "Lost my CLS!"; + } + if(thread_flags & TF_U_T){ + fn = do_one_spec("spid", NULL); + if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use)) + return "-T with H/-m/m but no PID for SPID to follow"; + } + if(thread_flags & TF_U_L){ + fn = do_one_spec("lwp", NULL); + if(fmt_add_after("SID", fn)) goto did_lwp; + if(fmt_add_after("SESS", fn)) goto did_lwp; + if(fmt_add_after("PGID", fn)) goto did_lwp; + if(fmt_add_after("PGRP", fn)) goto did_lwp; + if(fmt_add_after("PPID", fn)) goto did_lwp; + if(fmt_add_after("PID", fn)) goto did_lwp; + if(thread_flags&TF_must_use) + return "-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow"; +did_lwp: + fn = do_one_spec("nlwp", NULL); + fmt_add_after("%CPU", fn); + } + if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style + fn = do_one_spec("label", NULL); + fn->next=format_list; + format_list=fn; + } + /* Do personality-specific translations not covered by format_flags. + * Generally, these only get hit when personality overrides unix output. + * That (mostly?) means the Digital and Debian personalities. + */ + if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){ + fn = do_one_spec("sgi_p", NULL); + if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR"); + } + if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){ + fn = do_one_spec("user", NULL); + if(fmt_add_after("UID", fn)) fmt_delete("UID"); + } + }while(0); + + return NULL; +} + diff --git a/pwdx.1 b/pwdx.1 new file mode 100644 index 0000000..728157d --- /dev/null +++ b/pwdx.1 @@ -0,0 +1,37 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for pwdx +.\" Licensed under version 2 of the GNU General Public License. +.\" Copyright 2004 Nicholas Miell. +.\" Based on the pmap(1) man page by Albert Cahalan. +.\" +.TH PWDX 1 "September 8, 2004" "Linux" "Linux User's Manual" +.SH NAME +pwdx \- report current working directory of a process + +.SH SYNOPSIS +.nf +pwdx pids... +pwdx -V +.fi + +.SH DESCRIPTION +The pwdx command reports the current working directory of a process or +processes. + +.SH "GENERAL OPTIONS" +.TS +l l l. +-V show version Displays version of program. +.TE + +.SH "SEE ALSO" +ps(1) pgrep(1) + +.SH STANDARDS +No standards apply, but pwdx looks an awful lot like a SunOS command. + +.SH AUTHOR +Nicholas Miell wrote pwdx in 2004. Please send bug +reports to . diff --git a/pwdx.c b/pwdx.c new file mode 100644 index 0000000..cb96a52 --- /dev/null +++ b/pwdx.c @@ -0,0 +1,95 @@ +// Copyright 2004 Nicholas Miell +// +// This file may be used subject to the terms and conditions of the +// GNU Library General Public License Version 2 as published by the +// Free Software Foundation.This program is distributed in the hope +// that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU Library General Public License for more +// details. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/version.h" + +static void die(const char *msg) NORETURN; +static void die(const char *msg) +{ + fputs(msg, stderr); + exit(1); +} + +static void version(void) NORETURN; +static void version(void) +{ + printf("pwdx (%s)\n", procps_version); + exit(0); +} + +int main(int argc, char* argv[]) +{ + char buf[PATH_MAX+1]; + regex_t re; + int i; + + if (argc < 2) + die("Usage: pwdx pid...\n"); + + // Allowed on the command line: + // + // --version + // -V + // /proc/nnnn + // nnnn + // + // where nnnn is any number that doesn't begin with 0. + // + // If --version or -V are present, further arguments are ignored + // completely. + + regcomp(&re, "^((/proc/+)?[1-9][0-9]*|-V|--version)$", + REG_EXTENDED|REG_NOSUB); + + for (i = 1; i < argc; i++) { + if (regexec(&re, argv[i], 0, NULL, 0) != 0) { + snprintf(buf, sizeof buf, "pwdx: invalid process id: %s\n", argv[i]); + die(buf); + } + if (!strcmp("-V", argv[i]) || !strcmp("--version", argv[i])) + version(); + } + + regfree(&re); + + for (i = 1; i < argc; i++) { + char * s = buf; + int len; + + // At this point, all arguments are in the form /proc/nnnn + // or nnnn, so a simple check based on the first char is + // possible + if (argv[i][0] != '/') + snprintf(buf, sizeof buf, "/proc/%s/cwd", argv[i]); + else + snprintf(buf, sizeof buf, "%s/cwd", argv[i]); + + // buf contains /proc/nnnn/cwd symlink name on entry, the + // target of that symlink on return + if ((len = readlink(buf, buf, PATH_MAX)) < 0) { + s = strerror(errno == ENOENT ? ESRCH : errno); + } else { + buf[len] = 0; + } + + printf("%s: %s\n", argv[i], s); + } + + return 0; +} diff --git a/skill.1 b/skill.1 new file mode 100644 index 0000000..a4fc274 --- /dev/null +++ b/skill.1 @@ -0,0 +1,123 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for skill and snice. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan, converted to a man page by +.\" Michael K. Johnson +.\" +.TH SKILL 1 "March 12, 1999" "Linux" "Linux User's Manual" +.SH NAME +skill, snice \- send a signal or report process status + +.SH SYNOPSIS +.nf +skill [signal to send] [options] process selection criteria +snice [new priority] [options] process selection criteria +.fi + +.SH DESCRIPTION +These tools are probably obsolete and unportable. The command +syntax is poorly defined. Consider using the killall, pkill, +and pgrep commands instead. + +The default signal for skill is TERM. Use -l or -L to list available signals. +Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0. +Alternate signals may be specified in three ways: -9 -SIGKILL -KILL. + +The default priority for snice is +4. (snice +4 ...) +Priority numbers range from +20 (slowest) to -20 (fastest). +Negative priority numbers are restricted to administrative users. + +.SH "GENERAL OPTIONS" +.TS +l l l. +-f fast mode This is not currently useful. +-i interactive use T{ +You will be asked to approve each action. +T} +-v verbose output T{ +Display information about selected processes. +T} +-w warnings enabled This is not currently useful. +-n no action This only displays the process ID. +-V show version Displays version of program. +.TE + +.SH "PROCESS SELECTION OPTIONS" +Selection criteria can be: terminal, user, pid, command. +The options below may be used to ensure correct interpretation. +Do not blame Albert for this interesting interface. +.TS +l l. +-t The next argument is a terminal (tty or pty). +-u The next argument is a username. +-p The next argument is a process ID number. +-c The next argument is a command name. +.TE + +.SH SIGNALS +The signals listed below may be available for use with skill. +When known, numbers and default behavior are shown. +.TS +lB rB lB lB +lfCW r l l. +Name Num Action Description +.TH +0 0 n/a exit code indicates if a signal may be sent +ALRM 14 exit +HUP 1 exit +INT 2 exit +KILL 9 exit this signal may not be blocked +PIPE 13 exit +POLL exit +PROF exit +TERM 15 exit +USR1 exit +USR2 exit +VTALRM exit +STKFLT exit may not be implemented +PWR ignore may exit on some systems +WINCH ignore +CHLD ignore +URG ignore +TSTP stop may interact with the shell +TTIN stop may interact with the shell +TTOU stop may interact with the shell +STOP stop this signal may not be blocked +CONT restart continue if stopped, otherwise ignore +ABRT 6 core +FPE 8 core +ILL 4 core +QUIT 3 core +SEGV 11 core +TRAP 5 core +SYS core may not be implemented +EMT core may not be implemented +BUS core core dump may fail +XCPU core core dump may fail +XFSZ core core dump may fail +.TE + +.SH EXAMPLES +.TS +lB lB +lfCW l. +Command Description +.TC +snice seti crack +7 Slow down seti and crack +skill -KILL -v /dev/pts/* Kill users on new-style PTY devices +skill -STOP viro lm davem Stop 3 users +snice -17 root bash Give priority to root's shell +.TE + +.SH "SEE ALSO" +killall(1) pkill(1) kill(1) renice(1) nice(1) signal(7) kill(2) + +.SH STANDARDS +No standards apply. + +.SH AUTHOR +Albert Cahalan wrote skill and snice in 1999 as a +replacement for a non-free version, and is the current maintainer of the +procps collection. Please send bug reports to . diff --git a/skill.c b/skill.c new file mode 100644 index 0000000..96cc386 --- /dev/null +++ b/skill.c @@ -0,0 +1,585 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/pwcache.h" +#include "proc/sig.h" +#include "proc/devname.h" +#include "proc/procps.h" /* char *user_from_uid(uid_t uid) */ +#include "proc/version.h" /* procps_version */ + +static int f_flag, i_flag, v_flag, w_flag, n_flag; + +static int tty_count, uid_count, cmd_count, pid_count; +static int *ttys; +static uid_t *uids; +static const char **cmds; +static int *pids; + +#define ENLIST(thing,addme) do{ \ +if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \ +if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \ +thing##s[thing##_count++] = addme; \ +}while(0) + +static int my_pid; +static int saved_argc; + +static int sig_or_pri; + +static int program; +#define PROG_GARBAGE 0 /* keep this 0 */ +#define PROG_KILL 1 +#define PROG_SKILL 2 +/* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */ +#define PROG_SNICE 4 + + +/********************************************************************/ + +static void display_kill_version(void){ + switch(program) { + case PROG_KILL: + fprintf(stdout, "kill (%s)\n",procps_version); + return; + case PROG_SKILL: + fprintf(stdout, "skill (%s)\n",procps_version); + return; + case PROG_SNICE: + fprintf(stdout, "snice (%s)\n",procps_version); + return; + default: + fprintf(stdout, "unknown (%s)\n",procps_version); + return; + } +} + +/***** kill or nice a process */ +static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd){ + int failed; + int saved_errno; + char dn_buf[1000]; + dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV); + if(i_flag){ + char buf[8]; + fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + if(!fgets(buf,7,stdin)){ + printf("\n"); + exit(0); + } + if(*buf!='y' && *buf!='Y') return; + } + /* do the actual work */ + if(program==PROG_SKILL) failed=kill(pid,sig_or_pri); + else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri); + saved_errno = errno; + if(w_flag && failed){ + fprintf(stderr, "%-8s %-8s %5d %-16.16s ", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + errno = saved_errno; + perror(""); + return; + } + if(i_flag) return; + if(v_flag){ + printf("%-8s %-8s %5d %-16.16s\n", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + return; + } + if(n_flag){ + printf("%d\n",pid); + return; + } +} + + +/***** check one process */ +static void check_proc(int pid){ + char buf[128]; + struct stat statbuf; + char *tmp; + int tty; + int fd; + int i; + if(pid==my_pid) return; + sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */ + fd = open(buf,O_RDONLY); + if(fd==-1){ /* process exited maybe */ + if(pids && w_flag) printf("WARNING: process %d could not be found.",pid); + return; + } + fstat(fd, &statbuf); + if(uids){ /* check the EUID */ + i=uid_count; + while(i--) if(uids[i]==statbuf.st_uid) break; + if(i==-1) goto closure; + } + read(fd,buf,128); + buf[127] = '\0'; + tmp = strrchr(buf, ')'); + *tmp++ = '\0'; + i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */ + tty = atoi(tmp); + if(ttys){ + i=tty_count; + while(i--) if(ttys[i]==tty) break; + if(i==-1) goto closure; + } + tmp = strchr(buf, '(') + 1; + if(cmds){ + i=cmd_count; + /* fast comparison trick -- useful? */ + while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break; + if(i==-1) goto closure; + } + /* This is where we kill/nice something. */ +/* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n", + pid, statbuf.st_uid, tty>>8, tty&0xf, tmp + ); +*/ + hurt_proc(tty, statbuf.st_uid, pid, tmp); +closure: + close(fd); /* kill/nice _first_ to avoid PID reuse */ +} + + +/***** debug function */ +#if 0 +static void show_lists(void){ + int i; + + fprintf(stderr, "%d TTY: ", tty_count); + if(ttys){ + i=tty_count; + while(i--){ + fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n'); + } + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d UID: ", uid_count); + if(uids){ + i=uid_count; + while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d PID: ", pid_count); + if(pids){ + i=pid_count; + while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d CMD: ", cmd_count); + if(cmds){ + i=cmd_count; + while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); +} +#endif + + +/***** iterate over all PIDs */ +static void iterate(void){ + int pid; + DIR *d; + struct dirent *de; + if(pids){ + pid = pid_count; + while(pid--) check_proc(pids[pid]); + return; + } +#if 0 + /* could setuid() and kill -1 to have the kernel wipe out a user */ + if(!ttys && !cmds && !pids && !i_flag){ + } +#endif + d = opendir("/proc"); + if(!d){ + perror("/proc"); + exit(1); + } + while(( de = readdir(d) )){ + if(de->d_name[0] > '9') continue; + if(de->d_name[0] < '1') continue; + pid = atoi(de->d_name); + if(pid) check_proc(pid); + } + closedir (d); +} + +/***** kill help */ +static void kill_usage(void) NORETURN; +static void kill_usage(void){ + fprintf(stderr, + "Usage:\n" + " kill pid ... Send SIGTERM to every process listed.\n" + " kill signal pid ... Send a signal to every process listed.\n" + " kill -s signal pid ... Send a signal to every process listed.\n" + " kill -l List all signal names.\n" + " kill -L List all signal names in a nice table.\n" + " kill -l signal Convert between signal numbers and names.\n" + ); + exit(1); +} + +/***** kill */ +static void kill_main(int argc, const char *restrict const *restrict argv) NORETURN; +static void kill_main(int argc, const char *restrict const *restrict argv){ + const char *sigptr; + int signo = SIGTERM; + int exitvalue = 0; + if(argc<2) kill_usage(); + if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){ + display_kill_version(); + exit(0); + } + if(argv[1][0]!='-'){ + argv++; + argc--; + goto no_more_args; + } + + /* The -l option prints out signal names. */ + if(argv[1][1]=='l' && argv[1][2]=='\0'){ + if(argc==2){ + unix_print_signals(); + exit(0); + } + /* at this point, argc must be 3 or more */ + if(argc>128 || argv[2][0] == '-') kill_usage(); + exit(print_given_signals(argc-2, argv+2, 80)); + } + + /* The -L option prints out signal names in a nice table. */ + if(argv[1][1]=='L' && argv[1][2]=='\0'){ + if(argc==2){ + pretty_print_signals(); + exit(0); + } + kill_usage(); + } + if(argv[1][1]=='-' && argv[1][2]=='\0'){ + argv+=2; + argc-=2; + goto no_more_args; + } + if(argv[1][1]=='-') kill_usage(); /* likely --help */ + // FIXME: "kill -sWINCH $$" not handled + if(argv[1][2]=='\0' && (argv[1][1]=='s' || argv[1][1]=='n')){ + sigptr = argv[2]; + argv+=3; + argc-=3; + }else{ + sigptr = argv[1]+1; + argv+=2; + argc-=2; + } + signo = signal_name_to_number(sigptr); + if(signo<0){ + fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr); + kill_usage(); + } +no_more_args: + if(!argc) kill_usage(); /* nothing to kill? */ + while(argc--){ + long pid; + char *endp; + pid = strtol(argv[argc],&endp,10); + if(!*endp){ + if(!kill((pid_t)pid,signo)) continue; + // The UNIX standard contradicts itself. If at least one process + // is matched for each PID (as if processes could share PID!) and + // "the specified signal was successfully processed" (the systcall + // returned zero?) for at least one of those processes, then we must + // exit with zero. Note that an error might have also occured. + // The standard says we return non-zero if an error occurs. Thus if + // killing two processes gives 0 for one and EPERM for the other, + // we are required to return both zero and non-zero. Quantum kill??? + exitvalue = 1; + continue; + } + fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]); + kill_usage(); + } + exit(exitvalue); +} + +/***** skill/snice help */ +static void skillsnice_usage(void) NORETURN; +static void skillsnice_usage(void){ + if(program==PROG_SKILL){ + fprintf(stderr, + "Usage: skill [signal to send] [options] process selection criteria\n" + "Example: skill -KILL -v pts/*\n" + "\n" + "The default signal is TERM. Use -l or -L to list available signals.\n" + "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n" + "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n" + ); + }else{ + fprintf(stderr, + "Usage: snice [new priority] [options] process selection criteria\n" + "Example: snice netscape crack +7\n" + "\n" + "The default priority is +4. (snice +4 ...)\n" + "Priority numbers range from +20 (slowest) to -20 (fastest).\n" + "Negative priority numbers are restricted to administrative users.\n" + ); + } + fprintf(stderr, + "\n" + "General options:\n" + "-f fast mode This is not currently useful.\n" + "-i interactive use You will be asked to approve each action.\n" + "-v verbose output Display information about selected processes.\n" + "-w warnings enabled This is not currently useful.\n" + "-n no action This only displays the process ID.\n" + "\n" + "Selection criteria can be: terminal, user, pid, command.\n" + "The options below may be used to ensure correct interpretation.\n" + "-t The next argument is a terminal (tty or pty).\n" + "-u The next argument is a username.\n" + "-p The next argument is a process ID number.\n" + "-c The next argument is a command name.\n" + ); + exit(1); +} + +#if 0 +static void _skillsnice_usage(int line){ + fprintf(stderr,"Something at line %d.\n", line); + skillsnice_usage(); +} +#define skillsnice_usage() _skillsnice_usage(__LINE__) +#endif + +#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL) + +/***** common skill/snice argument parsing code */ +#define NO_PRI_VAL ((int)0xdeafbeef) +static void skillsnice_parse(int argc, const char *restrict const *restrict argv){ + int signo = -1; + int prino = NO_PRI_VAL; + int force = 0; + int num_found = 0; + const char *restrict argptr; + if(argc<2) skillsnice_usage(); + if(argc==2 && argv[1][0]=='-'){ + if(!strcmp(argv[1],"-L")){ + pretty_print_signals(); + exit(0); + } + if(!strcmp(argv[1],"-l")){ + unix_print_signals(); + exit(0); + } + if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){ + display_kill_version(); + exit(0); + } + skillsnice_usage(); + } + NEXTARG; + /* Time for serious parsing. What does "skill -int 123 456" mean? */ + while(argc){ + if(force && !num_found){ /* if forced, _must_ find something */ + fprintf(stderr,"ERROR: -%c used with bad data.\n", force); + skillsnice_usage(); + } + force = 0; + if(program==PROG_SKILL && signo<0 && *argptr=='-'){ + signo = signal_name_to_number(argptr+1); + if(signo>=0){ /* found a signal */ + if(!NEXTARG) break; + continue; + } + } + if(program==PROG_SNICE && prino==NO_PRI_VAL + && (*argptr=='+' || *argptr=='-') && argptr[1]){ + long val; + char *endp; + val = strtol(argptr,&endp,10); + if(!*endp && val<=999 && val>=-999){ + prino=val; + if(!NEXTARG) break; + continue; + } + } + /* If '-' found, collect any flags. (but lone "-" is a tty) */ + if(*argptr=='-' && argptr[1]){ + argptr++; + do{ + switch(( force = *argptr++ )){ + default: skillsnice_usage(); + case 't': + case 'u': + case 'p': + case 'c': + if(!*argptr){ /* nothing left here, *argptr is '\0' */ + if(!NEXTARG){ + fprintf(stderr,"ERROR: -%c with nothing after it.\n", force); + skillsnice_usage(); + } + } + goto selection_collection; + case 'f': f_flag++; break; + case 'i': i_flag++; break; + case 'v': v_flag++; break; + case 'w': w_flag++; break; + case 'n': n_flag++; break; + case 0: + NEXTARG; + /* + * If no more arguments, all the "if(argc)..." tests will fail + * and the big loop will exit. + */ + } /* END OF SWITCH */ + }while(force); + } /* END OF IF */ +selection_collection: + num_found = 0; /* we should find at least one thing */ + switch(force){ /* fall through each data type */ + default: skillsnice_usage(); + case 0: /* not forced */ + case 't': + if(argc){ + struct stat sbuf; + char path[32]; + if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */ + snprintf(path,32,"/dev/%s",argptr); + if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){ + num_found++; + ENLIST(tty,sbuf.st_rdev); + if(!NEXTARG) break; + }else if(!(argptr[1])){ /* if only 1 character */ + switch(*argptr){ + default: + if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */ + case '-': + case '?': + num_found++; + ENLIST(tty,0); + if(!NEXTARG) break; + } + } + } + if(force) continue; + case 'u': + if(argc){ + struct passwd *passwd_data; + passwd_data = getpwnam(argptr); + if(passwd_data){ + num_found++; + ENLIST(uid,passwd_data->pw_uid); + if(!NEXTARG) break; + } + } + if(force) continue; + case 'p': + if(argc && *argptr>='0' && *argptr<='9'){ + char *endp; + int num; + num = strtol(argptr, &endp, 0); + if(*endp == '\0'){ + num_found++; + ENLIST(pid,num); + if(!NEXTARG) break; + } + } + if(force) continue; + if(num_found) continue; /* could still be an option */ + case 'c': + if(argc){ + num_found++; + ENLIST(cmd,argptr); + if(!NEXTARG) break; + } + } /* END OF SWITCH */ + } /* END OF WHILE */ + /* No more arguments to process. Must sanity check. */ + if(!tty_count && !uid_count && !cmd_count && !pid_count){ + fprintf(stderr,"ERROR: no process selection criteria.\n"); + skillsnice_usage(); + } + if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){ + fprintf(stderr,"ERROR: general flags may not be repeated.\n"); + skillsnice_usage(); + } + if(i_flag && (v_flag|f_flag|n_flag)){ + fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n"); + skillsnice_usage(); + } + if(v_flag && (i_flag|f_flag)){ + fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n"); + skillsnice_usage(); + } + /* OK, set up defaults */ + if(prino==NO_PRI_VAL) prino=4; + if(signo<0) signo=SIGTERM; + if(n_flag){ + program=PROG_SKILL; + signo=0; /* harmless */ + } + if(program==PROG_SKILL) sig_or_pri = signo; + else sig_or_pri = prino; +} + +/***** main body */ +int main(int argc, const char *argv[]){ + const char *tmpstr; + my_pid = getpid(); + saved_argc = argc; + if(!argc){ + fprintf(stderr,"ERROR: could not determine own name.\n"); + exit(1); + } + tmpstr=strrchr(*argv,'/'); + if(tmpstr) tmpstr++; + if(!tmpstr) tmpstr=*argv; + program = PROG_GARBAGE; + if(*tmpstr=='s'){ + setpriority(PRIO_PROCESS,my_pid,-20); + if(!strcmp(tmpstr,"snice")) program = PROG_SNICE; + if(!strcmp(tmpstr,"skill")) program = PROG_SKILL; + }else{ + if(!strcmp(tmpstr,"kill")) program = PROG_KILL; + } + switch(program){ + case PROG_SNICE: + case PROG_SKILL: + skillsnice_parse(argc, argv); +/* show_lists(); */ + iterate(); /* this is it, go get them */ + break; + case PROG_KILL: + kill_main(argc, argv); + break; + default: + fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr); + } + return 0; +} + + diff --git a/slabtop.1 b/slabtop.1 new file mode 100644 index 0000000..11f277e --- /dev/null +++ b/slabtop.1 @@ -0,0 +1,124 @@ +.\" slabtop.1 - manpage for the slabtop(1) utility, part of procps +.\" +.\" Copyright (C) 2003 Chris Rivera +.\" Licensed under the terms of the GNU Library General Public License, v2 +.TH SLABTOP 1 "13 Sep 2003" "Linux" "Linux User's Manual" +.SH NAME +slabtop \- display kernel slab cache information in real time + +.SH SYNOPSIS +.BI "slabtop [ " options " ] " + +.SH DESCRIPTION +.BR slabtop (1) +displays detailed kernel slab cache information in real time. It displays a +listing of the top caches sorted by one of the listed sort criteria. It also +displays a statistics header filled with slab layer information. + +.SH OPTIONS +Normal invocation of +.BR slabtop (1) +does not require any options. The behavior, however, can be fine-tuned by +specifying one or more of the following flags: +.TP +.B \-\^\-delay=n, \-d n +Refresh the display every n seconds. By default, +.BR slabtop (1) +refreshes the display every three seconds. To exit the program, hit +.BR q. +.TP +.B \-\^\-sort=S, \-s S +Sort by S, where S is one of the sort criteria. +.TP +.B \-\^\-once, \-o +Display the output once and then exit. +.TP +.B \-\^\-version, \-V +Display version information and exit. +.TP +.B \-\^\-help +Display usage information and exit. + +.SH SORT CRITERIA +The following are valid sort criteria used to sort the individual slab caches +and thereby determine what are the "top" slab caches to display. The default +sort criteria is to sort by the number of objects ("o"). + +The sort criteria can also be changed while slabtop is running by pressing +the associated character. +.TP +.BR a: +sort by number of active objects +.TP +.BR b: +sort by objects per slab +.TP +.BR c: +sort by cache size +.TP +.BR l: +sort by number of slabs +.TP +.BR v +sort by number of active slabs +.TP +.BR n: +sort by name +.TP +.BR o: +sort by number of objects +.TP +.BR p: +sort by pages per slab +.TP +.BR s: +sort by object size +.TP +.BR u: +sort by cache utilization + +.SH COMMANDS +.BR slabtop (1) +accepts keyboard commands from the user during use. The following are +supported. In the case of letters, both cases are accepted. + +Each of the valid sort characters are also accepted, to change the sort +routine. See the section +.IR "SORT CRITERIA" . + +.TP +.BR +Refresh the screen. +.TP +.BR Q +Quit the program. + +.SH FILES +.IR /proc/slabinfo " \-\- slab information" + +.SH "SEE ALSO" +.BR free (1), +.BR ps (1), +.BR top (1), +.BR vmstat (8) + +.SH NOTES +Currently, +.BR slabtop (1) +requires a 2.4 or later kernel (specifically, a version 1.1 or later +.IR /proc/slabinfo ). +Kernel 2.2 should be supported in the future. + +The slabtop statistic header is tracking how many bytes of slabs are being used +and it not a measure of physical memory. The 'Slab' field in the /proc/meminfo +file is tracking information about used slab physical memory. + +.SH AUTHORS +Written by Chris Rivera and Robert Love. + +.BR slabtop (1) +was inspired by Martin Bligh's perl script, +.BR vmtop . +The procps package is maintained by Albert Cahalan . + +Please send bug reports to . diff --git a/slabtop.c b/slabtop.c new file mode 100644 index 0000000..36025e2 --- /dev/null +++ b/slabtop.c @@ -0,0 +1,397 @@ +/* + * slabtop.c - utility to display kernel slab information. + * + * Chris Rivera + * Robert Love + * + * This program is licensed under the GNU Library General Public License, v2 + * + * Copyright (C) 2003 Chris Rivera + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "proc/slab.h" +#include "proc/version.h" + +#define DEF_SORT_FUNC sort_nr_objs +#define SLAB_STAT_ZERO { nr_objs: 0 } + +static unsigned short cols, rows; +static struct termios saved_tty; +static long delay = 3; +static int (*sort_func)(const struct slab_info *, const struct slab_info *); + +static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b) +{ + struct slab_info sorted_list; + struct slab_info *curr = &sorted_list; + + while ((a != NULL) && (b != NULL)) { + if (sort_func(a, b)) { + curr->next = a; + curr = a; + a = a->next; + } else { + curr->next = b; + curr = b; + b = b->next; + } + } + + curr->next = (a == NULL) ? b : a; + return sorted_list.next; +} + +/* + * slabsort - merge sort the slab_info linked list based on sort_func + */ +static struct slab_info *slabsort(struct slab_info *list) +{ + struct slab_info *a, *b; + + if ((list == NULL) || (list->next == NULL)) + return list; + + a = list; + b = list->next; + + while ((b != NULL) && (b->next != NULL)) { + list = list->next; + b = b->next->next; + } + + b = list->next; + list->next = NULL; + + return merge_objs(slabsort(a), slabsort(b)); +} + +/* + * Sort Routines. Each of these should be associated with a command-line + * search option. The functions should fit the prototype: + * + * int sort_foo(const struct slab_info *a, const struct slab_info *b) + * + * They return one if the first parameter is larger than the second + * Otherwise, they return zero. + */ + +static int sort_name(const struct slab_info *a, const struct slab_info *b) +{ + return (strcmp(a->name, b->name) < 0) ? 1 : 0; +} + +static int sort_nr_objs(const struct slab_info *a, const struct slab_info *b) +{ + return (a->nr_objs > b->nr_objs); +} + +static int sort_nr_active_objs(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->nr_active_objs > b->nr_active_objs); +} + +static int sort_obj_size(const struct slab_info *a, const struct slab_info *b) +{ + return (a->obj_size > b->obj_size); +} + +static int sort_objs_per_slab(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->objs_per_slab > b->objs_per_slab); +} + +static int sort_pages_per_slab(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->pages_per_slab > b->pages_per_slab); +} + +static int sort_nr_slabs(const struct slab_info *a, const struct slab_info *b) +{ + return (a->nr_slabs > b->nr_slabs); +} + +static int sort_nr_active_slabs(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->nr_active_slabs > b->nr_active_slabs); +} + + +static int sort_use(const struct slab_info *a, const struct slab_info *b) +{ + return (a->use > b->use); +} + +static int sort_cache_size(const struct slab_info *a, const struct slab_info *b) +{ + return (a->cache_size > b->cache_size); +} + +/* + * term_size - set the globals 'cols' and 'rows' to the current terminal size + */ +static void term_size(int unused) +{ + struct winsize ws; + (void) unused; + + if ((ioctl(1, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) { + cols = ws.ws_col; + rows = ws.ws_row; + } else { + cols = 80; + rows = 24; + } +} + +static void sigint_handler(int unused) +{ + (void) unused; + + delay = 0; +} + +static void usage(const char *cmd) +{ + fprintf(stderr, "usage: %s [options]\n\n", cmd); + fprintf(stderr, "options:\n"); + fprintf(stderr, " --delay=n, -d n " + "delay n seconds between updates\n"); + fprintf(stderr, " --once, -o " + "only display once, then exit\n"); + fprintf(stderr, " --sort=S, -s S " + "specify sort criteria S (see below)\n"); + fprintf(stderr, " --version, -V " + "display version information and exit\n"); + fprintf(stderr, " --help display this help and exit\n\n"); + fprintf(stderr, "The following are valid sort criteria:\n"); + fprintf(stderr, " a: sort by number of active objects\n"); + fprintf(stderr, " b: sort by objects per slab\n"); + fprintf(stderr, " c: sort by cache size\n"); + fprintf(stderr, " l: sort by number of slabs\n"); + fprintf(stderr, " v: sort by number of active slabs\n"); + fprintf(stderr, " n: sort by name\n"); + fprintf(stderr, " o: sort by number of objects\n"); + fprintf(stderr, " p: sort by pages per slab\n"); + fprintf(stderr, " s: sort by object size\n"); + fprintf(stderr, " u: sort by cache utilization\n"); +} + +/* + * set_sort_func - return the slab_sort_func that matches the given key. + * On unrecognizable key, DEF_SORT_FUNC is returned. + */ +static void * set_sort_func(char key) +{ + switch (key) { + case 'n': + return sort_name; + case 'o': + return sort_nr_objs; + case 'a': + return sort_nr_active_objs; + case 's': + return sort_obj_size; + case 'b': + return sort_objs_per_slab; + case 'p': + return sort_pages_per_slab; + case 'l': + return sort_nr_slabs; + case 'v': + return sort_nr_active_slabs; + case 'c': + return sort_cache_size; + case 'u': + return sort_use; + default: + return DEF_SORT_FUNC; + } +} + +static void parse_input(char c) +{ + c = toupper(c); + switch(c) { + case 'A': + sort_func = sort_nr_active_objs; + break; + case 'B': + sort_func = sort_objs_per_slab; + break; + case 'C': + sort_func = sort_cache_size; + break; + case 'L': + sort_func = sort_nr_slabs; + break; + case 'V': + sort_func = sort_nr_active_slabs; + break; + case 'N': + sort_func = sort_name; + break; + case 'O': + sort_func = sort_nr_objs; + break; + case 'P': + sort_func = sort_pages_per_slab; + break; + case 'S': + sort_func = sort_obj_size; + break; + case 'U': + sort_func = sort_use; + break; + case 'Q': + delay = 0; + break; + } +} + +int main(int argc, char *argv[]) +{ + int o; + unsigned short old_rows; + struct slab_info *slab_list = NULL; + + struct option longopts[] = { + { "delay", 1, NULL, 'd' }, + { "sort", 1, NULL, 's' }, + { "once", 0, NULL, 'o' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + sort_func = DEF_SORT_FUNC; + + while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) { + int ret = 1; + + switch (o) { + case 'd': + errno = 0; + delay = strtol(optarg, NULL, 10); + if (errno) { + perror("strtoul"); + return 1; + } + if (delay < 0) { + fprintf(stderr, "error: can't have a "\ + "negative delay\n"); + exit(1); + } + break; + case 's': + sort_func = set_sort_func(optarg[0]); + break; + case 'o': + delay = 0; + break; + case 'V': + display_version(); + return 0; + case 'h': + ret = 0; + default: + usage(argv[0]); + return ret; + } + } + + if (tcgetattr(0, &saved_tty) == -1) + perror("tcgetattr"); + + initscr(); + term_size(0); + old_rows = rows; + resizeterm(rows, cols); + signal(SIGWINCH, term_size); + signal(SIGINT, sigint_handler); + + do { + struct slab_info *curr; + struct slab_stat stats = SLAB_STAT_ZERO; + struct timeval tv; + fd_set readfds; + char c; + int i; + + if (get_slabinfo(&slab_list, &stats)) + break; + + if (old_rows != rows) { + resizeterm(rows, cols); + old_rows = rows; + } + + move(0,0); + printw( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n" + " Minimum / Average / Maximum Object : %.2fK / %.2fK / %.2fK\n\n", + stats.nr_active_objs, stats.nr_objs, 100.0 * stats.nr_active_objs / stats.nr_objs, + stats.nr_active_slabs, stats.nr_slabs, 100.0 * stats.nr_active_slabs / stats.nr_slabs, + stats.nr_active_caches, stats.nr_caches, 100.0 * stats.nr_active_caches / stats.nr_caches, + stats.active_size / 1024.0, stats.total_size / 1024.0, 100.0 * stats.active_size / stats.total_size, + stats.min_obj_size / 1024.0, stats.avg_obj_size / 1024.0, stats.max_obj_size / 1024.0 + ); + + slab_list = slabsort(slab_list); + + attron(A_REVERSE); + printw( "%6s %6s %4s %8s %6s %8s %10s %-23s\n", + "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS", + "OBJ/SLAB", "CACHE SIZE", "NAME"); + attroff(A_REVERSE); + + curr = slab_list; + for (i = 0; i < rows - 8 && curr->next; i++) { + printw("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", + curr->nr_objs, curr->nr_active_objs, curr->use, + curr->obj_size / 1024.0, curr->nr_slabs, + curr->objs_per_slab, (unsigned)(curr->cache_size / 1024), + curr->name); + curr = curr->next; + } + + refresh(); + put_slabinfo(slab_list); + + FD_ZERO(&readfds); + FD_SET(0, &readfds); + tv.tv_sec = delay; + tv.tv_usec = 0; + if (select(1, &readfds, NULL, NULL, &tv) > 0) { + if (read(0, &c, 1) != 1) + break; + parse_input(c); + } + } while (delay); + + tcsetattr(0, TCSAFLUSH, &saved_tty); + free_slabinfo(slab_list); + endwin(); + return 0; +} diff --git a/snice.1 b/snice.1 new file mode 100644 index 0000000..1595a80 --- /dev/null +++ b/snice.1 @@ -0,0 +1 @@ +.so man1/skill.1 diff --git a/sysctl.8 b/sysctl.8 new file mode 100644 index 0000000..e26c4fb --- /dev/null +++ b/sysctl.8 @@ -0,0 +1,85 @@ +.\" Copyright 1999, George Staikos (staikos@0wned.org) +.\" This file may be used subject to the terms and conditions of the +.\" GNU General Public License Version 2, or any later version +.\" at your option, as published by the Free Software Foundation. +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details." +.TH SYSCTL 8 "21 Sep 1999" "" "" +.SH NAME +sysctl \- configure kernel parameters at runtime +.SH SYNOPSIS +.B "sysctl [-n] [-e] variable ..." +.br +.B "sysctl [-n] [-e] [-q] -w variable=value ..." +.br +.B "sysctl [-n] [-e] [-q] -p " +.br +.B "sysctl [-n] [-e] -a" +.br +.B "sysctl [-n] [-e] -A" +.SH DESCRIPTION +.B sysctl +is used to modify kernel parameters at runtime. The parameters available +are those listed under /proc/sys/. Procfs is required for +.B sysctl(8) +support in Linux. You can use +.B sysctl(8) +to both read and write sysctl data. +.SH PARAMETERS +.TP +.B "variable" +The name of a key to read from. An example is kernel.ostype. The '/' +separator is also accepted in place of a '.'. +.TP +.B "variable=value" +To set a key, use the form variable=value, where variable is the key and +value is the value to set it to. If the value contains quotes or characters +which are parsed by the shell, you may need to enclose the value in double +quotes. This requires the -w parameter to use. +.TP +.B "-n" +Use this option to disable printing of the key name when printing values. +.TP +.B "-e" +Use this option to ignore errors about unknown keys. +.TP +.B "-N" +Use this option to only print the names. It may be useful with shells that +have programmable completion. +.TP +.B "-q" +Use this option to not display the values set to stdout. +.TP +.B "-w" +Use this option when you want to change a sysctl setting. +.TP +.B "-p" +Load in sysctl settings from the file specified or /etc/sysctl.conf if none given. +Specifying \- as filename means reading data from standard input. +.TP +.B "-a" +Display all values currently available. +.TP +.B "-A" +Display all values currently available in table form. +.SH EXAMPLES +.TP +/sbin/sysctl -a +.TP +/sbin/sysctl -n kernel.hostname +.TP +/sbin/sysctl -w kernel.domainname="example.com" +.TP +/sbin/sysctl -p /etc/sysctl.conf +.SH FILES +.I /proc/sys +.I /etc/sysctl.conf +.SH SEE ALSO +.BR sysctl.conf (5) +.SH BUGS +The -A parameter behaves just as -a does. +.SH AUTHOR +George Staikos, + diff --git a/sysctl.c b/sysctl.c new file mode 100644 index 0000000..1470df9 --- /dev/null +++ b/sysctl.c @@ -0,0 +1,516 @@ + +/* + * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters + * + * + * "Copyright 1999 George Staikos + * This file may be used subject to the terms and conditions of the + * GNU General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details." + * + * Changelog: + * v1.01: + * - added -p to preload values from a file + * Horms: + * - added -q to be quiet when modifying values + * + * Changes by Albert Cahalan, 2002. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/procps.h" +#include "proc/version.h" + + +// Proof that C++ causes brain damage: +typedef int bool; +static bool true = 1; +static bool false = 0; + +/* + * Globals... + */ + +static const char PROC_PATH[] = "/proc/sys/"; +static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf"; +static bool NameOnly; +static bool PrintName; +static bool PrintNewline; +static bool IgnoreError; +static bool Quiet; + +/* error messages */ +static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter \"%s\"\n"; +static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting \"%s\"\n"; +static const char ERR_NO_EQUALS[] = "error: \"%s\" must be of the form name=value\n"; +static const char ERR_INVALID_KEY[] = "error: \"%s\" is an unknown key\n"; +static const char ERR_UNKNOWN_WRITING[] = "error: \"%s\" setting key \"%s\"\n"; +static const char ERR_UNKNOWN_READING[] = "error: \"%s\" reading key \"%s\"\n"; +static const char ERR_PERMISSION_DENIED[] = "error: permission denied on key '%s'\n"; +static const char ERR_OPENING_DIR[] = "error: unable to open directory \"%s\"\n"; +static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n"; +static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n"; + + +static void slashdot(char *restrict p, char old, char new){ + p = strpbrk(p,"/."); + if(!p) return; /* nothing -- can't be, but oh well */ + if(*p==new) return; /* already in desired format */ + while(p){ + char c = *p; + if(c==old) *p=new; + if(c==new) *p=old; + p = strpbrk(p+1,"/."); + } +} + + + +/* + * Display the usage format + * + */ +static int Usage(const char *restrict const name) { + printf("usage: %s [-n] [-e] variable ... \n" + " %s [-n] [-e] [-q] -w variable=value ... \n" + " %s [-n] [-e] -a \n" + " %s [-n] [-e] [-q] -p (default /etc/sysctl.conf) \n" + " %s [-n] [-e] -A\n", name, name, name, name, name); + return -1; +} + + +/* + * Strip the leading and trailing spaces from a string + * + */ +static char *StripLeadingAndTrailingSpaces(char *oneline) { + char *t; + + if (!oneline || !*oneline) + return oneline; + + t = oneline; + t += strlen(oneline)-1; + + while ((*t==' ' || *t=='\t' || *t=='\n' || *t=='\r') && t!=oneline) + *t-- = 0; + + t = oneline; + + while ((*t==' ' || *t=='\t') && *t!=0) + t++; + + return t; +} + +static int DisplayAll(const char *restrict const path); + +/* + * Read a sysctl setting + * + */ +static int ReadSetting(const char *restrict const name) { + int rc = 0; + char *restrict tmpname; + char *restrict outname; + char inbuf[1025]; + FILE *restrict fp; + + if (!name || !*name) { + fprintf(stderr, ERR_INVALID_KEY, name); + return -1; + } + + /* used to open the file */ + tmpname = malloc(strlen(name)+strlen(PROC_PATH)+2); + strcpy(tmpname, PROC_PATH); + strcat(tmpname, name); + slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */ + + /* used to display the output */ + outname = strdup(name); + slashdot(outname,'/','.'); /* change / to . */ + + fp = fopen(tmpname, "r"); + + if (!fp) { + switch(errno) { + case ENOENT: + if (!IgnoreError) { + fprintf(stderr, ERR_INVALID_KEY, outname); + rc = -1; + } + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; + default: + fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname); + rc = -1; + break; + } + } else { + if(fgets(inbuf, sizeof inbuf - 1, fp)) { + // this loop is required, see + // /sbin/sysctl -a | egrep -6 dev.cdrom.info + do { + if (NameOnly) { + fprintf(stdout, "%s\n", outname); + } else { + /* already has the \n in it */ + if (PrintName) { + fprintf(stdout, "%s = %s", outname, inbuf); + } else { + if (!PrintNewline) { + char *nlptr = strchr(inbuf,'\n'); + if(nlptr) *nlptr='\0'; + } + fprintf(stdout, "%s", inbuf); + } + } + } while(fgets(inbuf, sizeof inbuf - 1, fp)); + } else { + switch(errno) { + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; + case EISDIR:{ + size_t len; + len = strlen(tmpname); + tmpname[len] = '/'; + tmpname[len+1] = '\0'; + rc = DisplayAll(tmpname); + break; + } + default: + fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname); + rc = -1; + break; + } + } + fclose(fp); + } + + free(tmpname); + free(outname); + return rc; +} + + + +/* + * Display all the sysctl settings + * + */ +static int DisplayAll(const char *restrict const path) { + int rc = 0; + int rc2; + DIR *restrict dp; + struct dirent *restrict de; + struct stat ts; + + dp = opendir(path); + + if (!dp) { + fprintf(stderr, ERR_OPENING_DIR, path); + rc = -1; + } else { + readdir(dp); // skip . + readdir(dp); // skip .. + while (( de = readdir(dp) )) { + char *restrict tmpdir; + tmpdir = (char *restrict)malloc(strlen(path)+strlen(de->d_name)+2); + sprintf(tmpdir, "%s%s", path, de->d_name); + rc2 = stat(tmpdir, &ts); + if (rc2 != 0) { + perror(tmpdir); + } else { + if (S_ISDIR(ts.st_mode)) { + strcat(tmpdir, "/"); + DisplayAll(tmpdir); + } else { + rc |= ReadSetting(tmpdir+strlen(PROC_PATH)); + } + } + free(tmpdir); + } + closedir(dp); + } + return rc; +} + + +/* + * Write a sysctl setting + * + */ +static int WriteSetting(const char *setting) { + int rc = 0; + const char *name = setting; + const char *value; + const char *equals; + char *tmpname; + FILE *fp; + char *outname; + + if (!name) { /* probably don't want to display this err */ + return 0; + } /* end if */ + + equals = index(setting, '='); + + if (!equals) { + fprintf(stderr, ERR_NO_EQUALS, setting); + return -1; + } + + value = equals + 1; /* point to the value in name=value */ + + if (!*name || !*value || name == equals) { + fprintf(stderr, ERR_MALFORMED_SETTING, setting); + return -2; + } + + /* used to open the file */ + tmpname = malloc(equals-name+1+strlen(PROC_PATH)); + strcpy(tmpname, PROC_PATH); + strncat(tmpname, name, (int)(equals-name)); + tmpname[equals-name+strlen(PROC_PATH)] = 0; + slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */ + + /* used to display the output */ + outname = malloc(equals-name+1); + strncpy(outname, name, (int)(equals-name)); + outname[equals-name] = 0; + slashdot(outname,'/','.'); /* change / to . */ + + fp = fopen(tmpname, "w"); + + if (!fp) { + switch(errno) { + case ENOENT: + if (!IgnoreError) { + fprintf(stderr, ERR_INVALID_KEY, outname); + rc = -1; + } + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; + default: + fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname); + rc = -1; + break; + } + } else { + rc = fprintf(fp, "%s\n", value); + if (rc < 0) { + fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname); + fclose(fp); + } else { + rc=fclose(fp); + if (rc != 0) + fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname); + } + if (rc==0 && !Quiet) { + if (NameOnly) { + fprintf(stdout, "%s\n", outname); + } else { + if (PrintName) { + fprintf(stdout, "%s = %s\n", outname, value); + } else { + if (PrintNewline) + fprintf(stdout, "%s\n", value); + else + fprintf(stdout, "%s", value); + } + } + } + } + + free(tmpname); + free(outname); + return rc; +} + + + +/* + * Preload the sysctl's from the conf file + * - we parse the file and then reform it (strip out whitespace) + * + */ +static int Preload(const char *restrict const filename) { + char oneline[256]; + char buffer[256]; + FILE *fp; + char *t; + int n = 0; + int rc = 0; + char *name, *value; + + fp = (filename[0]=='-' && !filename[1]) + ? stdin + : fopen(filename, "r") + ; + + if (!fp) { + fprintf(stderr, ERR_PRELOAD_FILE, filename); + return -1; + } + + while (fgets(oneline, sizeof oneline, fp)) { + n++; + t = StripLeadingAndTrailingSpaces(oneline); + + if (strlen(t) < 2) + continue; + + if (*t == '#' || *t == ';') + continue; + + name = strtok(t, "="); + if (!name || !*name) { + fprintf(stderr, WARN_BAD_LINE, filename, n); + continue; + } + + StripLeadingAndTrailingSpaces(name); + + value = strtok(NULL, "\n\r"); + if (!value || !*value) { + fprintf(stderr, WARN_BAD_LINE, filename, n); + continue; + } + + while ((*value == ' ' || *value == '\t') && *value != 0) + value++; + + // should NameOnly affect this? + sprintf(buffer, "%s=%s", name, value); + rc |= WriteSetting(buffer); + } + + fclose(fp); + return rc; +} + + + +/* + * Main... + * + */ +int main(int argc, char *argv[]) { + const char *me = (const char *)basename(argv[0]); + bool SwitchesAllowed = true; + bool WriteMode = false; + int ReturnCode = 0; + const char *preloadfile = DEFAULT_PRELOAD; + + PrintName = true; + PrintNewline = true; + IgnoreError = false; + Quiet = false; + + if (argc < 2) { + return Usage(me); + } + + argv++; + + for (; argv && *argv && **argv; argv++) { + if (SwitchesAllowed && **argv == '-') { /* we have a switch */ + if ((*argv)[1] && (*argv)[2]){ // don't yet handle "sysctl -ew" + if (!strcmp("--help",*argv)) { + Usage(me); + exit(0); + } + if (!strcmp("--version",*argv)) { + fprintf(stdout, "sysctl (%s)\n",procps_version); + exit(0); + } + fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv); + return Usage(me); + } + switch((*argv)[1]) { + case 'b': + /* This is "binary" format, which means more for BSD. */ + PrintNewline = false; + /* FALL THROUGH */ + case 'n': + PrintName = false; + break; + case 'e': + // For FreeBSD, -e means a "%s=%s\n" format. ("%s: %s\n" default) + // We (and NetBSD) use "%s = %s\n" always, and -e to ignore errors. + IgnoreError = true; + break; + case 'N': + NameOnly = true; + break; + case 'w': + SwitchesAllowed = false; + WriteMode = true; + break; + case 'f': // the NetBSD way + case 'p': + argv++; + if (argv && *argv && **argv) { + preloadfile = *argv; + } + return Preload(preloadfile); + case 'q': + Quiet = true; + break; + case 'o': // BSD: binary values too, 1st 16 bytes in hex + case 'x': // BSD: binary values too, whole thing in hex + /* does nothing */ ; + break; + case 'a': // string and integer values (for Linux, all of them) + case 'A': // same as -a -o + case 'X': // same as -a -x + SwitchesAllowed = false; + return DisplayAll(PROC_PATH); + case 'V': + fprintf(stdout, "sysctl (%s)\n",procps_version); + exit(0); + case 'd': // BSD: print description ("vm.kvm_size: Size of KVM") + case 'h': // BSD: human-readable (did FreeBSD 5 make -e default?) + case '?': + return Usage(me); + default: + fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv); + return Usage(me); + } + } else { + if (NameOnly && Quiet) // nonsense + return Usage(me); + SwitchesAllowed = false; + if (WriteMode || index(*argv, '=')) + ReturnCode = WriteSetting(*argv); + else + ReturnCode = ReadSetting(*argv); + } + } + + return ReturnCode; +} + + diff --git a/sysctl.conf b/sysctl.conf new file mode 100644 index 0000000..720294f --- /dev/null +++ b/sysctl.conf @@ -0,0 +1,60 @@ +# /etc/sysctl.conf - Configuration file for setting system variables +# See sysctl.conf (5) for information. + +# you can have the CD-ROM close when you use it, and open +# when you are done. +#dev.cdrom.autoeject = 1 +#dev.cdrom.autoclose = 1 + +# protection from the SYN flood attack +net/ipv4/tcp_syncookies=1 + +# see the evil packets in your log files +net/ipv4/conf/all/log_martians=1 + +# makes you vulnerable or not :-) +net/ipv4/conf/all/accept_redirects=0 +net/ipv4/conf/all/accept_source_route=0 +net/ipv4/icmp_echo_ignore_broadcasts =1 + +# needed for routing, including masquerading or NAT +#net/ipv4/ip_forward=1 + +# sets the port range used for outgoing connections +#net.ipv4.ip_local_port_range = 32768 61000 + +# Broken routers and obsolete firewalls will corrupt the window scaling +# and ECN. Set these values to 0 to disable window scaling and ECN. +# This may, rarely, cause some performance loss when running high-speed +# TCP/IP over huge distances or running TCP/IP over connections with high +# packet loss and modern routers. This sure beats dropped connections. +#net.ipv4.tcp_default_win_scale = 0 +#net.ipv4.tcp_ecn = 0 + +# Swapping too much or not enough? Disks spinning up when you'd +# rather they didn't? Tweak these. +#vm.vfs_cache_pressure = 100 +#vm.laptop_mode = 0 +#vm.swappiness = 60 + +#kernel.printk_ratelimit_burst = 10 +#kernel.printk_ratelimit = 5 +#kernel.panic_on_oops = 0 + +# Reboot 600 seconds after a panic +#kernel.panic = 600 + +# enable SysRq key (note: console security issues) +#kernel.sysrq = 1 + +# Change name of core file to start with the command name +# so you get things like: emacs.core mozilla-bin.core X.core +#kernel.core_pattern = %e.core + +# NIS/YP domain (not always equal to DNS domain) +#kernel.domainname = example.com +#kernel.hostname = darkstar + +# This limits PID values to 4 digits, which allows tools like ps +# to save screen space. +kernel/pid_max=10000 diff --git a/sysctl.conf.5 b/sysctl.conf.5 new file mode 100644 index 0000000..0d8b073 --- /dev/null +++ b/sysctl.conf.5 @@ -0,0 +1,51 @@ +.\" Copyright 1999, George Staikos (staikos@0wned.org) +.\" This file may be used subject to the terms and conditions of the +.\" GNU General Public License Version 2, or any later version +.\" at your option, as published by the Free Software Foundation. +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details." +.TH SYSCTL.CONF 5 "21 Sep 1999" "" "" +.SH NAME +sysctl.conf \- sysctl(8) preload/configuration file +.SH DESCRIPTION +.I sysctl.conf +is a simple file containing sysctl values to be read in and set by sysctl(8). +The syntax is simply as follows: +.RS +.sp +.nf +.ne 7 +# comment +; comment + + token = value +.fi +.sp +.RE +.PP +Note that blank lines are ignored, and whitespace before and after a token or +value is ignored, although a value can contain whitespace within. Lines which +begin with a # or ; are considered comments and ignored. +.SH EXAMPLE +.RS +.sp +.nf +.ne 7 +# sysctl.conf sample +# + + kernel.domainname = example.com +; this one has a space which will be written to the sysctl! + kernel.modprobe = /sbin/mod probe + +.fi +.sp +.RE +.PP +.SH SEE ALSO +.BR sysctl(8) +.SH AUTHOR +George Staikos, + diff --git a/t b/t new file mode 100755 index 0000000..3842ee0 --- /dev/null +++ b/t @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with embedded spaces in arguments. +# The "$@" won't break that into 2 arguments. +# +LD_LIBRARY_PATH=proc exec ./top "$@" diff --git a/tload.1 b/tload.1 new file mode 100644 index 0000000..ef82b01 --- /dev/null +++ b/tload.1 @@ -0,0 +1,50 @@ +.\" -*-Nroff-*- +.\" This page Copyright (C) 1993 Matt Welsh, mdw@tc.cornell.edu. +.\" Freely distributable under the terms of the GPL +.TH TLOAD 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual" +.SH NAME +tload \- graphic representation of system load average +.SH SYNOPSIS +.B tload +.RB [ "\-V" "] [" "\-s" +.IR scale "] [" +.BI "\-d" " delay" +.RI "] [" tty ] +.SH DESCRIPTION +\fBtload\fP prints a graph of the current system load average to the +specified \fItty\fP (or the tty of the tload process if none is specified). +.SS Options +The +.BI "\-s" " scale" +option allows a vertical scale to be specified for the +display (in characters between graph ticks); thus, a smaller value +represents a larger scale, and vice versa. + +The +.BI "\-d" " delay" +sets the delay between graph updates in seconds. +.PP +.SH FILES +.I /proc/loadavg +load average information + +.SH "SEE ALSO" +.BR ps (1), +.BR top (1), +.BR uptime (1), +.BR w (1) + +.SH BUGS +The +.BI "\-d" " delay" +option sets the time argument for an +.BR alarm (2); +if -d 0 is specified, the alarm is set to 0, which will never send the +.B SIGALRM +and update the display. + +.SH AUTHORS +Branko Lankester, David Engel , and +Michael K. Johnson . + +Please send bug reports to diff --git a/tload.c b/tload.c new file mode 100644 index 0000000..1d346bb --- /dev/null +++ b/tload.c @@ -0,0 +1,156 @@ +/* + * tload.c - terminal version of xload + * + * Options: + * -s initial scaling exponent (default = 6) + * -d delay + * + * Copyright (c) 1992 Branko Lankester + * /proc changes by David Engel (david@ods.com) + * Made a little more efficient by Michael K. Johnson (johnsonm@sunsite.unc.edu) + */ +#include "proc/version.h" +#include "proc/sysinfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *screen; + +static int nrows = 25; +static int ncols = 80; +static int scr_size; +static int fd=1; +static int dly=5; +static jmp_buf jb; + +extern int optind; +extern char *optarg; + +static void alrm(int signo) +{ + (void)signo; + signal(SIGALRM, alrm); + alarm(dly); +} + +static void setsize(int i) +{ + struct winsize win; + + signal(SIGWINCH, setsize); + if (ioctl(fd, TIOCGWINSZ, &win) != -1) { + if (win.ws_col > 0) + ncols = win.ws_col; + if (win.ws_row > 0) + nrows = win.ws_row; + } + scr_size = nrows * ncols; + if (screen == NULL) + screen = (char *) malloc(scr_size); + else + screen = (char *) realloc(screen, scr_size); + + if (screen == NULL) { + perror(""); + exit(1); + } + memset(screen, ' ', scr_size-1); + *(screen + scr_size - 2) = '\0'; + if (i) + longjmp(jb, 0); +} + +int main(int argc, char **argv) +{ + int lines, row, col=0; + int i, opt; + double av[3]; + static double max_scale, scale_fact; + char *scale_arg = NULL; + + while ((opt = getopt(argc, argv, "s:d:V")) != -1) + switch (opt) { + case 's': scale_arg = optarg; break; + case 'd': dly = atoi(optarg); break; + case 'V': display_version(); exit(0); break; + default: + printf("usage: tload [-V] [-d delay] [-s scale] [tty]\n"); + exit(1); + } + + if (argc > optind) { + if ((fd = open(argv[optind], 1)) == -1) { + perror(argv[optind]); + exit(1); + } + } + + setsize(0); + + if (scale_arg) + max_scale = atof(scale_arg); + else + max_scale = nrows; + + scale_fact = max_scale; + + setjmp(jb); + col = 0; + alrm(0); + + while (1) { + + if (scale_fact < max_scale) + scale_fact *= 2.0; /* help it drift back up. */ + + loadavg(&av[0], &av[1], &av[2]); + + repeat: + lines = av[0] * scale_fact; + row = nrows-1; + + while (--lines >= 0) { + *(screen + row * ncols + col) = '*'; + if (--row < 0) { + scale_fact /= 2.0; + goto repeat; + } + } + while (row >= 0) + *(screen + row-- * ncols + col) = ' '; + + for (i = 1; ; ++i) { + char *p; + row = nrows - (i * scale_fact); + if (row < 0) + break; + if (*(p = screen + row * ncols + col) == ' ') + *p = '-'; + else + *p = '='; + } + + if (++col == ncols) { + --col; + memmove(screen, screen + 1, scr_size-1); + + for(row = nrows-2; row >= 0; --row) + *(screen + row * ncols + col) = ' '; + } + i = sprintf(screen, " %.2f, %.2f, %.2f", + av[0], av[1], av[2]); + if (i>0) + screen[i] = ' '; + + write(fd, "\033[H", 3); + write(fd, screen, scr_size - 1); + pause(); + } +} diff --git a/top.1 b/top.1 new file mode 100644 index 0000000..aeee3c9 --- /dev/null +++ b/top.1 @@ -0,0 +1,1617 @@ +.ig +. manual page for NEW top +. Copyright (c) 2002, by: JC Warner & Associates, Ltd. +. +. Permission is granted to copy, distribute and/or modify this document +. under the terms of the GNU Free Documentation License, Version 1.1 or +. any later version published by the Free Software Foundation; +. with no Front-Cover Texts, no Back-Cover Texts, and with the following +. Invariant Sections (and any sub-sections therein): +. all .ig sections, including this one +. STUPID TRICKS Sampler +. AUTHOR +. +. A copy of the Free Documentation License is included in the section +. entitled "GNU Free Documentation License". +. +. [ that section is found near the end of this document & ] +. [ can be made printable by disabling the .ig directive! ] +. +.. +.\" Setup //////////////////////////////////////////////////////////////// +\# ** Comment out '.nr' or set to 0 to eliminate WIDTH fiddlin' ! +.nr half_xtra 4 +. +.ll +(\n[half_xtra] + \n[half_xtra]) +. +\# Our darn Bullet style ---------------------------- +.de Jbu +.IP "-" 3 +.. +\# - bullet continuation paragraph +.de Jp +.IP "" 3 +.. +\# New features/differences style ------------------- +.de New +.IP "-*-" 5 +.. +. +\# Commonly used strings (for consistency) ---------- +\# - a real em-dash, darn-it +.ds EM \ \fB\-\-\ \fR +\# - these two are for chuckles, makes great grammar +.ds Me top +.ds ME \fBtop\fR +\# - other misc strings for consistent usage/emphasis +.ds F \fIOff\fR +.ds O \fIOn\fR +. +.ds AM alternate\-display mode +.ds AS asterisk ('*') +.ds CF configuration file +.ds CI interactive command +.ds CO command\-line option +.ds CW \'current' window +.ds FM full\-screen mode +.ds MP \fBphysical\fR memory +.ds MS \fBshared\fR memory +.ds MV \fBvirtual\fR memory +.ds NT \fBNote\fR: +.ds PU CPU +.ds Pu cpu +.ds SA summary area +.ds TA task area +.ds TD task display +.ds TW task window +\# - xref's that depend on commands or topic names +.ds XC See the +.ds Xc see the +.ds XT See topic +.ds Xt see topic +. +.\" ////////////////////////////////////////////////////////////////////// +.\" ---------------------------------------------------------------------- +.TH TOP 1 "September 2002" "Linux" "Linux User's Manual" +.\" ---------------------------------------------------------------------- + + +.\" ---------------------------------------------------------------------- +.SH NAME +.\" ---------------------------------------------------------------------- +top \- display Linux tasks + + +.\" ---------------------------------------------------------------------- +.SH SYNOPSIS +.\" ---------------------------------------------------------------------- +\*(ME \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI +iterations\fR \-\fBp\fI pid\fR [,\fI pid\fR ...] + +The traditional switches '-' and whitespace are optional. + + +.\" ---------------------------------------------------------------------- +.SH DESCRIPTION +.\" ---------------------------------------------------------------------- +The \*(ME program provides a dynamic real-time view of a running system. +It can display\fB system\fR summary information as well as a list of\fB +tasks\fR currently being managed by the Linux kernel. +The types of system summary information shown and the types, order and +size of information displayed for tasks are all user configurable and +that configuration can be made persistent across restarts. + +The program provides a limited interactive interface for process +manipulation as well as a much more extensive interface for personal +configuration \*(EM encompassing every aspect of its operation. +And while \*(ME is referred to throughout this document, you are free +to name the program anything you wish. +That new name, possibly an alias, will then be reflected on \*(Me's display +and used when reading and writing a \*(CF. + + +.\" ---------------------------------------------------------------------- +.SH OVERVIEW +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS Documentation +.\" ---------------------------------------------------------------------- +The remaining Table of Contents + 1. COMMAND\-LINE Options + 2. FIELDS / Columns + a. DESCRIPTIONS of Fields + b. SELECTING and ORDERING Columns + 3. INTERACTIVE Commands + a. GLOBAL Commands + b. SUMMARY Area Commands + c. TASK Area Commands + d. COLOR Mapping + 4. ALTERNATE\-DISPLAY Mode + a. WINDOWS Overview + b. COMMANDS for Windows + 5. FILES + a. SYSTEM Configuration File + b. PERSONAL Configuration File + 6. STUPID TRICKS Sampler + a. Kernel Magic + b. Bouncing Windows + c. The Big Bird Window + 7. BUGS, 8. HISTORY Former top, 9. AUTHOR, 10. SEE ALSO + +.\" ...................................................................... +.SS Operation +.\" ---------------------------------------------------------------------- +When operating \*(Me, the two most important keys are help ('h' or '?') and +quit ('q') key. +Alternatively, you could simply use the traditional interrupt key ('^C') +when you're done. + +When you start \*(Me for the first time, you'll be presented with the +traditional screen elements: 1) Summary Area; 2) Message/Prompt Line; +3) Columns Header; 4) Task Area. +There will, however, be some differences when compared to the former top. + +.TP 3 +.B Highlighting +.I Summary_Area\fR: +There is no highlighting for load/uptime and only values are highlighted for +other elements. + +.I Task_Area\fR: +Tasks running (or ready to run) will be highlighted, and bold is only one way +of emphasizing such processes. + +.TP 3 +.B Content/Labels +.I Summary_Area\fR: +The program name is shown, perhaps a symlink or alias. +The Cpu(s) state label hints at other possibilities. +The memory stats use a lower case 'k'. + +.I Columns_Header\fR: +Will show a new field and some changed labels. +More new fields will be found as you customize your \*(Me. + +.PP +\*(NT the width of \*(Me's display will be limited to 512 positions. +Displaying all fields requires a minimum of 160 characters. +The remaining width could be used for the 'Command' column. + +.\" ...................................................................... +.SS Startup Defaults +.\" ---------------------------------------------------------------------- +The following startup defaults assume no \*(CF, thus no user customizations. +Even so, items shown with an \*(AS could be overridden through the +command-line. + + \fIGlobal_defaults\fR + 'A' - Alt display Off (full-screen) + * 'd' - Delay time 3.0 seconds + 'I' - Irix mode On\ \ (no, 'solaris' smp) + * 'p' - PID monitoring Off + * 's' - Secure mode Off (unsecured) + 'B' - Bold disable Off + \fISummary_Area_defaults\fR + 'l' - Load Avg/Uptime On\ \ (thus program name) + 't' - Task/Cpu states On\ \ (1+1 lines, see '1') + 'm' - Mem/Swap usage On\ \ (2 lines worth) + '1' - Single Cpu On\ \ (thus 1 line if smp) + \fITask_Area_defaults\fR + 'b' - Bold hilite On\ \ (not 'reverse') + * 'c' - Command line Off (name, not cmdline) + * 'H' - Threads Off\ (show all threads) + * 'i' - Idle tasks On\ \ (show all tasks) + 'R' - Reverse sort On\ \ (pids high-to-low) + * 'S' - Cumulative time Off (no, dead children) + 'x' - Column hilite Off\ (no, sort field) + 'y' - Row hilite On\ \ (yes, running tasks) + 'z' - color/mono Off\ (no, colors) + + +.\" ---------------------------------------------------------------------- +.SH 1. COMMAND-LINE Options +.\" ---------------------------------------------------------------------- +The command-line syntax for \*(Me consists of: + + \-\fBhv\fR\ |\ -\fBbcHisS\fR\ \-\fBd\fI\ delay\fR\ \-\fBn\fI\ iterations\ +\fR\ \-\fBp\fI\ pid\fR\ [,\fIpid\fR...] + +The typically mandatory switches ('-') and even whitespace are completely +optional. + +.TP 5 +\-\fBb\fR :\fB Batch mode\fR operation +Starts \*(Me in 'Batch mode', which could be useful for sending output +from \*(Me to other programs or to a file. +In this mode, \*(Me will not accept input and runs until the iterations +limit you've set with the '-n' \*(CO or until killed. + +.TP 5 +\-\fBc\fR :\fB Command line/Program name\fR toggle +Starts \*(Me with the last remembered 'c' state reversed. +Thus, if \*(Me was displaying command lines, now that field will show program +names, and visa versa. +\*(XC 'c' \*(CI for additional information. + +.TP 5 +\-\fBd\fR :\fB Delay time\fR interval as:\ \ \fB-d ss.tt\fR (\fIseconds\fR.\fItenths\fR) +Specifies the delay between screen updates, and overrides the corresponding +value in one's personal \*(CF or the startup default. +Later this can be changed with the 'd' or 's' \*(CIs. + +Fractional seconds are honored, but a negative number is not allowed. +In all cases, however, such changes are prohibited if \*(Me is running +in 'Secure mode', except for root (unless the 's' \*(CO was used). +For additional information on 'Secure mode' \*(Xt 5a. SYSTEM Configuration File. + + +.TP 5 +\-\fBh\fR :\fB Help\fR +Show library version and the usage prompt, then quit. + +.TP 5 +\-\fBH\fR :\fB Threads\fR toggle +Starts \*(Me with the last remembered 'H' state reversed. +When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process. + +.TP 5 +\-\fBi\fR :\fB Idle Processes\fR toggle +Starts \*(Me with the last remembered 'i' state reversed. +When this toggle is \*F, tasks that are idled or zombied will not be displayed. + +.TP 5 +\-\fBn\fR :\fB Number of iterations\fR limit as:\fB\ \ -n number\fR +Specifies the maximum number of iterations, or frames, \*(Me should +produce before ending. + +.TP 5 +\-\fBu\fR :\fB Monitor by user\fR as:\fB\ \ -u somebody +Monitor only processes with an effective UID or user name +matching that given. + +.TP 5 +\-\fBU\fR :\fB Monitor by user\fR as:\fB\ \ -U somebody +Monitor only processes with a UID or user name matching that given. +This matches real, effective, saved, and filesystem UIDs. + +.TP 5 +\-\fBp\fR :\fB Monitor PIDs\fR as:\fB\ \ -pN1 -pN2 ...\fR\ \ or\fB\ \ -pN1, N2 [,...] +Monitor only processes with specified process IDs. +This option can be given up to 20 times, or you can provide a comma delimited +list with up to 20 pids. +Co-mingling both approaches is permitted. + +This is a \*(CO only. +And should you wish to return to normal operation, it is not necessary +to quit and and restart \*(Me \*(EM just issue the '=' \*(CI. + +.TP 5 +\-\fBs\fR :\fB Secure mode\fR operation +Starts \*(Me with secure mode forced, even for root. +This mode is far better controlled through the system \*(CF +(\*(Xt 5. FILES). + +.TP 5 +\-\fBS\fR :\fB Cumulative time mode\fR toggle +Starts \*(Me with the last remembered 'S' state reversed. +When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +time that it and its dead children have used. +\*(XC 'S' \*(CI for additional information regarding this mode. + +.TP 5 +\-\fBv\fR :\fB Version\fR +Show library version and the usage prompt, then quit. + + +.\" ---------------------------------------------------------------------- +.SH 2. FIELDS / Columns +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS 2a. DESCRIPTIONS of Fields +.\" ---------------------------------------------------------------------- +Listed below are \*(Me's available fields. +They are always associated with the letter shown, regardless of the position +you may have established for them with the 'o' (Order fields) \*(CI. + +Any field is selectable as the sort field, and you control whether they +are sorted high-to-low or low-to-high. +For additional information on sort provisions \*(Xt 3c. TASK Area Commands. + +.TP 3 +a:\fB PID\fR \*(EM Process Id\fR +The task's unique process ID, which periodically wraps, +though never restarting at zero. + +.TP 3 +b:\fB PPID\fR \*(EM Parent Process Pid\fR +The process ID of a task's parent. + +.TP 3 +c:\fB RUSER\fR \*(EM Real User Name\fR +The real user name of the task's owner. + +.TP 3 +d:\fB UID\fR \*(EM User Id\fR +The effective user ID of the task's owner. + +.TP 3 +e:\fB USER\fR \*(EM User Name\fR +The effective user name of the task's owner. + +.TP 3 +f:\fB GROUP\fR \*(EM Group Name\fR +The effective group name of the task's owner. + +.TP 3 +g:\fB TTY\fR \*(EM Controlling Tty +The name of the controlling terminal. +This is usually the device (serial port, pty, etc.) from which the +process was started, and which it uses for input or output. +However, a task need not be associated with a terminal, in which case +you'll see '?' displayed. + +.TP 3 +h:\fB PR\fR \*(EM Priority +The priority of the task. + +.TP 3 +i:\fB NI\fR \*(EM Nice value +The nice value of the task. +A negative nice value means higher priority, whereas a positive nice value +means lower priority. +Zero in this field simply means priority will not be adjusted in determining a +task's dispatchability. + +.TP 3 +j:\fB P\fR \*(EM Last used \*(PU (SMP) +A number representing the last used processor. +In a true SMP environment this will likely change frequently since the kernel +intentionally uses weak affinity. +Also, the very act of running \*(Me may break this weak affinity and cause more +processes to change \*(PUs more often (because of the extra demand for +\*(Pu time). + +.TP 3 +k:\fB %CPU\fR \*(EM \*(PU usage +The task's share of the elapsed \*(PU time since the last screen update, +expressed as a percentage of total \*(PU time. +In a true SMP environment, if 'Irix mode' is \*F, \*(Me will operate +in 'Solaris mode' where a task's \*(Pu usage will be divided by the total +number of \*(PUs. +You toggle 'Irix/Solaris' modes with the 'I' \*(CI. + +.TP 3 +l:\fB TIME\fR \*(EM \*(PU Time +Total \*(PU time the task has used since it started. +When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +time that it and its dead children has used. +You toggle 'Cumulative mode' with 'S', which is a \*(CO and an \*(CI. +\*(XC 'S' \*(CI for additional information regarding this mode. + +.TP 3 +m:\fB TIME+\fR \*(EM \*(PU Time, hundredths +The same as 'TIME', but reflecting more granularity through hundredths of +a second. + +.TP 3 +n:\fB %MEM\fR \*(EM Memory usage (RES) +A task's currently used share of available \*(MP. + +.TP 3 +o:\fB VIRT\fR \*(EM Virtual Image (kb) +The total amount of \*(MV used by the task. +It includes all code, data and shared libraries plus pages that have been +swapped out. + +VIRT = SWAP + RES. + +.TP 3 +p:\fB SWAP\fR \*(EM Swapped size (kb) +The swapped out portion of a task's total \*(MV image. + +.TP 3 +q:\fB RES\fR \*(EM Resident size (kb) +The non-swapped \*(MP a task has used. + +RES = CODE + DATA. + +.TP 3 +r:\fB CODE\fR \*(EM Code size (kb) +The amount of \*(MP devoted to executable code, also known as +the 'text resident set' size or TRS. + +.TP 3 +s:\fB DATA\fR \*(EM Data+Stack size (kb) +The amount of \*(MP devoted to other than executable code, also known as +the 'data resident set' size or DRS. + +.TP 3 +t:\fB SHR\fR \*(EM Shared Mem size (kb) +The amount of \*(MS used by a task. +It simply reflects memory that could be potentially shared with +other processes. + +.TP 3 +u:\fB nFLT\fR \*(EM Page Fault count +The number of\fB major\fR page faults that have occurred for a task. +A page fault occurs when a process attempts to read from or write to a virtual +page that is not currently present in its address space. +A major page fault is when disk access is involved in making that +page available. + +.TP 3 +v:\fB nDRT\fR \*(EM Dirty Pages count +The number of pages that have been modified since they were last +written to disk. +Dirty pages must be written to disk before the corresponding physical memory +location can be used for some other virtual page. + +.TP 3 +w:\fB S\fR \*(EM Process Status +The status of the task which can be one of: + '\fBD\fR' = uninterruptible sleep + '\fBR\fR' = running + '\fBS\fR' = sleeping + '\fBT\fR' = traced or stopped + '\fBZ\fR' = zombie + +Tasks shown as running should be more properly thought of as 'ready to run' +\*(EM their task_struct is simply represented on the Linux run-queue. +Even without a true SMP machine, you may see numerous tasks in this state +depending on \*(Me's delay interval and nice value. + +.TP 3 +x:\fB Command\fR \*(EM Command\fB line\fR or Program\fB name\fR +Display the command line used to start a task or the name of the associated +program. +You toggle between command\fI line\fR and\fI name\fR with 'c', which is both +a \*(CO and an \*(CI. + +When you've chosen to display command lines, processes without a command +line (like kernel threads) will be shown with only the program name in +parentheses, as in this example: + \fR( mdrecoveryd ) + +Either form of display is subject to potential truncation if it's too long to +fit in this field's current width. +That width depends upon other fields selected, their order and the current +screen width. + +\*(NT The 'Command' field/column is unique, in that it is not fixed-width. +When displayed, this column will be allocated all remaining screen width (up +to the maximum 512 characters) to provide for the potential growth of program +names into command lines. + +.TP 3 +y:\fB WCHAN\fR \*(EM Sleeping in Function +Depending on the availability of the kernel link map ('System.map'), this field +will show the name or the address of the kernel function in which the task is +currently sleeping. +Running tasks will display a dash ('-') in this column. + +\*(NT By displaying this field, \*(Me's own working set will be increased by +over 700Kb. +Your only means of reducing that overhead will be to stop and restart \*(Me. + +.TP 3 +z:\fB Flags\fR \*(EM Task Flags +This column represents the task's current scheduling flags which are +expressed in hexadecimal notation and with zeros suppressed. +These flags are officially documented in . +Less formal documentation can also be found on the 'Fields select' +and 'Order fields' screens. + +.\" ...................................................................... +.SS 2b. SELECTING and ORDERING Columns +.\" ---------------------------------------------------------------------- +After pressing the \*(CIs 'f' (Fields select) or \'o' (Order fields) you will +be shown a screen containing the current \fBfields string\fR followed by names +and descriptions for all fields. + +Here is a sample\fB fields string\fR from one of \*(Me's four windows/field +groups and an explanation of the conventions used: +.Jbu +Sample fields string: + \fIANOPQRSTUVXbcdefgjlmyzWHIK\fR +.Jbu +The order of displayed fields corresponds to the order of the letters +in that string. +.Jbu +If the letter is\fI upper case\fR the corresponding field itself will +then be shown as part of the \*(TD (screen width permitting). +This will also be indicated by a leading \*(AS, as in this excerpt: + \fR... + \fB* K: %CPU = CPU usage + \fR l: TIME = CPU Time + \fR m: TIME+ = CPU Time, hundredths + \fB* N: %MEM = Memory usage (RES) + \fB* O: VIRT = Virtual Image (kb) + \fR... + +.TP +.B Fields select\fR screen \*(EM the 'f' \*(CI +You\fI toggle\fR the\fB display\fR of a field by simply pressing the +corresponding letter. + +.TP +.B Order fields\fR screen \*(EM the 'o' \*(CI +You\fI move\fR a field to the\fB left\fR by pressing the corresponding\fB +upper case\fR letter and to the\fB right\fR with the\fB lower case\fR +letter. + + +.\" ---------------------------------------------------------------------- +.SH 3. INTERACTIVE Commands +.\" ---------------------------------------------------------------------- +Listed below is a brief index of commands within categories. +Some commands appear more than once \*(EM their meaning or scope may vary +depending on the context in which they are issued. + + 3a.\fI GLOBAL_Commands\fR + ?, =, A, B, d, G, h, I, k, q, r, s, W, Z + 3b.\fI SUMMARY_Area_Commands\fR + l, m, t, 1 + 3c.\fI TASK_Area_Commands\fR + Appearance: b, x, y, z + Content: c, f, H, o, S, u + Size: #, i, n + Sorting: <, >, F, O, R + 3d.\fI COLOR_Mapping\fR + , a, B, b, H, M, q, S, T, w, z, 0 - 7 + 4b.\fI COMMANDS_for_Windows\fR + -, _, =, +, A, a, G, g, w + +.\" ...................................................................... +.SS 3a. GLOBAL Commands +The global \*(CIs are\fB always\fR available\fR in both \*(FM and \*(AM. +However, some of these \*(CIs are\fB not available\fR when running +in 'Secure mode'. + +If you wish to know in advance whether or not your \*(Me has been secured, +simply ask for help and view the system summary on the second line. + +.TP 7 +\ \ \<\fBEnter\fR> or <\fBSpace\fR> :\fIRefresh_Display\fR +These commands do nothing, they are simply ignored. +However, they will awaken \*(Me and following receipt of any input +the entire display will be repainted. + +Use either of these keys if you have a large delay interval and wish to +see current status, + +.TP 7 +\ \ \'\fB?\fR\' or \'\fBh\fR\' :\fIHelp\fR +There are two help levels available. +The first will provide a reminder of all the basic \*(CIs. +If \*(Me is\fI secured\fR, that screen will be abbreviated. + +Typing 'h' or '?' on that help screen will take you to help for those \*(CIs +applicable to \*(AM. + +.TP 7 +\ \ \'\fB=\fR\' :\fIExit_Task_Limits\fR +Removes restrictions on which tasks are shown. +This command will reverse any 'i' (idle tasks) and 'n' (max tasks) commands +that might be active. +It also provides for an 'exit' from PID monitoring. +See the '-p' \*(CO for a discussion of PID monitoring. + +When operating in \*(AM this command has a slightly broader meaning. + +.TP 7 +\ \ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR +This command will switch between \*(FM and \*(AM. +\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into +\*(CWs and field groups. + +.TP 7 +\ \ \'\fBB\fR\' :\fIBold_Disable/Enable_toggle\fR +This command will influence use of the 'bold' terminfo capability and +alters\fB both\fR the \*(SA and \*(TA for the \*(CW. +While it is intended primarily for use with dumb terminals, it can be +applied anytime. + +\*(NT When this toggle is \*O and \*(Me is operating in monochrome mode, +the\fB entire display\fR will appear as normal text. +Thus, unless the 'x' and/or 'y' toggles are using reverse for emphasis, +there will be no visual confirmation that they are even on. + +.TP 7 +*\ \'\fBd\fR\' or \'\fBs\fR\' :\fIChange_Delay_Time_interval\fR +You will be prompted to enter the delay time, in seconds, between +display updates. + +Fractional seconds are honored, but a negative number is not allowed. +Entering 0 causes (nearly) continuous updates, with an unsatisfactory +display as the system and tty driver try to keep up with \*(Me's demands. +The delay value is inversely proportional to system loading, +so set it with care. + +If at any time you wish to know the current delay time, simply ask for help +and view the system summary on the second line. + +.TP 7 +\ \ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR +You will be prompted to enter a number between 1 and 4 designating the +window/field group which should be made the \*(CW. +You will soon grow comfortable with these 4 windows, especially after +experimenting with \*(AM. + +.TP 7 +\ \ \'\fBI\fR\' :\fIIrix/Solaris_Mode_toggle\fR +When operating in 'Solaris mode' ('I' toggled \*F), a task's \*(Pu usage +will be divided by the total number of \*(PUs. +After issuing this command, you'll be informed of the new state of this toggle. + +.TP 7 +\ \ \'\fBu\fR\' :\fIselect a user\fR +You will be prompted for a UID or username. Only processes +belonging to the selected user will be displayed. This option +matches on the effective UID. + +.TP 7 +\ \ \'\fBU\fR\' :\fIselect a user\fR +You will be prompted for a UID or username. Only processes +belonging to the selected user will be displayed. This option +matches on the real, effective, saved, and filesystem UID. + +.TP 7 +*\ \'\fBk\fR\' :\fIKill_a_task\fR +You will be prompted for a PID and then the signal to send. +The default signal, as reflected in the prompt, is SIGTERM. +However, you can send any signal, via number or name. + +If you wish to abort the kill process, do one of the following +depending on your progress: + 1) at the pid prompt, just press + 2) at the signal prompt, type 0 + +.TP 7 +\ \ \'\fBq\fR\' :\fIQuit\fR + +.TP 7 +*\ \'\fBr\fR\' :\fIRenice_a_Task\fR +You will be prompted for a PID and then the value to nice it to. +Entering a positive value will cause a process to lose priority. +Conversely, a negative value will cause a process to be viewed more +favorably by the kernel. + +.TP 7 +\ \ \'\fBW\fR\' :\fIWrite_the_Configuration_File\fR +This will save all of your options and toggles plus the current +display mode and delay time. +By issuing this command just before quitting \*(Me, you will be able restart +later in exactly that same state. + +.TP 7 +\ \ \'\fBZ\fR\' :\fIChange_Color_Mapping +This key will take you to a separate screen where you can change the +colors for the \*(CW, or for all windows. +For details regarding this \*(CI \*(Xt 3d. COLOR Mapping. + +.IP "*" 3 +The commands shown with an \*(AS are not available in 'Secure mode', +nor will they be shown on the level-1 help screen. + +.\" ...................................................................... +.SS 3b. SUMMARY Area Commands +The \*(SA \*(CIs are\fB always available\fR in both \*(FM and \*(AM. +They affect the beginning lines of your display and will determine the position +of messages and prompts. + +These commands always impact just the \*(CW/field group. +\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into +\*(CWs and field groups. + +.TP 7 +\ \ \'\fBl\fR\' :\fIToggle_Load_Average/Uptime\fR \*(EM On/Off +This is also the line containing the program name (possibly an alias) when +operating in \*(FM or the \*(CW name when operating in \*(AM. + +.TP 7 +\ \ \'\fBm\fR\' :\fIToggle_Memory/Swap_Usage\fR \*(EM On/Off +This command affects two \*(SA lines. + +.TP 7 +\ \ \'\fBt\fR\' :\fIToggle_Task/Cpu_States\fR \*(EM On/Off +This command affects from 2 to many \*(SA lines, depending on the state +of the '1' toggle and whether or not \*(Me is running under true SMP. + +.TP 7 +\ \ \'\fB1\fR\' :\fIToggle_Single/Separate_Cpu_States\fR \*(EM On/Off +This command affects how the 't' command's Cpu States portion is shown. +Although this toggle exists primarily to serve massively-parallel SMP machines, +it is not restricted to solely SMP environments. + +When you see 'Cpu(s):' in the \*(SA, the '1' toggle is \*O and all +\*(Pu information is gathered in a single line. +Otherwise, each \*(Pu is displayed separately as: 'Cpu0, Cpu1, ...' + +.PP +\*(NT If the entire \*(SA has been toggled \*F for any window, you would be left +with just the\fB message line\fR. +In that way, you will have maximized available task rows but (temporarily) +sacrificed the program name in \*(FM or the \*(CW name when in \*(AM. + +.\" ...................................................................... +.SS 3c. TASK Area Commands +The \*(TA \*(CIs are\fB always\fR available in \*(FM. + +The \*(TA \*(CIs are\fB never available\fR in \*(AM\fI if\fR the \*(CW's +\*(TD has been toggled \*F (\*(Xt 4. ALTERNATE\-DISPLAY Mode). + +.PP +.\" ......................... +.B APPEARANCE\fR of \*(TW +.br +.in +2 +The following commands will also be influenced by the state of the +global 'B' (bold disable) toggle. +.in + +.TP 7 +\ \ \'\fBb\fR\' :\fIBold/Reverse_toggle\fR +This command will impact how the 'x' and 'y' toggles are displayed. +Further, it will only be available when at least one of those toggles is \*O. + +.TP 7 +\ \ \'\fBx\fR\' :\fIColumn_Highlight_toggle\fR +Changes highlighting for the current sort field. +You probably don't need a constant visual reminder of the sort field and +\*(Me hopes that you always run with 'column highlight' \*F, due to the cost +in path-length. + +If you forget which field is being sorted this command can serve as a quick +visual reminder. + +.TP 7 +\ \ \'\fBy\fR\' :\fIRow_Highlight_toggle\fR +Changes highlighting for "running" tasks. +For additional insight into this task state, \*(Xt 2a. DESCRIPTIONS of Fields, +Process Status. + +Use of this provision provides important insight into your system's health. +The only costs will be a few additional tty escape sequences. + +.TP 7 +\ \ \'\fBz\fR\' :\fIColor/Monochrome_toggle\fR +Switches the \*(CW between your last used color scheme and the older form +of black-on-white or white-on-black. +This command will alter\fB both\fR the \*(SA and \*(TA but does not affect the +state of the 'x', 'y' or 'b' toggles. + +.PP +.\" ......................... +.B CONTENT\fR of \*(TW +.PD 0 +.TP 7 +\ \ \'\fBc\fR\' :\fICommand_Line/Program_Name_toggle\fR +This command will be honored whether or not the 'Command' column +is currently visible. +Later, should that field come into view, the change you applied will be seen. + +.TP 7 +\ \ \'\fBf\fR\' and \'\fBo\fR\' :\fIFields_select\fR or \fIOrder_fields\fR +These keys display separate screens where you can change which +fields are displayed and their order. +For additional information on these \*(CIs +\*(Xt 2b. SELECTING and ORDERING Columns. + +.TP 7 +\ \ \'\fBH\fR\' :\fIThreads_toggle\fR +When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process. + +.TP 7 +\ \ \'\fBS\fR\' :\fICumulative_Time_Mode_toggle\fR +When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +time that it and its dead children have used. + +When \*F, programs that fork into many separate tasks will appear +less demanding. +For programs like 'init' or a shell this is appropriate but for others, +like compilers, perhaps not. +Experiment with two \*(TWs sharing the same sort field but with different 'S' +states and see which representation you prefer. + +After issuing this command, you'll be informed of the new state of this toggle. +If you wish to know in advance whether or not 'Cumulative mode' is in +effect, simply ask for help and view the window summary on the second line. + +.TP 7 +\ \ \'\fBu\fR\' :\fIShow_Specific_User_Only\fR +You will be prompted to enter the name of the user to display. +Thereafter, in that \*(TW only matching User ID's will be shown, or possibly +no tasks will be shown. + +Later, if you wish to monitor all tasks again, re-issue this command but +just press at the prompt, without providing a name. + +.PP +.\" ......................... +.B SIZE\fR of \*(TW +.PD 0 +.TP 7 +\ \ \'\fBi\fR\' :\fIIdle_Processes_toggle\fR +Displays all tasks or just active tasks. +When this toggle is \*F, idled or zombied processes will not be displayed. + +If this command is applied to the last \*(TD when in \*(AM, then it will not +affect the window's size, as all prior \*(TDs will have already been painted. + +.TP 7 +\ \ \'\fBn\fR\' or \'#\' :\fISet_Maximum_Tasks\fR +You will be prompted to enter the number of tasks to display. +The lessor of your number and available screen rows will be used. + +When used in \*(AM, this is the command that gives you precise control over +the size of each currently visible \*(TD, except for the very last. +It will not affect the last window's size, as all prior \*(TDs will have +already been painted. + +\*(NT If you wish to increase the size of the last visible \*(TD when in \*(AM, +simply decrease the size of the \*(TD(s) above it. + +.PP +.\" ......................... +.B SORTING\fR of \*(TW +.br +.in +2 +For compatibility, this \*(Me supports most of the former \*(Me sort keys. +Since this is primarily a service to former \*(Me users, these commands do +not appear on any help screen. + command sorted field supported + A start time (non-display) No + M %MEM Yes + N PID Yes + P %CPU Yes + T TIME+ Yes + +Before using any of the following sort provisions, \*(Me suggests that you +temporarily turn on column highlighting using the 'x' \*(CI. +That will help ensure that the actual sort environment matches your intent. + +The following \*(CIs will\fB only\fR be honored when the +current sort field is\fB visible\fR. +The sort field might\fI not\fR be visible because: + 1) there is insufficient\fI Screen Width\fR + 2) the 'f' \*(CI turned it \*F +.in + +.TP 7 +\ \ \'\fB<\fR\' :\fIMove_Sort_Field_Left\fR +Moves the sort column to the left unless the current sort field is +the first field being displayed. + +.TP 7 +\ \ \'\fB>\fR\' :\fIMove_Sort_Field_Right\fR +Moves the sort column to the right unless the current sort field is +the last field being displayed. + +.PP +.in +2 +The following \*(CIs will\fB always\fR be honored whether or not +the current sort field is visible. +.in + +.TP 7 +\ \ \'\fBF\fR\' or \'\fBO\fR\' :\fISelect_Sort_Field\fR +These keys display a separate screen where you can change which field +is used as the sort column. + +If a field is selected which was not previously being displayed, it will +be forced \*O when you return to the \*(Me display. +However, depending upon your screen width and the order of your fields, +this sort field may not be displayable. + +This \*(CI can be a convenient way to simply verify the current sort field, +when running \*(Me with column highlighting turned \*F. + +.TP 7 +\ \ \'\fBR\fR\' :\fIReverse/Normal_Sort_Field_toggle\fR +Using this \*(CI you can alternate between high-to-low and low-to-high sorts. + +.PP +.in +2 +\*(NT Field sorting uses internal values, not those in column display. +Thus, the TTY and WCHAN fields will violate strict ASCII collating sequence. +.in + +.\" ...................................................................... +.SS 3d. COLOR Mapping +When you issue the 'Z' \*(CI, you will be presented with a separate screen. +That screen can be used to change the colors in just the \*(CW or +in all four windows before returning to the \*(Me display. + +.P +.B Available \*(CIs + \fB4\fR upper case letters to select a\fB target\fR + \fB8\fR numbers to select a\fB color\fR + normal toggles available\fR + 'B' :bold disable/enable + 'b' :running tasks "bold"/reverse + 'z' :color/mono + other commands available\fR + 'a'/'w' :apply, then go to next/prior + :apply and exit + 'q' :abandon current changes and exit + +If your use 'a' or 'w' to cycle the targeted window, you will +have applied the color scheme that was displayed when you left that window. +You can, of course, easily return to any window and reapply different +colors or turn colors \*F completely with the 'z' toggle. + +The Color Mapping screen can also be used to change the \*(CW/field group +in either \*(FM or \*(AM. +Whatever was targeted when 'q' or was pressed will be made current +as you return to the \*(Me display. + + +.\" ---------------------------------------------------------------------- +.SH 4. ALTERNATE\-DISPLAY Mode +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS 4a. WINDOWS Overview +.TP +.B Field Groups/Windows\fR: +.br +In \*(FM there is a single window represented by the entire screen. +That single window can still be changed to display 1 of 4 different\fB field +groups\fR (\*(Xc 'G' \*(CI, repeated below). +Each of the 4 field groups has a unique separately configurable\fB \*(SA\fR +and its own configurable\fB \*(TA\fR. + +In \*(AM, those 4 underlying field groups can now be made visible +simultaneously, or can be turned \*F individually at your command. + +The \*(SA will always exist, even if it's only the message line. +At any given time only\fI one\fR \*(SA can be displayed. +However, depending on your commands, there could be from\fI zero\fR +to\fI four\fR separate \*(TDs currently showing on the screen. + +.TP +.B Current Window\fR: +.br +The \*(CW is the window associated with the \*(SA and the window to which +task related commands are always directed. +Since in \*(AM you can toggle the \*(TD \*F, some commands might be +restricted for the \*(CW. + +A further complication arises when you have toggled the first \*(SA +line \*F. +With the loss of the window name (the 'l' toggled line), you'll not easily +know what window is the \*(CW. + +.\" ...................................................................... +.SS 4b. COMMANDS for Windows +.TP 7 +\ \ \'\fB-\fR\' and \'\fB_\fR\' :\fIShow/Hide_Window(s)_toggles\fR +The '-' key turns the \*(CW's \*(TD \*O and \*F. +When \*O, that \*(TA will show a minimum of the columns header you've +established with the 'f' and 'o' commands. +It will also reflect any other \*(TA options/toggles you've applied yielding +zero or more tasks. + +The '_' key does the same for all \*(TDs. +In other words, it switches between the currently visible \*(TD(s) and any +\*(TD(s) you had toggled \*F. +If all 4 \*(TDs are currently visible, this \*(CI will leave the \*(SA +as the only display element. + +.TP 7 +*\ \'\fB=\fR\' and \'\fB+\fR\' :\fIEqualize_(re-balance)_Window(s)\fR +The '=' key forces the \*(CW's \*(TD to be visible. +It also reverses any 'i' (idle tasks) and 'n' (max tasks) commands that might +be active. + +The '+' key does the same for all windows. +The four \*(TDs will reappear, evenly balanced. +They will also have retained any customizations you had previously applied, +except for the 'i' (idle tasks) and 'n' (max tasks) commands. + +.TP 7 +*\ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR +This command will switch between \*(FM and \*(AM. + +The first time you issue this command, all four \*(TDs will be shown. +Thereafter when you switch modes, you will see only the \*(TD(s) you've +chosen to make visible. + +.TP 7 +*\ \'\fBa\fR\' and \'\fBw\fR\' :\fINext_Window_Forward/Backward\fR +This will change the \*(CW, which in turn changes the window to which +commands are directed. +These keys act in a circular fashion so you can reach any desired \*(CW +using either key. + +Assuming the window name is visible (you have not toggled 'l' \*F), +whenever the \*(CW name loses its emphasis/color, that's a reminder +the \*(TD is \*F and many commands will be restricted. + +.TP 7 +*\ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR +You will be prompted to enter a number between 1 and 4 designating the +window/field group which should be made the \*(CW. + +In \*(FM, this command is necessary to alter the \*(CW. +In \*(AM, it is simply a less convenient alternative to the 'a' and 'w' +commands. + +.TP 7 +\ \ \'\fBg\fR\' :\fIChange_Window/Field_Group_Name\fR +You will be prompted for a new name to be applied to the \*(CW. +It does not require that the window name be visible +(the 'l' toggle to be \*O). + +.IP "*" 3 +The \*(CIs shown with an \*(AS have use beyond \*(AM. + \'=', 'A', 'G' are always available + \'a', 'w' act the same when color mapping + + +.\" ---------------------------------------------------------------------- +.SH 5. FILES +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS 5a. SYSTEM Configuration File +The presence of this file will influence which version of the 'help' screen +is shown to an ordinary user. +More importantly, it will limit what ordinary users are allowed +to do when \*(Me is running. +They will not be able to issue the following commands. + k Kill a task + r Renice a task + d or s Change delay/sleep interval + +The system \*(CF is\fB not\fR created by \*(Me. +Rather, you create this file manually and place it in the \fI/etc\fR +directory. +Its name must be 'toprc' and must have no leading '.' (period). +It must have only two lines. + +Here is an example of the contents of\fI /etc/toprc\fR: + s # line 1: 'secure' mode switch + 5.0 # line 2: 'delay'\ \ interval in seconds + +.\" ...................................................................... +.SS 5b. PERSONAL Configuration File +This file is written as '$HOME/.your-name-4-top' + 'rc'. +Use the 'W' \*(CI to create it or update it. + +Here is the general layout: + global # line 1: the program name/alias notation + " # line 2: id,altscr,irixps,delay,curwin + per ea # line a: winname,fieldscur + window # line b: winflags,sortindx,maxtasks + " # line c: summclr,msgsclr,headclr,taskclr + +If the $HOME variable is not present, \*(Me will try to write the +personal \*(CF to the current directory, subject to permissions. + + +.\" ---------------------------------------------------------------------- +.SH 6. STUPID TRICKS Sampler +.\" ---------------------------------------------------------------------- +Many of these 'tricks' work best when you give \*(Me a scheduling boost. +So plan on starting him with a nice value of -10, assuming you've got +the authority. + +.\" ...................................................................... +.SS 6a. Kernel Magic +.\" sorry, just can't help it -- don't ya love the sound of this? +For these stupid tricks, \*(Me needs \*(FM. +.\" ( apparently AM static was a potential concern ) + +.New +The user interface, through prompts and help, intentionally implies +that the delay interval is limited to tenths of a second. +However, you're free to set any desired delay. +If you want to see Linux at his scheduling best, try a delay of .09 +seconds or less. + +For this experiment, under x-windows open an xterm and maximize it. +Then do the following: + . provide a scheduling boost and tiny delay via: + nice -n -10 top -d.09 + . keep sorted column highlighting \*F to minimize + path length + . turn \*O reverse row highlighting for emphasis + . try various sort columns (TIME/MEM work well), + and normal or reverse sorts to bring the most + active processes into view + +What you'll see is a very busy Linux doing what he's always done for you, +but there was no program available to illustrate this. + +.New +Under an xterm using 'white-on-black' colors, try setting \*(Me's task color +to black and be sure that task highlighting is set to bold, not reverse. +Then set the delay interval to around .3 seconds. + +After bringing the most active processes into view, what you'll see are +the ghostly images of just the currently running tasks. + +.New +Delete the existing rcfile, or create a new symlink. +Start this new version then type 'T' (a secret key, \*(Xt 3c. TASK Area +Commands, Sorting) followed by 'W' and 'q'. +Finally, restart the program with -d0 (zero delay). + +Your display will be refreshed at three times the rate of the former \*(Me, +a 300% speed advantage. +As \*(Me climbs the TIME ladder, be as patient as you can while speculating +on whether or not \*(Me will ever reach the \*(Me. + +.\" ...................................................................... +.SS 6b. Bouncing Windows +For these stupid tricks, \*(Me needs \*(AM. + +.New +With 3 or 4 \*(TDs visible, pick any window other than the last +and turn idle processes \*F. +Depending on where you applied 'i', sometimes several \*(TDs are bouncing and +sometimes it's like an accordion, as \*(Me tries his best to allocate space. + +.New +Set each window's summary lines differently: one with no memory; another with +no states; maybe one with nothing at all, just the message line. +Then hold down 'a' or 'w' and watch a variation on bouncing windows \*(EM +hopping windows. + +.New +Display all 4 windows and for each, in turn, set idle processes to \*F. +You've just entered the "extreme bounce" zone. + +.\" ...................................................................... +.SS 6c. The Big Bird Window +This stupid trick also requires \*(AM. + +.New +Display all 4 windows and make sure that 1:Def is the \*(CW. +Then, keep increasing window size until the all the other \*(TDs +are "pushed out of the nest". + +When they've all been displaced, toggle between all visible/invisible windows. +Then ponder this: +.br + is \*(Me fibbing or telling honestly your imposed truth? + + +.\" ---------------------------------------------------------------------- +.SH 7. BUGS +.\" ---------------------------------------------------------------------- +Send bug reports to: + Albert D\. Cahalan, + + +.\" ---------------------------------------------------------------------- +.SH 8. HISTORY Former top +.\" ---------------------------------------------------------------------- +The original top was written by Roger Binns, +based on Branko Lankester's ps program. + +Robert Nation +adapted it for the proc file system. + +Helmut Geyer +added support for configurable fields. + +Plus many other individuals contributed over the years. + + +.\" ---------------------------------------------------------------------- +.SH 9. AUTHOR +.\" ---------------------------------------------------------------------- +This entirely new and enhanced replacement was written by: + Jim / James C. Warner, +.ig + ( as a means to learn Linux, can you believe it? ) + ( & he accidentally learned a little groff, too! ) +.. + +With invaluable help from: + Albert D\. Cahalan, + Craig Small, + +.ig +.rj 2 +.B -*-\fR few though they are, some yet believe\fB -*-\fR +.B -*-\~\~\~\~\~\~\~\fRin-the-\fBart\fR-of-programming\~\~\~\~\~\~\~\fB-*-\fR +.. + +.\" ---------------------------------------------------------------------- +.SH 10. SEE ALSO +.\" ---------------------------------------------------------------------- +.BR free (1), +.BR ps (1), +.BR uptime (1), +.BR atop (1), +.BR slabtop (1), +.BR vmstat (8), +.BR w (1). + + +.\" ---------------------------------------------------------------------- +.ig +.rj 1 +\-*- +.PD +.in -3 +Copyright (c) 2002 \*(EM JC Warner & Associates, Ltd. + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.1 or +any later version published by the Free Software Foundation; +with no Front-Cover Texts, no Back-Cover Texts, and with the following +Invariant Sections and any sub-sections therein: +.na +.hy 0 +.in +3 +STUPID\ TRICKS\ Sampler; +.br +AUTHOR +.in +A copy of the license is included in the section entitled +\(dqGNU Free Documentation License\(dq. +.. +. +.\" end: active doc |||||||||||||||||||||||||||||||||||||||||||||||||| +.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +.ig +.\" ---------------------------------------------------------------------- +.SH GNU Free Documentation License +Version 1.1, March 2000 + +Copyright (C) 2000 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +.SS 0. PREAMBLE +The purpose of this License is to make a manual, textbook, or other +written document "free" in the sense of freedom: to assure everyone +the effective freedom to copy and redistribute it, with or without +modifying it, either commercially or noncommercially. Secondarily, +this License preserves for the author and publisher a way to get +credit for their work, while not being considered responsible for +modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +.SS 1. APPLICABILITY AND DEFINITIONS +This License applies to any manual or other work that contains a +notice placed by the copyright holder saying it can be distributed +under the terms of this License. The "Document", below, refers to any +such manual or work. Any member of the public is a licensee, and is +addressed as "you". + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (For example, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, whose contents can be viewed and edited directly and +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup has been designed to thwart or discourage +subsequent modification by readers is not Transparent. A copy that is +not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML designed for human modification. Opaque formats include +PostScript, PDF, proprietary formats that can be read and edited only +by proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML produced by some word processors for output +purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +.SS 2. VERBATIM COPYING +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +.SS 3. COPYING IN QUANTITY +If you publish printed copies of the Document numbering more than 100, +and the Document's license notice requires Cover Texts, you must enclose +the copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a publicly-accessible computer-network location containing a complete +Transparent copy of the Document, free of added material, which the +general network-using public has access to download anonymously at no +charge using public-standard network protocols. If you use the latter +option, you must take reasonably prudent steps, when you begin +distribution of Opaque copies in quantity, to ensure that this +Transparent copy will remain thus accessible at the stated location +until at least one year after the last time you distribute an Opaque +copy (directly or through your agents or retailers) of that edition to +the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +.SS 4. MODIFICATIONS +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +.HP 3 +.B A\fR.\ Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions (which should, +if there were any, be listed in the History section of the Document). +You may use the same title as a previous version if the original publisher of +that version gives permission. +.HP 3 +.B B\fR.\ List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified Version, +together with at least five of the principal authors of the Document +(all of its principal authors, if it has less than five). +.HP 3 +.B C\fR.\ State on the Title page the name of the publisher of the Modified +Version, as the publisher. +.HP 3 +.B D\fR.\ Preserve all the copyright notices of the Document. +.HP 3 +.B E\fR.\ Add an appropriate copyright notice for your modifications adjacent +to the other copyright notices. +.HP 3 +.B F\fR.\ Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the terms of +this License, in the form shown in the Addendum below. +.HP 3 +.B G\fR.\ Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. +.HP 3 +.B H\fR.\ Include an unaltered copy of this License. +.HP 3 +.B I\fR.\ Preserve the section entitled "History", and its title, and add to it +an item stating at least the title, year, new authors, and publisher of the +Modified Version as given on the Title Page. +If there is no section entitled "History" in the Document, create one stating +the title, year, authors, and publisher of the Document as given on its Title +Page, then add an item describing the Modified Version as stated in the +previous sentence. +.HP 3 +.B J\fR.\ Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise the network +locations given in the Document for previous versions it was based on. +These may be placed in the "History" section. +You may omit a network location for a work that was published at least four +years before the Document itself, or if the original publisher of the version +it refers to gives permission. +.HP 3 +.B K\fR.\ In any section entitled "Acknowledgements" or "Dedications", preserve +the section's title, and preserve in the section all the substance and tone of +each of the contributor acknowledgements and/or dedications given therein. +.HP 3 +.B L\fR.\ Preserve all the Invariant Sections of the Document, unaltered in their +text and in their titles. +Section numbers or the equivalent are not considered part of the section titles. +.HP 3 +.B M\fR.\ Delete any section entitled "Endorsements". +Such a section may not be included in the Modified Version. +.HP 3 +.B N\fR.\ Do not retitle any existing section as "Endorsements" or to conflict +in title with any Invariant Section. + +.PP +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +.SS 5. COMBINING DOCUMENTS +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections entitled "History" +in the various original documents, forming one section entitled +"History"; likewise combine any sections entitled "Acknowledgements", +and any sections entitled "Dedications". You must delete all sections +entitled "Endorsements." + +.SS 6. COLLECTIONS OF DOCUMENTS +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +.SS 7. AGGREGATION WITH INDEPENDENT WORKS +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, does not as a whole count as a Modified Version +of the Document, provided no compilation copyright is claimed for the +compilation. Such a compilation is called an "aggregate", and this +License does not apply to the other self-contained works thus compiled +with the Document, on account of their being thus compiled, if they +are not themselves derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one quarter +of the entire aggregate, the Document's Cover Texts may be placed on +covers that surround only the Document within the aggregate. +Otherwise they must appear on covers around the whole aggregate. + +.SS 8. TRANSLATION +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License provided that you also include the +original English version of this License. In case of a disagreement +between the translation and the original English version of this +License, the original English version will prevail. + +.SS 9. TERMINATION +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +.SS 10. FUTURE REVISIONS OF THIS LICENSE +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + +.SS ADDENDUM: How to use this License for your documents +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +.IP "" 3 +Copyright (c) YEAR YOUR NAME. + +Permission is granted to copy, distribute and/or modify this document under the +terms of the GNU Free Documentation License, Version 1.1 or any later version +published by the Free Software Foundation;\ \ with the Invariant Sections being +LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the +Back-Cover Texts being LIST. +A copy of the license is included in the section entitled "GNU +Free Documentation License". + +If you have no Invariant Sections, write "with no Invariant Sections" +instead of saying which ones are invariant. If you have no +Front-Cover Texts, write "no Front-Cover Texts" instead of +"Front-Cover Texts being LIST"; likewise for Back-Cover Texts. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +.\" ---------------------------------------------------------------------- +.SH \fRend of\fB GNU Free Documentation License +.IP "" +.PP +.. +.\" end: gfdl license |||||||||||||||||||||||||||||||||||||||||||||||| +.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/top.c b/top.c new file mode 100644 index 0000000..6d4a7cc --- /dev/null +++ b/top.c @@ -0,0 +1,3411 @@ +/* top.c - Source file: show Linux processes */ +/* + * Copyright (c) 2002, by: James C. Warner + * All rights reserved. 8921 Hilloway Road + * Eden Prairie, Minnesota 55347 USA + * + * + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * For their contributions to this program, the author wishes to thank: + * Albert D. Cahalan, + * Craig Small, + * + * Changes by Albert Cahalan, 2002-2004. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Foul POS defines all sorts of stuff... +#include +#undef tab + +#include +#include +#include +#include + +#include "proc/devname.h" +#include "proc/wchan.h" +#include "proc/procps.h" +#include "proc/readproc.h" +#include "proc/escape.h" +#include "proc/sig.h" +#include "proc/sysinfo.h" +#include "proc/version.h" +#include "proc/whattime.h" + +#include "top.h" + +/*###### Miscellaneous global stuff ####################################*/ + + /* The original and new terminal attributes */ +static struct termios Savedtty, + Rawtty; +static int Ttychanged = 0; + + /* Program name used in error messages and local 'rc' file name */ +static char *Myname; + + /* Name of user config file (dynamically constructed) and our + 'Current' rcfile contents, initialized with defaults but may be + overridden with the local rcfile (old or new-style) values */ +static char Rc_name [OURPATHSZ]; +static RCF_t Rc = DEF_RCFILE; + + /* The run-time acquired page size */ +static unsigned Page_size; +static unsigned page_to_kb_shift; + + /* SMP Irix/Solaris mode */ +static int Cpu_tot; +static double pcpu_max_value; // usually 99.9, for %CPU display + /* assume no IO-wait stats, overridden if linux 2.5.41 */ +static const char *States_fmts = STATES_line2x4; + + /* Specific process id monitoring support */ +static pid_t Monpids [MONPIDMAX] = { 0 }; +static int Monpidsidx = 0; + + /* A postponed error message */ +static char Msg_delayed [SMLBUFSIZ]; +static int Msg_awaiting = 0; + +// This is the select() timeout. Clear it in sig handlers to avoid a race. +// (signal happens just as we are about to select() and thus does not +// break us out of the select(), causing us to delay until timeout) +static volatile struct timeval tv; +#define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0); + + /* Configurable Display support ##################################*/ + + /* Current screen dimensions. + note: the number of processes displayed is tracked on a per window + basis (see the WIN_t). Max_lines is the total number of + screen rows after deducting summary information overhead. */ + /* Current terminal screen size. */ +static int Screen_cols, Screen_rows, Max_lines; + +// set to 1 if writing to the last column would be troublesome +// (we don't distinguish the lowermost row from the other rows) +static int avoid_last_column; + + /* This is really the number of lines needed to display the summary + information (0 - nn), but is used as the relative row where we + stick the cursor between frames. */ +static int Msg_row; + + /* Global/Non-windows mode stuff that is NOT persistent */ +static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise + PSDBopen = 0, // set to '1' if psdb opened (now postponed) + Batch = 0, // batch mode, collect no input, dumb output + Loops = -1, // number of iterations, -1 loops forever + Secure_mode = 0; // set if some functionality restricted + + /* Some cap's stuff to reduce runtime calls -- + to accomodate 'Batch' mode, they begin life as empty strings */ +static char Cap_clr_eol [CAPBUFSIZ], + Cap_clr_eos [CAPBUFSIZ], + Cap_clr_scr [CAPBUFSIZ], + Cap_rmam [CAPBUFSIZ], + Cap_smam [CAPBUFSIZ], + Cap_curs_norm [CAPBUFSIZ], + Cap_curs_huge [CAPBUFSIZ], + Cap_home [CAPBUFSIZ], + Cap_norm [CAPBUFSIZ], + Cap_reverse [CAPBUFSIZ], + Caps_off [CAPBUFSIZ]; +static int Cap_can_goto = 0; + + /* Some optimization stuff, to reduce output demands... + The Pseudo_ guys are managed by wins_resize and frame_make. They + are exploited in a macro and represent 90% of our optimization. + The Stdout_buf is transparent to our code and regardless of whose + buffer is used, stdout is flushed at frame end or if interactive. */ +static char *Pseudo_scrn; +static int Pseudo_row, Pseudo_cols, Pseudo_size; +#ifndef STDOUT_IOLBF + // less than stdout's normal buffer but with luck mostly '\n' anyway +static char Stdout_buf[2048]; +#endif + + + /* ////////////////////////////////////////////////////////////// */ + /* Special Section: multiple windows/field groups ---------------*/ + + /* The pointers to our four WIN_t's, and which of those is considered + the 'current' window (ie. which window is associated with any summ + info displayed and to which window commands are directed) */ +static WIN_t Winstk [GROUPSMAX], + *Curwin; + + /* Frame oriented stuff that can't remain local to any 1 function + and/or that would be too cumbersome managed as parms, + and/or that are simply more efficiently handled as globals + (first 2 persist beyond a single frame, changed infrequently) */ +static int Frames_libflags; // PROC_FILLxxx flags (0 = need new) +//atic int Frames_maxcmdln; // the largest from the 4 windows +static unsigned Frame_maxtask; // last known number of active tasks + // ie. current 'size' of proc table +static unsigned Frame_running, // state categories for this frame + Frame_sleepin, + Frame_stopped, + Frame_zombied; +static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu' +static int Frame_srtflg, // the subject window's sort direction + Frame_ctimes, // the subject window's ctimes flag + Frame_cmdlin; // the subject window's cmdlin flag + /* ////////////////////////////////////////////////////////////// */ + + +/*###### Sort callbacks ################################################*/ + + /* + * These happen to be coded in the same order as the enum 'pflag' + * values. Note that 2 of these routines serve double duty -- + * 2 columns each. + */ + +SCB_NUMx(P_PID, XXXID) +SCB_NUMx(P_PPD, ppid) +SCB_STRx(P_URR, ruser) +SCB_NUMx(P_UID, euid) +SCB_STRx(P_URE, euser) +SCB_STRx(P_GRP, egroup) +SCB_NUMx(P_TTY, tty) +SCB_NUMx(P_PRI, priority) +SCB_NUMx(P_NCE, nice) +SCB_NUMx(P_CPN, processor) +SCB_NUM1(P_CPU, pcpu) + // also serves P_TM2 ! +static int sort_P_TME (const proc_t **P, const proc_t **Q) +{ + if (Frame_ctimes) { + if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) + < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return SORT_lt; + if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) + > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return SORT_gt; + } else { + if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime)) + return SORT_lt; + if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime)) + return SORT_gt; + } + return SORT_eq; +} + +SCB_NUM1(P_VRT, size) +SCB_NUM2(P_SWP, size, resident) +SCB_NUM1(P_RES, resident) // also serves P_MEM ! +SCB_NUM1(P_COD, trs) +SCB_NUM1(P_DAT, drs) +SCB_NUM1(P_SHR, share) +SCB_NUM1(P_FLT, maj_flt) +SCB_NUM1(P_DRT, dt) +SCB_NUMx(P_STA, state) + +static int sort_P_CMD (const proc_t **P, const proc_t **Q) +{ + /* if a process doesn't have a cmdline, we'll consider it a kernel thread + -- since displayed tasks are given special treatment, we must too */ + if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) { + if (!(*Q)->cmdline) return Frame_srtflg * -1; + if (!(*P)->cmdline) return Frame_srtflg; + return Frame_srtflg * + strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln); + } + // this part also handles the compare if both are kernel threads + return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd); +} + +SCB_NUM1(P_WCH, wchan) +SCB_NUM1(P_FLG, flags) + + /* ///////////////////////////////// special sort for prochlp() ! */ +static int sort_HST_t (const HST_t *P, const HST_t *Q) +{ + return P->pid - Q->pid; +} + + +/*###### Tiny useful routine(s) ########################################*/ + + /* + * This routine isolates ALL user INPUT and ensures that we + * wont be mixing I/O from stdio and low-level read() requests */ +static int chin (int ech, char *buf, unsigned cnt) +{ + int rc; + + fflush(stdout); + if (!ech) + rc = read(STDIN_FILENO, buf, cnt); + else { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + rc = read(STDIN_FILENO, buf, cnt); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); + } + // may be the beginning of a lengthy escape sequence + tcflush(STDIN_FILENO, TCIFLUSH); + return rc; // note: we do NOT produce a vaid 'string' +} + + +// This routine simply formats whatever the caller wants and +// returns a pointer to the resulting 'const char' string... +static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2))); +static const char *fmtmk (const char *fmts, ...) +{ + static char buf[BIGBUFSIZ]; // with help stuff, our buffer + va_list va; // requirements exceed 1k + + va_start(va, fmts); + vsnprintf(buf, sizeof(buf), fmts, va); + va_end(va); + return (const char *)buf; +} + + +// This guy is just our way of avoiding the overhead of the standard +// strcat function (should the caller choose to participate) +static inline char *scat (char *restrict dst, const char *restrict src) +{ + while (*dst) dst++; + while ((*(dst++) = *(src++))); + return --dst; +} + + +// Trim the rc file lines and any 'open_psdb_message' result which arrives +// with an inappropriate newline (thanks to 'sysmap_mmap') +static char *strim_0 (char *str) +{ + static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape + char *p; + + if ((p = strpbrk(str, ws))) *p = 0; + return str; +} + + +// This guy just facilitates Batch and protects against dumb ttys +// -- we'd 'inline' him but he's only called twice per frame, +// yet used in many other locations. +static const char *tg2 (int x, int y) +{ + return Cap_can_goto ? tgoto(cursor_address, x, y) : ""; +} + + +/*###### Exit/Interrput routines #######################################*/ + +// The usual program end -- called only by functions in this section. +static void bye_bye (FILE *fp, int eno, const char *str) NORETURN; +static void bye_bye (FILE *fp, int eno, const char *str) +{ + if (!Batch) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + putp(tg2(0, Screen_rows)); + putp(Cap_curs_norm); + putp(Cap_smam); + putp("\n"); + fflush(stdout); + +//#define ATEOJ_REPORT +#ifdef ATEOJ_REPORT + + fprintf(fp, + "\n\tTerminal: %s" + "\n\t device = %s, ncurses = v%s" + "\n\t max_colors = %d, max_pairs = %d" + "\n\t Cap_can_goto = %s" + "\n\t Screen_cols = %d, Screen_rows = %d" + "\n\t Max_lines = %d, most recent Pseudo_size = %d" + "\n" +#ifdef PRETENDNOCAP + , "dumb" +#else + , termname() +#endif + , ttyname(STDOUT_FILENO), NCURSES_VERSION + , max_colors, max_pairs + , Cap_can_goto ? "yes" : "No!" + , Screen_cols, Screen_rows + , Max_lines, Pseudo_size + ); + + fprintf(fp, +#ifndef STDOUT_IOLBF + "\n\t Stdout_buf = %d, BUFSIZ = %u" +#endif + "\n\tWindows and Curwin->" + "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d" + "\n\t rc.winname = %s, grpname = %s" + "\n\t rc.winflags = %08x, maxpflgs = %d" + "\n\t rc.fieldscur = %s" + "\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d" + "\n\t rc.sortindx = %d" + "\n" +#ifndef STDOUT_IOLBF + , sizeof(Stdout_buf), (unsigned)BUFSIZ +#endif + , sizeof(WIN_t), GROUPSMAX + , Curwin->rc.winname, Curwin->grpname + , Curwin->rc.winflags, Curwin->maxpflgs + , Curwin->rc.fieldscur + , Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln + , Curwin->rc.sortindx + ); + + fprintf(fp, + "\n\tProgram" + "\n\t Linux version = %u.%u.%u, %s" + "\n\t Hertz = %u (%u bytes, %u-bit time)" + "\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u" + "\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)" + "\n" + , LINUX_VERSION_MAJOR(linux_version_code) + , LINUX_VERSION_MINOR(linux_version_code) + , LINUX_VERSION_PATCH(linux_version_code) + , procps_version + , (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8 + , Page_size, Cpu_tot, sizeof(proc_t) + , sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t) + ); + + +#endif + + if (str) fputs(str, fp); + exit(eno); +} + + + /* + * Normal end of execution. + * catches: + * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */ +// FIXME: can't do this shit in a signal handler +static void end_pgm (int sig) NORETURN; +static void end_pgm (int sig) +{ + if(sig) + sig |= 0x80; // for a proper process exit code + bye_bye(stdout, sig, NULL); +} + + + /* + * Standard error handler to normalize the look of all err o/p */ +static void std_err (const char *str) NORETURN; +static void std_err (const char *str) +{ + static char buf[SMLBUFSIZ]; + + fflush(stdout); + /* we'll use our own buffer so callers can still use fmtmk() and, yes the + leading tab is not the standard convention, but the standard is wrong + -- OUR msg won't get lost in screen clutter, like so many others! */ + snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str); + if (!Ttychanged) { + fprintf(stderr, "%s\n", buf); + exit(1); + } + /* not to worry, he'll change our exit code to 1 due to 'buf' */ + bye_bye(stderr, 1, buf); +} + + + /* + * Standard out handler */ +static void std_out (const char *str) NORETURN; +static void std_out (const char *str) +{ + static char buf[SMLBUFSIZ]; + + fflush(stdout); + /* we'll use our own buffer so callers can still use fmtmk() and, yes the + leading tab is not the standard convention, but the standard is wrong + -- OUR msg won't get lost in screen clutter, like so many others! */ + snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str); + if (!Ttychanged) { + fprintf(stdout, "%s\n", buf); + exit(0); + } + bye_bye(stdout, 0, buf); +} + + + /* + * Suspend ourself. + * catches: + * SIGTSTP, SIGTTIN and SIGTTOU */ +// FIXME: can't do this shit in a signal handler! +static void suspend (int dont_care_sig) +{ + (void)dont_care_sig; + /* reset terminal */ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + putp(tg2(0, Screen_rows)); + putp(Cap_curs_norm); + putp(Cap_smam); + putp("\n"); + fflush(stdout); + raise(SIGSTOP); + /* later, after SIGCONT... */ + ZAP_TIMEOUT + if (!Batch) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); + putp(Cap_clr_scr); + putp(Cap_rmam); +} + + +/*###### Misc Color/Display support ####################################*/ + + /* macro to test if a basic (non-color) capability is valid + thanks: Floyd Davidson */ +#define tIF(s) s ? s : "" +#define CAPCOPY(dst,src) src && strcpy(dst,src) + + /* + * Make the appropriate caps/color strings and set some + * lengths which are used to distinguish twix the displayed + * columns and an actual printed row! + * note: we avoid the use of background color so as to maximize + * compatibility with the user's xterm settings */ +static void capsmk (WIN_t *q) +{ + static int capsdone = 0; + + // we must NOT disturb our 'empty' terminfo strings! + if (Batch) return; + + // these are the unchangeable puppies, so we only do 'em once + if (!capsdone) { + CAPCOPY(Cap_clr_eol, clr_eol); + CAPCOPY(Cap_clr_eos, clr_eos); + CAPCOPY(Cap_clr_scr, clear_screen); + + if (!eat_newline_glitch) { // we like the eat_newline_glitch + CAPCOPY(Cap_rmam, exit_am_mode); + CAPCOPY(Cap_smam, enter_am_mode); + if (!*Cap_rmam || !*Cap_smam) { // need both + *Cap_rmam = '\0'; + *Cap_smam = '\0'; + if (auto_right_margin) { + avoid_last_column = 1; + } + } + } + + CAPCOPY(Cap_curs_huge, cursor_visible); + CAPCOPY(Cap_curs_norm, cursor_normal); + CAPCOPY(Cap_home, cursor_home); + CAPCOPY(Cap_norm, exit_attribute_mode); + CAPCOPY(Cap_reverse, enter_reverse_mode); + + snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair)); + if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1; + capsdone = 1; + } + /* the key to NO run-time costs for configurable colors -- we spend a + little time with the user now setting up our terminfo strings, and + the job's done until he/she/it has a change-of-heart */ + strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode)); + if (CHKw(q, Show_COLORS) && max_colors > 0) { + strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr)); + snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s" + , tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse); + snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s" + , tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold); + snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s" + , tparm(set_a_foreground, q->rc.headclr), Cap_reverse); + snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s" + , Caps_off, tparm(set_a_foreground, q->rc.taskclr)); + } else { + q->capclr_sum[0] = '\0'; + strcpy(q->capclr_msg, Cap_reverse); + strcpy(q->capclr_pmt, q->cap_bold); + strcpy(q->capclr_hdr, Cap_reverse); + strcpy(q->capclr_rownorm, Cap_norm); + } + // composite(s), so we do 'em outside and after the if + snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s" + , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse); + q->len_rownorm = strlen(q->capclr_rownorm); + q->len_rowhigh = strlen(q->capclr_rowhigh); + +#undef tIF +} + + +// Show an error, but not right now. +// Due to the postponed opening of ksym, using open_psdb_message, +// if P_WCH had been selected and the program is restarted, the +// message would otherwise be displayed prematurely. +static void msg_save (const char *fmts, ...) __attribute__((format(printf,1,2))); +static void msg_save (const char *fmts, ...) +{ + char tmp[SMLBUFSIZ]; + va_list va; + + va_start(va, fmts); + vsnprintf(tmp, sizeof(tmp), fmts, va); + va_end(va); + /* we'll add some extra attention grabbers to whatever this is */ + snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim_0(tmp)); + Msg_awaiting = 1; +} + + + /* + * Show an error message (caller may include a '\a' for sound) */ +static void show_msg (const char *str) +{ + PUTT("%s%s %s %s%s", + tg2(0, Msg_row), + Curwin->capclr_msg, + str, + Caps_off, + Cap_clr_eol + ); + fflush(stdout); + sleep(MSG_SLEEP); + Msg_awaiting = 0; +} + + + /* + * Show an input prompt + larger cursor */ +static void show_pmt (const char *str) +{ + PUTT("%s%s%s: %s%s", + tg2(0, Msg_row), + Curwin->capclr_pmt, + str, + Cap_curs_huge, + Caps_off + ); + fflush(stdout); +} + + + /* + * Show lines with specially formatted elements, but only output + * what will fit within the current screen width. + * Our special formatting consists of: + * "some text <_delimiter_> some more text <_delimiter_>...\n" + * Where <_delimiter_> is a single byte in the range of: + * \01 through \10 (in decimalizee, 1 - 8) + * and is used to select an 'attribute' from a capabilities table + * which is then applied to the *preceding* substring. + * Once recognized, the delimiter is replaced with a null character + * and viola, we've got a substring ready to output! Strings or + * substrings without delimiters will receive the Cap_norm attribute. + * + * Caution: + * This routine treats all non-delimiter bytes as displayable + * data subject to our screen width marching orders. If callers + * embed non-display data like tabs or terminfo strings in our + * glob, a line will truncate incorrectly at best. Worse case + * would be truncation of an embedded tty escape sequence. + * + * Tabs must always be avoided or our efforts are wasted and + * lines will wrap. To lessen but not eliminate the risk of + * terminfo string truncation, such non-display stuff should + * be placed at the beginning of a "short" line. + * (and as for tabs, gimme 1 more color then no worries, mate) */ +static void show_special (int interact, const char *glob) +{ /* note: the following is for documentation only, + the real captab is now found in a group's WIN_t ! + +------------------------------------------------------+ + | char *captab[] = { : Cap's/Delim's | + | Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 | + | Sum_color, = \03 | + | Msg_color, Pmt_color, = \04, \05 | + | Hdr_color, = \06 | + | Row_color_high, = \07 | + | Row_color_norm }; = \10 [octal!] | + +------------------------------------------------------+ */ + char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ]; + char *rp, *cap, *lin_end, *sub_beg, *sub_end; + int room; + + /* handle multiple lines passed in a bunch */ + while ((lin_end = strchr(glob, '\n'))) { + + /* create a local copy we can extend and otherwise abuse */ + size_t amt = lin_end - glob; + if(amt > sizeof lin - 1) + amt = sizeof lin - 1; // shit happens + memcpy(lin, glob, amt); + /* zero terminate this part and prepare to parse substrings */ + lin[amt] = '\0'; + room = Screen_cols; + sub_beg = sub_end = lin; + *(rp = row) = '\0'; + + while (*sub_beg) { + switch (*sub_end) { + case 0: /* no end delim, captab makes normal */ + *(sub_end + 1) = '\0'; /* extend str end, then fall through */ + case 1 ... 8: + cap = Curwin->captab[(int)*sub_end]; + *sub_end = '\0'; + snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off); + amt = strlen(tmp); + if(rp - row + amt + 1 > sizeof row) + goto overflow; // shit happens + rp = scat(rp, tmp); + room -= (sub_end - sub_beg); + sub_beg = ++sub_end; + break; + default: /* nothin' special, just text */ + ++sub_end; + } + if (unlikely(0 >= room)) break; /* skip substrings that won't fit */ + } +overflow: + if (interact) PUTT("%s%s\n", row, Cap_clr_eol); + else PUFF("%s%s\n", row, Cap_clr_eol); + glob = ++lin_end; /* point to next line (maybe) */ + } /* end: while 'lines' */ + + /* If there's anything left in the glob (by virtue of no trailing '\n'), + it probably means caller wants to retain cursor position on this final + line. That, in turn, means we're interactive and so we'll just do our + 'fit-to-screen' thingy... */ + if (*glob) PUTT("%.*s", Screen_cols, glob); +} + + +/*###### Small Utility routines ########################################*/ + +// Get a string from the user +static char *ask4str (const char *prompt) +{ + static char buf[GETBUFSIZ]; + + show_pmt(prompt); + memset(buf, '\0', sizeof(buf)); + chin(1, buf, sizeof(buf) - 1); + putp(Cap_curs_norm); + return strim_0(buf); +} + + +// Get a float from the user +static float get_float (const char *prompt) +{ + char *line; + float f; + + if (!(*(line = ask4str(prompt)))) return -1; + // note: we're not allowing negative floats + if (strcspn(line, ",.1234567890")) { + show_msg("\aNot valid"); + return -1; + } + sscanf(line, "%f", &f); + return f; +} + + +// Get an integer from the user +static int get_int (const char *prompt) +{ + char *line; + int n; + + if (!(*(line = ask4str(prompt)))) return -1; + // note: we've got to allow negative ints (renice) + if (strcspn(line, "-1234567890")) { + show_msg("\aNot valid"); + return -1; + } + sscanf(line, "%d", &n); + return n; +} + + + /* + * Do some scaling stuff. + * We'll interpret 'num' as one of the following types and + * try to format it to fit 'width'. + * SK_no (0) it's a byte count + * SK_Kb (1) it's kilobytes + * SK_Mb (2) it's megabytes + * SK_Gb (3) it's gigabytes + * SK_Tb (4) it's terabytes */ +static const char *scale_num (unsigned long num, const int width, const unsigned type) +{ + /* kilobytes, megabytes, gigabytes, terabytes, duh! */ + static double scale[] = { 1024.0, 1024.0*1024, 1024.0*1024*1024, 1024.0*1024*1024*1024, 0 }; + /* kilo, mega, giga, tera, none */ +#ifdef CASEUP_SCALE + static char nextup[] = { 'K', 'M', 'G', 'T', 0 }; +#else + static char nextup[] = { 'k', 'm', 'g', 't', 0 }; +#endif + static char buf[TNYBUFSIZ]; + double *dp; + char *up; + + /* try an unscaled version first... */ + if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf; + + /* now try successively higher types until it fits */ + for (up = nextup + type, dp = scale; *dp; ++dp, ++up) { + /* the most accurate version */ + if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up)) + return buf; + /* the integer version */ + if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up)) + return buf; + } + /* well shoot, this outta' fit... */ + return "?"; +} + + + /* + * Do some scaling stuff. + * format 'tics' to fit 'width'. */ +static const char *scale_tics (TIC_t tics, const int width) +{ +#ifdef CASEUP_SCALE +#define HH "%uH" +#define DD "%uD" +#define WW "%uW" +#else +#define HH "%uh" +#define DD "%ud" +#define WW "%uw" +#endif + static char buf[TNYBUFSIZ]; + unsigned long nt; // narrow time, for speed on 32-bit + unsigned cc; // centiseconds + unsigned nn; // multi-purpose whatever + + nt = (tics * 100ull) / Hertz; + cc = nt % 100; // centiseconds past second + nt /= 100; // total seconds + nn = nt % 60; // seconds past the minute + nt /= 60; // total minutes + if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc)) + return buf; + if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn)) + return buf; + nn = nt % 60; // minutes past the hour + nt /= 60; // total hours + if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn)) + return buf; + nn = nt; // now also hours + if (width >= snprintf(buf, sizeof buf, HH, nn)) + return buf; + nn /= 24; // now days + if (width >= snprintf(buf, sizeof buf, DD, nn)) + return buf; + nn /= 7; // now weeks + if (width >= snprintf(buf, sizeof buf, WW, nn)) + return buf; + // well shoot, this outta' fit... + return "?"; + +#undef HH +#undef DD +#undef WW +} + +#include + +static int selection_type; +static uid_t selection_uid; + +// FIXME: this is "temporary" code we hope +static int good_uid(const proc_t *restrict const pp){ + switch(selection_type){ + case 'p': + return 1; + case 0: + return 1; + case 'U': + if (pp->ruid == selection_uid) return 1; + if (pp->suid == selection_uid) return 1; + if (pp->fuid == selection_uid) return 1; + // FALLTHROUGH + case 'u': + if (pp->euid == selection_uid) return 1; + // FALLTHROUGH + default: + ; // don't know what it is; find bugs fast + } + return 0; +} + +// swiped from ps, and ought to be in libproc +static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){ + struct passwd *passwd_data; + char *endp; + unsigned long num; + static const char uidrange[] = "User ID out of range."; + static const char uidexist[] = "User name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + passwd_data = getpwnam(str); + if(!passwd_data) return uidexist; + num = passwd_data->pw_uid; + } + if(num > 0xfffffffeUL) return uidrange; + *ret = num; + return 0; +} + + +/*###### Library Alternatives ##########################################*/ + + /* + * Handle our own memory stuff without the risk of leaving the + * user's terminal in an ugly state should things go sour. */ + +static void *alloc_c (unsigned numb) MALLOC; +static void *alloc_c (unsigned numb) +{ + void * p; + + if (!numb) ++numb; + if (!(p = calloc(1, numb))) + std_err("failed memory allocate"); + return p; +} + +static void *alloc_r (void *q, unsigned numb) MALLOC; +static void *alloc_r (void *q, unsigned numb) +{ + void *p; + + if (!numb) ++numb; + if (!(p = realloc(q, numb))) + std_err("failed memory allocate"); + return p; +} + + + /* + * This guy's modeled on libproc's 'five_cpu_numbers' function except + * we preserve all cpu data in our CPU_t array which is organized + * as follows: + * cpus[0] thru cpus[n] == tics for each separate cpu + * cpus[Cpu_tot] == tics from the 1st /proc/stat line */ +static CPU_t *cpus_refresh (CPU_t *cpus) +{ + static FILE *fp = NULL; + int i; + int num; + // enough for a /proc/stat CPU line (not the intr line) + char buf[SMLBUFSIZ]; + + /* by opening this file once, we'll avoid the hit on minor page faults + (sorry Linux, but you'll have to close it for us) */ + if (!fp) { + if (!(fp = fopen("/proc/stat", "r"))) + std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno))); + /* note: we allocate one more CPU_t than Cpu_tot so that the last slot + can hold tics representing the /proc/stat cpu summary (the first + line read) -- that slot supports our View_CPUSUM toggle */ + cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t)); + } + rewind(fp); + fflush(fp); + + // first value the last slot with the cpu summary line + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); + cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number + cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number + cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number + num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", + &cpus[Cpu_tot].u, + &cpus[Cpu_tot].n, + &cpus[Cpu_tot].s, + &cpus[Cpu_tot].i, + &cpus[Cpu_tot].w, + &cpus[Cpu_tot].x, + &cpus[Cpu_tot].y, + &cpus[Cpu_tot].z + ); + if (num < 4) + std_err("failed /proc/stat read"); + + // and just in case we're 2.2.xx compiled without SMP support... + if (Cpu_tot == 1) { + cpus[1].id = 0; + memcpy(cpus, &cpus[1], sizeof(CPU_t)); + } + + // now value each separate cpu's tics + for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) { + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); + cpus[i].x = 0; // FIXME: can't tell by kernel version number + cpus[i].y = 0; // FIXME: can't tell by kernel version number + cpus[i].z = 0; // FIXME: can't tell by kernel version number + num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", + &cpus[i].id, + &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z + ); + if (num < 4) + std_err("failed /proc/stat read"); + } + return cpus; +} + + + /* + * Refresh procs *Helper* function to eliminate yet one more need + * to loop through our darn proc_t table. He's responsible for: + * 1) calculating the elapsed time since the previous frame + * 2) counting the number of tasks in each state (run, sleep, etc) + * 3) maintaining the HST_t's and priming the proc_t pcpu field + * 4) establishing the total number tasks for this frame */ +static void prochlp (proc_t *this) +{ + static HST_t *hist_sav = NULL; + static HST_t *hist_new = NULL; + static unsigned hist_siz = 0; // number of structs + static unsigned maxt_sav; // prior frame's max tasks + TIC_t tics; + + if (unlikely(!this)) { + static struct timeval oldtimev; + struct timeval timev; + struct timezone timez; + HST_t *hist_tmp; + float et; + + gettimeofday(&timev, &timez); + et = (timev.tv_sec - oldtimev.tv_sec) + + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0; + oldtimev.tv_sec = timev.tv_sec; + oldtimev.tv_usec = timev.tv_usec; + + // if in Solaris mode, adjust our scaling for all cpus + Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot)); + maxt_sav = Frame_maxtask; + Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0; + + // reuse memory each time around + hist_tmp = hist_sav; + hist_sav = hist_new; + hist_new = hist_tmp; + // prep for our binary search by sorting the last frame's HST_t's + qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t); + return; + } + + switch (this->state) { + case 'R': + Frame_running++; + break; + case 'S': + case 'D': + Frame_sleepin++; + break; + case 'T': + Frame_stopped++; + break; + case 'Z': + Frame_zombied++; + break; + } + + if (unlikely(Frame_maxtask+1 >= hist_siz)) { + hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25% + hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz); + hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz); + } + /* calculate time in this process; the sum of user time (utime) and + system time (stime) -- but PLEASE dont waste time and effort on + calcs and saves that go unused, like the old top! */ + hist_new[Frame_maxtask].pid = this->tid; + hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime); + +#if 0 +{ int i; + int lo = 0; + int hi = maxt_sav - 1; + + // find matching entry from previous frame and make ticks elapsed + while (lo <= hi) { + i = (lo + hi) / 2; + if (this->tid < hist_sav[i].pid) + hi = i - 1; + else if (likely(this->tid > hist_sav[i].pid)) + lo = i + 1; + else { + tics -= hist_sav[i].tics; + break; + } + } +} +#else +{ + HST_t tmp; + const HST_t *ptr; + tmp.pid = this->tid; + ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t); + if(ptr) tics -= ptr->tics; +} +#endif + + // we're just saving elapsed tics, to be converted into %cpu if + // this task wins it's displayable screen row lottery... */ + this->pcpu = tics; +// if (Frames_maxcmdln) { } + // shout this to the world with the final call (or us the next time in) + Frame_maxtask++; +} + + + /* + * This guy's modeled on libproc's 'readproctab' function except + * we reuse and extend any prior proc_t's. He's been customized + * for our specific needs and to avoid the use of */ +static proc_t **procs_refresh (proc_t **table, int flags) +{ +#define PTRsz sizeof(proc_t *) +#define ENTsz sizeof(proc_t) + static unsigned savmax = 0; // first time, Bypass: (i) + proc_t *ptsk = (proc_t *)-1; // first time, Force: (ii) + unsigned curmax = 0; // every time (jeeze) + PROCTAB* PT; + static int show_threads_was_enabled = 0; // optimization + + prochlp(NULL); // prep for a new frame + if (Monpidsidx) + PT = openproc(flags, Monpids); + else + PT = openproc(flags); + + // i) Allocated Chunks: *Existing* table; refresh + reuse + if (!(CHKw(Curwin, Show_THREADS))) { + while (curmax < savmax) { + if (table[curmax]->cmdline) { + unsigned idx; + // Skip if Show_THREADS was never enabled + if (show_threads_was_enabled) { + for (idx = curmax + 1; idx < savmax; idx++) { + if (table[idx]->cmdline == table[curmax]->cmdline) + table[idx]->cmdline = NULL; + } + } + free(*table[curmax]->cmdline); + table[curmax]->cmdline = NULL; + } + if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break; + prochlp(ptsk); // tally & complete this proc_t + ++curmax; + } + } + else { // show each thread in a process separately + while (curmax < savmax) { + proc_t *ttsk; + if (unlikely(!(ptsk = readproc(PT, NULL)))) break; + show_threads_was_enabled = 1; + while (curmax < savmax) { + unsigned idx; + if (table[curmax]->cmdline) { + // threads share the same cmdline storage. 'table' is + // qsort()ed, so must look through the rest of the table. + for (idx = curmax + 1; idx < savmax; idx++) { + if (table[idx]->cmdline == table[curmax]->cmdline) + table[idx]->cmdline = NULL; + } + free(*table[curmax]->cmdline); // only free once + table[curmax]->cmdline = NULL; + } + if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break; + prochlp(ttsk); + ++curmax; + } + free(ptsk); // readproc() proc_t not used + } + } + + // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill + if (!(CHKw(Curwin, Show_THREADS))) { + while (ptsk) { + // realloc as we go, keeping 'table' ahead of 'currmax++' + table = alloc_r(table, (curmax + 1) * PTRsz); + // here, readproc will allocate the underlying proc_t stg + if (likely(ptsk = readproc(PT, NULL))) { + prochlp(ptsk); // tally & complete this proc_t + table[curmax++] = ptsk; + } + } + } + else { // show each thread in a process separately + while (ptsk) { + proc_t *ttsk; + if (likely(ptsk = readproc(PT, NULL))) { + show_threads_was_enabled = 1; + while (1) { + table = alloc_r(table, (curmax + 1) * PTRsz); + if (!(ttsk = readtask(PT, ptsk, NULL))) break; + prochlp(ttsk); + table[curmax++] = ttsk; + } + free(ptsk); // readproc() proc_t not used + } + } + } + closeproc(PT); + + // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists + if (curmax >= savmax) { + table = alloc_r(table, (curmax + 1) * PTRsz); + // here, we must allocate the underlying proc_t stg ourselves + table[curmax] = alloc_c(ENTsz); + savmax = curmax + 1; + } + // this frame's end, but not necessarily end of allocated space + table[curmax]->tid = -1; + return table; + +#undef PTRsz +#undef ENTsz +} + +/*###### Field Table/RCfile compatability support ######################*/ + +// from either 'stat' or 'status' (preferred), via bits not otherwise used +#define L_EITHER PROC_SPARE_1 +// These are the Fieldstab.lflg values used here and in reframewins. +// (own identifiers as documentation and protection against changes) +#define L_stat PROC_FILLSTAT +#define L_statm PROC_FILLMEM +#define L_status PROC_FILLSTATUS +#define L_CMDLINE L_EITHER | PROC_FILLARG +#define L_EUSER PROC_FILLUSR +#define L_RUSER L_status | PROC_FILLUSR +#define L_GROUP L_status | PROC_FILLGRP +#define L_NONE 0 +// for reframewins and summary_show 1st pass +#define L_DEFAULT PROC_FILLSTAT + +// a temporary macro, soon to be undef'd... +#define SF(f) (QFP_t)sort_P_ ## f + + /* These are our gosh darn 'Fields' ! + They MUST be kept in sync with pflags !! + note: for integer data, the length modifiers found in .fmts may + NOT reflect the true field type found in proc_t -- this plus + a cast when/if displayed provides minimal width protection. */ +static FLD_t Fieldstab[] = { +/* .lflg anomolies: + P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid) + P_CPU, L_stat - never filled by libproc, but requires times (pcpu) + P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline) + L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit ! + keys head fmts width scale sort desc lflg + ------ ----------- ------- ------ ----- ----- ---------------------- -------- */ + { "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE }, + { "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER }, + { "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER }, + { "DdCc", " UID", " %4u", -1, -1, SF(UID), "User Id", L_NONE }, + { "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER }, + { "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP }, + { "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat }, + { "HhHh", " PR", " %3d", -1, -1, SF(PRI), "Priority", L_stat }, + { "IiIi", " NI", " %3d", -1, -1, SF(NCE), "Nice value", L_stat }, + { "JjYy", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat }, + { "KkEe", " %CPU", " %#4.1f", -1, -1, SF(CPU), "CPU usage", L_stat }, + { "LlWw", " TIME", " %6.6s", 6, -1, SF(TME), "CPU Time", L_stat }, + { "MmRr", " TIME+ ", " %9.9s", 9, -1, SF(TME), "CPU Time, hundredths", L_stat }, + { "NnFf", " %MEM", " %#4.1f", -1, -1, SF(RES), "Memory usage (RES)", L_statm }, + { "OoMm", " VIRT", " %5.5s", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", L_statm }, + { "PpOo", " SWAP", " %4.4s", 4, SK_Kb, SF(SWP), "Swapped size (kb)", L_statm }, + { "QqTt", " RES", " %4.4s", 4, SK_Kb, SF(RES), "Resident size (kb)", L_statm }, + { "RrKk", " CODE", " %4.4s", 4, SK_Kb, SF(COD), "Code size (kb)", L_statm }, + { "SsLl", " DATA", " %4.4s", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm }, + { "TtPp", " SHR", " %4.4s", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm }, + { "UuJj", " nFLT", " %4.4s", 4, SK_no, SF(FLT), "Page Fault count", L_stat }, + { "VvSs", " nDRT", " %4.4s", 4, SK_no, SF(DRT), "Dirty Pages count", L_statm }, + { "WwVv", " S", " %c", -1, -1, SF(STA), "Process Status", L_EITHER }, + // next entry's special: '.head' will be formatted using table entry's own + // '.fmts' plus runtime supplied conversion args! + { "XxXx", " COMMAND", " %-*.*s", -1, -1, SF(CMD), "Command name/line", L_EITHER }, + { "YyUu", " WCHAN ", " %-9.9s", -1, -1, SF(WCH), "Sleeping in Function", L_stat }, + // next entry's special: the 0's will be replaced with '.'! + { "ZzZz", " Flags ", " %08lx", -1, -1, SF(FLG), "Task Flags ", L_stat }, +#if 0 + { "..Qq", " A", " %4.4s", 4, SK_no, SF(PID), "Accessed Page count", L_stat }, + { "..Nn", " TRS", " %4.4s", 4, SK_Kb, SF(PID), "Code in memory (kb)", L_stat }, + { "..Rr", " WP", " %4.4s", 4, SK_no, SF(PID), "Unwritable Pages", L_stat }, + { "Jj[{", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat }, + { "..\\|"," Bad", " %2u", -1, -1, SF(CPN), "-- must ignore | --", 0 }, + { "..]}", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 }, + { "..^~", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 }, +#endif +}; +#undef SF + + + /* All right, those-that-follow -- Listen Up! + * For the above table keys and the following present/future rc file + * compatibility support, you have Mr. Albert D. Cahalan to thank. + * He must have been in a 'Christmas spirit'. Were it left to me, + * this top would never have gotten that close to the former top's + * crufty rcfile. Not only is it illogical, it's odoriferous ! + */ + + // used as 'to' and/or 'from' args in the ft_xxx utilities... +#define FT_NEW_fmt 0 +#define FT_OLD_fmt 2 + + +#if 0 + // convert, or 0 for failure +static int ft_cvt_char (const int fr, const int to, int c) { + int j = -1; + + while (++j < MAXTBL(Fieldstab)) { + if (c == Fieldstab[j].keys[fr]) return Fieldstab[j].keys[to]; + if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1]; + } + return 0; +} +#endif + + + // convert +static inline int ft_get_char (const int fr, int i) { + int c; + if (i < 0) return 0; + if (i >= MAXTBL(Fieldstab)) return 0; + c = Fieldstab[i].keys[fr]; + if (c == '.') c = 0; // '.' marks a bad entry + return c; +} + + +#if 0 + // convert, or -1 for failure +static int ft_get_idx (const int fr, int c) { + int j = -1; + + while (++j < MAXTBL(Fieldstab)) { + if (c == Fieldstab[j].keys[fr]) return j; + if (c == Fieldstab[j].keys[fr+1]) return j; + } + return -1; +} +#endif + + + // convert, or NULL for failure +static const FLD_t *ft_get_ptr (const int fr, int c) { + int j = -1; + + while (++j < MAXTBL(Fieldstab)) { + if (c == Fieldstab[j].keys[fr]) return Fieldstab+j; + if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j; + } + return NULL; +} + + +#if 0 + // convert, or NULL for failure +static const FLD_t *ft_idx_to_ptr (const int i) { + if (i < 0) return NULL; + if (i >= MAXTBL(Fieldstab)) return NULL; + return Fieldstab + i; +} + + + // convert, or -1 for failure +static int ft_ptr_to_idx (const FLD_t *p) { + int i; + if (p < Fieldstab) return -1; + i = p - Fieldstab; + if (i >= MAXTBL(Fieldstab)) return -1; + return i; +} +#endif + + +#if 0 +static void rc_bugless (const RCF_t *const rc) { + const RCW_t *w; + int i = 0; + + fprintf(stderr,"\n%d %d %f %d\n", + rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index + ); + while(i < 4) { + w = &rc->win[i++]; + fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n", + w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks, + w->summclr, w->msgsclr, w->headclr, w->taskclr + ); + } +} +#endif + + +// '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window. +// line 1: an eyecatcher, with a shameless advertisement +// line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin. +// For each of the 4 windows: +// line a: contains winname, fieldscur +// line b: contains winflags, sortindx, maxtasks +// line c: contains summclr, msgsclr, headclr, taskclr +// line d: if present, would crash procps-3.1.1 +static int rc_read_new (const char *const buf, RCF_t *rc) { + int i; + int cnt; + const char *cp; + + cp = strstr(buf, "\n\n" RCF_EYECATCHER); + if (!cp) return -1; + cp = strchr(cp + 2, '\n'); + if (!cp++) return -2; + + cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n", + &rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index + ); + if (cnt != 4) return -3; + cp = strchr(cp, '\n'); + if (!cp++) return -4; + + for (i = 0; i < GROUPSMAX; i++) { + RCW_t *ptr = &rc->win[i]; + cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur); + if (cnt != 2) return 5+100*i; // OK to have less than 4 windows + if (WINNAMSIZ <= strlen(ptr->winname)) return -6; + if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7; + cp = strchr(cp, '\n'); + if (!cp++) return -(8+100*i); + + cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n", + &ptr->winflags, &ptr->sortindx, &ptr->maxtasks + ); + if (cnt != 3) return -(9+100*i); + cp = strchr(cp, '\n'); + if (!cp++) return -(10+100*i); + + cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n", + &ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr + ); + if (cnt != 4) return -(11+100*i); + cp = strchr(cp, '\n'); + if (!cp++) return -(12+100*i); + while (*cp == '\t') { // skip unknown per-window settings + cp = strchr(cp, '\n'); + if (!cp++) return -(13+100*i); + } + } + return 13; +} + + + +static int rc_read_old (const char *const buf, RCF_t *rc) { + const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......"; + const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr"; + unsigned u; + const char *cp; + unsigned c_show = 0; + int badchar = 0; // allow a limited number of duplicates and junk + + char scoreboard[256]; + memset(scoreboard, '\0', sizeof scoreboard); + + cp = buf+2; // skip the "\n\n" we stuck at the beginning + u = 0; + for (;;) { + const char *tmp; + int c = *cp++; + if (u+1 >= sizeof rc->win[0].fieldscur) return -1; + if (c == '\0') return -2; + if (c == '\n') break; + if (c & ~0x7f) return -3; + if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden + if (scoreboard[c|0xe0u]) badchar++; // duplicates not allowed + scoreboard[c|0xe0u]++; + tmp = strchr(old,c); + if (!tmp) continue; + c = *((tmp-old)+std); + if (c == '.') continue; + if (scoreboard[c&0x1fu]) badchar++; // duplicates not allowed + scoreboard[c&0x1fu]++; + rc->win[0].fieldscur[u++] = c; + } + rc->win[0].fieldscur[u++] = '\0'; + if (u < 21) return -6; // catch junk, not good files (had 23 chars in one) + if (u > 33) return -7; // catch junk, not good files (had 29 chars in one) +// fprintf(stderr, "badchar: %d\n", badchar); sleep(2); + if (badchar > 8) return -8; // too much junk + if (!c_show) return -9; // nothing was shown + + // rest of file is optional, but better look right if it exists + if (!*cp) return 12; + if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid? + rc->delay_time = *cp - '0'; + + memset(scoreboard, '\0', sizeof(scoreboard)); + for (;;) { + int c = *++cp & 0xffu; // protect scoreboard[] from negative char + if (!c) return -14; // not OK to hit EOL w/o '\n' + if (c == '\n') break; + switch (c) { + case ' ': + case '.': + case '0' ... '9': + return -15; // not supposed to have digits here + +// case 's': // mostly for global rcfile +// rc->mode_secure = 1; +// break; + case 'S': + rc->win[0].winflags |= Show_CTIMES; + break; + case 'c': + rc->win[0].winflags |= Show_CMDLIN; + break; + case 'i': + rc->win[0].winflags &= ~Show_IDLEPS; + break; + case 'H': + rc->win[0].winflags |= Show_THREADS; + break; + case 'm': + rc->win[0].winflags &= ~View_MEMORY; + break; + case 'l': + rc->win[0].winflags &= ~View_LOADAV; + break; + case 't': + rc->win[0].winflags &= ~View_STATES; + break; + case 'I': + rc->mode_irixps = 0; + break; + + case 'M': + c = 0; // for scoreboard + rc->win[0].sortindx = P_MEM; + break; + case 'P': + c = 0; // for scoreboard + rc->win[0].sortindx = P_CPU; + break; + case 'A': // supposed to be start_time + c = 0; // for scoreboard + rc->win[0].sortindx = P_PID; + break; + case 'T': + c = 0; // for scoreboard + rc->win[0].sortindx = P_TM2; + break; + case 'N': + c = 0; // for scoreboard + rc->win[0].sortindx = P_PID; + break; + + default: + // just ignore it, except for the scoreboard of course + break; + } + if (scoreboard[c]) return -16; // duplicates not allowed + scoreboard[c] = 1; + } + return 17; +} + + +static void rc_write_new (FILE *fp) { + int i; + + fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n", + Myname + ); + fprintf(fp, RCF_DEPRECATED + "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n", + Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk) + ); + for (i = 0; i < GROUPSMAX; i++) { + char buf[40]; + char *cp = Winstk[i].rc.fieldscur; + int j = 0; + + while (j < 36) { + int c = *cp++ & 0xff; + switch (c) { + case '.': + case 1 ... ' ': + case 0x7f ... 0xff: + continue; // throw away junk (some of it) + default: + buf[j++] = c; // gets the '\0' too + } + if (!c) break; + } + fprintf(fp, "%s\tfieldscur=%s\n", + Winstk[i].rc.winname, buf + ); + fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n", + Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks + ); + fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n", + Winstk[i].rc.summclr, Winstk[i].rc.msgsclr, + Winstk[i].rc.headclr, Winstk[i].rc.taskclr + ); + } +} + + +static const char *rc_write_whatever (void) { + FILE *fp = fopen(Rc_name, "w"); + + if (!fp) return strerror(errno); + rc_write_new(fp); + fclose(fp); + return NULL; +} + + +/*###### Startup routines ##############################################*/ + +// No mater what *they* say, we handle the really really BIG and +// IMPORTANT stuff upon which all those lessor functions depend! +static void before (char *me) +{ + int i; + + /* setup our program name -- big! */ + Myname = strrchr(me, '/'); + if (Myname) ++Myname; else Myname = me; + + /* establish cpu particulars -- even bigger! */ + Cpu_tot = smp_num_cpus; + if (linux_version_code > LINUX_VERSION(2, 5, 41)) + States_fmts = STATES_line2x5; + if (linux_version_code >= LINUX_VERSION(2, 6, 0)) // grrr... only some 2.6.0-testX :-( + States_fmts = STATES_line2x6; + if (linux_version_code >= LINUX_VERSION(2, 6, 11)) + States_fmts = STATES_line2x7; + + /* get virtual page size -- nearing huge! */ + Page_size = getpagesize(); + i = Page_size; + while(i>1024){ + i >>= 1; + page_to_kb_shift++; + } + + pcpu_max_value = 99.9; + + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %1u"; + if(smp_num_cpus>9){ + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %2u"; + } + if(smp_num_cpus>99){ + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %3u"; + } + if(smp_num_cpus>999){ + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %4u"; + } + + { + static char pid_fmt[6]; + unsigned pid_digits = get_pid_digits(); + if(pid_digits<4) pid_digits=4; + snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits); + Fieldstab[P_PID].fmts = pid_fmt; + Fieldstab[P_PID].head = " PID" + 10 - pid_digits; + Fieldstab[P_PPD].fmts = pid_fmt; + Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits; + } +} + + +// Config file read *helper* function. +// Anything missing won't show as a choice in the field editor, +// so make sure there is exactly one of each letter. +// +// Due to Rik blindly accepting damem's broken patches, procps-2.0.1x +// has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too. +static void confighlp (char *fields) { + unsigned upper[PFLAGSSIZ]; + unsigned lower[PFLAGSSIZ]; + char c; + char *cp; + + memset(upper, '\0', sizeof upper); + memset(lower, '\0', sizeof lower); + + cp = fields; + for (;;) { + c = *cp++; + if (!c) break; + if(isupper(c)) upper[c&0x1f]++; + else lower[c&0x1f]++; + } + + c = 'a'; + while (c <= 'z') { + if (upper[c&0x1f] && lower[c&0x1f]) { + lower[c&0x1f] = 0; // got both, so wipe out unseen column + for (;;) { + cp = strchr(fields, c); + if (cp) memmove(cp, cp+1, strlen(cp)); + else break; + } + } + while (lower[c&0x1f] > 1) { // got too many a..z + lower[c&0x1f]--; + cp = strchr(fields, c); + memmove(cp, cp+1, strlen(cp)); + } + while (upper[c&0x1f] > 1) { // got too many A..Z + upper[c&0x1f]--; + cp = strchr(fields, toupper(c)); + memmove(cp, cp+1, strlen(cp)); + } + if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing + lower[c&0x1f]++; + memmove(fields+1, fields, strlen(fields)+1); + fields[0] = c; + } + c++; + } +} + + +// First attempt to read the /etc/rcfile which contains two lines +// consisting of the secure mode switch and an update interval. +// It's presence limits what ordinary users are allowed to do. +// (it's actually an old-style config file) +// +// Then build the local rcfile name and try to read a crufty old-top +// rcfile (whew, odoriferous), which may contain an embedded new-style +// rcfile. Whether embedded or standalone, new-style rcfile values +// will always override that crufty stuff! +// note: If running in secure mode via the /etc/rcfile, +// Delay_time will be ignored except for root. +static void configs_read (void) +{ + const RCF_t def_rcf = DEF_RCFILE; + char fbuf[MEDBUFSIZ]; + int i, fd; + RCF_t rcf; + float delay = Rc.delay_time; + + // read part of an old-style config in /etc/toprc + fd = open(SYS_RCFILESPEC, O_RDONLY); + if (fd > 0) { + ssize_t num; + num = read(fd, fbuf, sizeof(fbuf) - 1); + if (num > 0) { + const char *sec = strchr(fbuf, 's'); + const char *eol = strchr(fbuf, '\n'); + if (eol) { + const char *two = eol + 1; // line two + if (sec < eol) Secure_mode = !!sec; + eol = strchr(two, '\n'); + if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two); + } + } + close(fd); + } + + if (getenv("TOPRC")) { // should switch on Myname before documenting this? + // not the most optimal here... + snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC")); + } else { + snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew... + if (getenv("HOME")) + snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname); + } + + rcf = def_rcf; + fd = open(Rc_name, O_RDONLY); + if (fd > 0) { + ssize_t num; + num = read(fd, fbuf+2, sizeof(fbuf) -3); + if (num > 0) { + fbuf[0] = '\n'; + fbuf[1] = '\n'; + fbuf[num+2] = '\0'; +//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf)); +//sleep(2); + if (rc_read_new(fbuf, &rcf) < 0) { + rcf = def_rcf; // on failure, maybe mangled + if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf; + } + delay = rcf.delay_time; + } + close(fd); + } + + // update Rc defaults, establish a Curwin and fix up the window stack + Rc.mode_altscr = rcf.mode_altscr; + Rc.mode_irixps = rcf.mode_irixps; + if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0; + Curwin = &Winstk[rcf.win_index]; + for (i = 0; i < GROUPSMAX; i++) { + memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]); + confighlp(Winstk[i].rc.fieldscur); + } + + if(Rc.mode_irixps && smp_num_cpus>1){ + // good for 100 CPUs per process + pcpu_max_value = 9999.0; + Fieldstab[P_CPU].fmts = " %4.0f"; + } + + // lastly, establish the true runtime secure mode and delay time + if (!getuid()) Secure_mode = 0; + if (!Secure_mode) Rc.delay_time = delay; +} + + +// Parse command line arguments. +// Note: it's assumed that the rc file(s) have already been read +// and our job is to see if any of those options are to be +// overridden -- we'll force some on and negate others in our +// best effort to honor the loser's (oops, user's) wishes... +static void parse_args (char **args) +{ + /* differences between us and the former top: + -C (separate CPU states for SMP) is left to an rcfile + -p (pid monitoring) allows a comma delimited list + -q (zero delay) eliminated as redundant, incomplete and inappropriate + use: "nice -n-10 top -d0" to achieve what was only claimed + -c,i,S act as toggles (not 'on' switches) for enhanced user flexibility + . no deprecated/illegal use of 'breakargv:' with goto + . bunched args are actually handled properly and none are ignored + . we tolerate NO whitespace and NO switches -- maybe too tolerant? */ + static const char usage[] = + " -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]"; + float tmp_delay = MAXFLOAT; + char *p; + + while (*args) { + const char *cp = *(args++); + + while (*cp) { + switch (*cp) { + case '\0': + case '-': + break; + case 'b': + Batch = 1; + break; + case 'c': + TOGw(Curwin, Show_CMDLIN); + break; + case 'd': + if (cp[1]) ++cp; + else if (*args) cp = *args++; + else std_err("-d requires argument"); + /* a negative delay will be dealt with shortly... */ + if (sscanf(cp, "%f", &tmp_delay) != 1) + std_err(fmtmk("bad delay '%s'", cp)); + break; + case 'H': + TOGw(Curwin, Show_THREADS); + break; + case 'h': + case 'v': case 'V': + std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage)); + case 'i': + TOGw(Curwin, Show_IDLEPS); + Curwin->rc.maxtasks = 0; + break; + case 'n': + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-n requires argument"); + if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1) + std_err(fmtmk("bad iterations arg '%s'", cp)); + break; + case 'p': + do { + if (selection_type && selection_type != 'p') std_err("conflicting process selection"); + selection_type = 'p'; + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-p argument missing"); + if (Monpidsidx >= MONPIDMAX) + std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX)); + if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0) + std_err(fmtmk("bad pid '%s'", cp)); + if (!Monpids[Monpidsidx]) + Monpids[Monpidsidx] = getpid(); + Monpidsidx++; + if (!(p = strchr(cp, ','))) + break; + cp = p; + } while (*cp); + break; + case 's': + Secure_mode = 1; + break; + case 'S': + TOGw(Curwin, Show_CTIMES); + break; + case 'u': + do { + const char *errmsg; + if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection"); + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-u missing name"); + errmsg = parse_uid(cp, &selection_uid); + if (errmsg) std_err(errmsg); + selection_type = 'u'; + cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk + } while(0); + break; + case 'U': + do { + const char *errmsg; + if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection"); + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-u missing name"); + errmsg = parse_uid(cp, &selection_uid); + if (errmsg) std_err(errmsg); + selection_type = 'U'; + cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk + } while(0); + break; + default : + std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s" + , *cp, Myname, usage)); + + } /* end: switch (*cp) */ + + /* advance cp and jump over any numerical args used above */ + if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1; + } /* end: while (*cp) */ + } /* end: while (*args) */ + + /* fixup delay time, maybe... */ + if (MAXFLOAT != tmp_delay) { + if (Secure_mode || tmp_delay < 0) + msg_save("Delay time Not changed"); + else + Rc.delay_time = tmp_delay; + } +} + + + /* + * Set up the terminal attributes */ +static void whack_terminal (void) +{ + struct termios newtty; + + if (Batch) { + setupterm("dumb", STDOUT_FILENO, NULL); + return; + } + setupterm(NULL, STDOUT_FILENO, NULL); + if (tcgetattr(STDIN_FILENO, &Savedtty) == -1) + std_err("failed tty get"); + newtty = Savedtty; + newtty.c_lflag &= ~(ICANON | ECHO); + newtty.c_oflag &= ~(TAB3); + newtty.c_cc[VMIN] = 1; + newtty.c_cc[VTIME] = 0; + + Ttychanged = 1; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) { + putp(Cap_clr_scr); + std_err(fmtmk("failed tty set: %s", strerror(errno))); + } + tcgetattr(STDIN_FILENO, &Rawtty); +#ifndef STDOUT_IOLBF + // thanks anyway stdio, but we'll manage buffering at the frame level... + setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf)); +#endif + putp(Cap_clr_scr); + fflush(stdout); +} + + +/*###### Field Selection/Ordering routines #############################*/ + + +// Display each field represented in the Fields Table along with its +// description and mark (with a leading asterisk) fields associated +// with upper case letter(s) in the passed 'fields string'. +// +// After all fields have been displayed, some extra explanatory +// text may also be output +static void display_fields (const char *fields, const char *xtra) +{ +#define yRSVD 3 + const char *p; + int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD; + + /* we're relying on callers to first clear the screen and thus avoid screen + flicker if they're too lazy to handle their own asterisk (*) logic */ + putp(Curwin->cap_bold); + for (i = 0; fields[i]; ++i) { + const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]); + int b = isupper(fields[i]); + + if (!f) continue; // hey, should be std_err! + for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces + ; + PUTT("%s%s%c %c: %-10s = %s", + tg2((i / rmax) * cmax, (i % rmax) + yRSVD), + b ? Curwin->cap_bold : Cap_norm, + b ? '*' : ' ', + fields[i], + p, + f->desc + ); + } + if (xtra) { + putp(Curwin->capclr_rownorm); + while ((p = strchr(xtra, '\n'))) { + ++i; + PUTT("%s%.*s", + tg2((i / rmax) * cmax, (i % rmax) + yRSVD), + (int)(p - xtra), + xtra + ); + xtra = ++p; + } + } + putp(Caps_off); + +#undef yRSVD +} + + +// Change order of displayed fields. +static void fields_reorder (void) +{ + static const char prompt[] = + "Upper case letter moves field left, lower case right"; + char c, *p; + int i; + + putp(Cap_clr_scr); + putp(Cap_curs_huge); + for (;;) { + display_fields(Curwin->rc.fieldscur, FIELDS_xtra); + show_special(1, fmtmk(FIELDS_current + , Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt)); + chin(0, &c, 1); + if (!ft_get_ptr(FT_NEW_fmt, c)) break; + i = toupper(c) - 'A'; + if (((p = strchr(Curwin->rc.fieldscur, i + 'A'))) + || ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) { + if (isupper(c)) p--; + if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) { + c = p[0]; + p[0] = p[1]; + p[1] = c; + } + } + } + putp(Cap_curs_norm); +} + +// Select sort field. +static void fields_sort (void) +{ + static const char prompt[] = + "Select sort field via field letter, type any other key to return"; + char phoney[PFLAGSSIZ]; + char c, *p; + int i, x; + + strcpy(phoney, NUL_FIELDS); + x = i = Curwin->rc.sortindx; + putp(Cap_clr_scr); + putp(Cap_curs_huge); + for (;;) { + p = phoney + i; + *p = toupper(*p); + display_fields(phoney, SORT_xtra); + show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt)); + chin(0, &c, 1); + if (!ft_get_ptr(FT_NEW_fmt, c)) break; + i = toupper(c) - 'A'; + *p = tolower(*p); + x = i; + } + if ((p = strchr(Curwin->rc.fieldscur, x + 'a'))) + *p = x + 'A'; + Curwin->rc.sortindx = x; + putp(Cap_curs_norm); +} + + +// Toggle displayed fields. +static void fields_toggle (void) +{ + static const char prompt[] = + "Toggle fields via field letter, type any other key to return"; + char c, *p; + int i; + + putp(Cap_clr_scr); + putp(Cap_curs_huge); + for (;;) { + display_fields(Curwin->rc.fieldscur, FIELDS_xtra); + show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt)); + chin(0, &c, 1); + if (!ft_get_ptr(FT_NEW_fmt, c)) break; + i = toupper(c) - 'A'; + if ((p = strchr(Curwin->rc.fieldscur, i + 'A'))) + *p = i + 'a'; + else if ((p = strchr(Curwin->rc.fieldscur, i + 'a'))) + *p = i + 'A'; + } + putp(Cap_curs_norm); +} + +/*###### Windows/Field Groups support #################################*/ + +// For each of the four windows: +// 1) Set the number of fields/columns to display +// 2) Create the field columns heading +// 3) Set maximum cmdline length, if command lines are in use +// In the process, the required PROC_FILLxxx flags will be rebuilt! +static void reframewins (void) +{ + WIN_t *w; + char *s; + const char *h; + int i, needpsdb = 0; + +// Frames_libflags = 0; // should be called only when it's zero +// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible + w = Curwin; + do { + if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) { + // build window's procflags array and establish a tentative maxpflgs + for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) { + if (isupper(w->rc.fieldscur[i])) + w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A'; + } + + /* build a preliminary columns header not to exceed screen width + while accounting for a possible leading window number */ + *(s = w->columnhdr) = '\0'; + if (Rc.mode_altscr) s = scat(s, " "); + for (i = 0; i < w->maxpflgs; i++) { + h = Fieldstab[w->procflags[i]].head; + // oops, won't fit -- we're outta here... + if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break; + s = scat(s, h); + } + + // establish the final maxpflgs and prepare to grow the command column + // heading via maxcmdln - it may be a fib if P_CMD wasn't encountered, + // but that's ok because it won't be displayed anyway + w->maxpflgs = i; + w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head)); + + // finally, we can build the true run-time columns header, format the + // command column heading, if P_CMD is really being displayed, and + // rebuild the all-important PROC_FILLxxx flags that will be used + // until/if we're we're called again + *(s = w->columnhdr) = '\0'; +// if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum)); + for (i = 0; i < w->maxpflgs; i++) { + int advance = (i==0) && !Rc.mode_altscr; + h = Fieldstab[w->procflags[i]].head; + if (P_WCH == w->procflags[i]) needpsdb = 1; + if (P_CMD == w->procflags[i]) { + s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/ )); + if (CHKw(w, Show_CMDLIN)) { + Frames_libflags |= L_CMDLINE; +// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln; + } + } else + s = scat(s, h+advance); + Frames_libflags |= Fieldstab[w->procflags[i]].lflg; + } + if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0'; + } + if (Rc.mode_altscr) w = w->next; + } while (w != Curwin); + + // do we need the kernel symbol table (and is it already open?) + if (needpsdb) { + if (No_ksyms == -1) { + No_ksyms = 0; + if (open_psdb_message(NULL, msg_save)) + No_ksyms = 1; + else + PSDBopen = 1; + } + } + + if (selection_type=='U') Frames_libflags |= L_status; + + if (Frames_libflags & L_EITHER) { + Frames_libflags &= ~L_EITHER; + if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status; + } + if (!Frames_libflags) Frames_libflags = L_DEFAULT; + if (selection_type=='p') Frames_libflags |= PROC_PID; +} + + +// Value a window's name and make the associated group name. +static void win_names (WIN_t *q, const char *name) +{ + sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name); + sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name); +} + + +// Display a window/field group (ie. make it "current"). +static void win_select (char ch) +{ + static const char prompt[] = "Choose field group (1 - 4)"; + + /* if there's no ch, it means we're supporting the external interface, + so we must try to get our own darn ch by begging the user... */ + if (!ch) { + show_pmt(prompt); + chin(0, (char *)&ch, 1); + } + switch (ch) { + case 'a': /* we don't carry 'a' / 'w' in our */ + Curwin = Curwin->next; /* pmt - they're here for a good */ + break; /* friend of ours -- wins_colors. */ + case 'w': /* (however those letters work via */ + Curwin = Curwin->prev; /* the pmt too but gee, end-loser */ + break; /* should just press the darn key) */ + case '1': case '2': + case '3': case '4': + Curwin = &Winstk[ch - '1']; + break; + } +} + + +// Just warn the user when a command can't be honored. +static int win_warn (void) +{ + show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname)); + // we gotta' return false 'cause we're somewhat well known within + // macro society, by way of that sassy little tertiary operator... + return 0; +} + + +// Change colors *Helper* function to save/restore settings; +// ensure colors will show; and rebuild the terminfo strings. +static void winsclrhlp (WIN_t *q, int save) +{ + static int flgssav, summsav, msgssav, headsav, tasksav; + + if (save) { + flgssav = q->rc.winflags; summsav = q->rc.summclr; + msgssav = q->rc.msgsclr; headsav = q->rc.headclr; tasksav = q->rc.taskclr; + SETw(q, Show_COLORS); + } else { + q->rc.winflags = flgssav; q->rc.summclr = summsav; + q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav; + } + capsmk(q); +} + + +// Change colors used in display +static void wins_colors (void) +{ +#define kbdABORT 'q' +#define kbdAPPLY '\n' + int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr; + char ch, tgt = 'T'; + + if (0 >= max_colors) { + show_msg("\aNo colors to map!"); + return; + } + winsclrhlp(Curwin, 1); + putp(Cap_clr_scr); + putp(Cap_curs_huge); + + do { + putp(Cap_home); + /* this string is well above ISO C89's minimum requirements! */ + show_special( + 1, + fmtmk( + COLOR_help, + procps_version, + Curwin->grpname, + CHKw(Curwin, View_NOBOLD) ? "On" : "Off", + CHKw(Curwin, Show_COLORS) ? "On" : "Off", + CHKw(Curwin, Show_HIBOLD) ? "On" : "Off", + tgt, + clr, + Curwin->grpname + ) + ); + chin(0, &ch, 1); + switch (ch) { + case 'S': + pclr = &Curwin->rc.summclr; + clr = *pclr; + tgt = ch; + break; + case 'M': + pclr = &Curwin->rc.msgsclr; + clr = *pclr; + tgt = ch; + break; + case 'H': + pclr = &Curwin->rc.headclr; + clr = *pclr; + tgt = ch; + break; + case 'T': + pclr = &Curwin->rc.taskclr; + clr = *pclr; + tgt = ch; + break; + case '0' ... '7': + clr = ch - '0'; + *pclr = clr; + break; + case 'B': + TOGw(Curwin, View_NOBOLD); + break; + case 'b': + TOGw(Curwin, Show_HIBOLD); + break; + case 'z': + TOGw(Curwin, Show_COLORS); + break; + case 'a': + case 'w': + win_select(ch); + winsclrhlp(Curwin, 1); + clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr; + tgt = 'T'; + break; + } + capsmk(Curwin); + } while (kbdAPPLY != ch && kbdABORT != ch); + + if (kbdABORT == ch) + winsclrhlp(Curwin, 0); + putp(Cap_curs_norm); + +#undef kbdABORT +#undef kbdAPPLY +} + + +// Manipulate flag(s) for all our windows. +static void wins_reflag (int what, int flg) +{ + WIN_t *w; + + w = Curwin; + do { + switch (what) { + case Flags_TOG: + TOGw(w, flg); + break; + case Flags_SET: /* Ummmm, i can't find anybody */ + SETw(w, flg); /* who uses Flags_set ... */ + break; + case Flags_OFF: + OFFw(w, flg); + break; + } + // a flag with special significance -- user wants to rebalance + // display so we gotta' 'off' one number then force on two flags... + if (EQUWINS_cwo == flg) { + w->rc.maxtasks = 0; + SETw(w, Show_IDLEPS | VISIBLE_tsk); + } + w = w->next; + } while (w != Curwin); +} + + +// using a flag to avoid other code seeing inconsistant state +static volatile int need_resize; +static void wins_resize_sighandler (int dont_care_sig) +{ + (void)dont_care_sig; + need_resize = 1; + ZAP_TIMEOUT +} + + +// Set the screen dimensions and arrange for the real workhorse. +// (also) catches: +// SIGWINCH and SIGCONT +static void wins_resize (void) +{ + struct winsize wz; + char *env_columns; // Unix98 environment variable COLUMNS + char *env_lines; // Unix98 environment variable LINES + + Screen_cols = columns; // + Screen_rows = lines; // + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) { + Screen_cols = wz.ws_col; + Screen_rows = wz.ws_row; + } + + if (Batch) Screen_rows = MAXINT; + + env_columns = getenv("COLUMNS"); + if(env_columns && *env_columns){ + long t; + char *endptr; + t = strtol(env_columns, &endptr, 0); + if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t; + } + env_lines = getenv("LINES"); + if(env_lines && *env_lines){ + long t; + char *endptr; + t = strtol(env_lines, &endptr, 0); + if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t; + } + + // be crudely tolerant of crude tty emulators + if (avoid_last_column) Screen_cols--; + + // we might disappoint some folks (but they'll deserve it) + if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX; + + // keep our support for output optimization in sync with current reality + // note: when we're in Batch mode, we don't really need a Pseudo_scrn and + // when not Batch, our buffer will contain 1 extra 'line' since + // Msg_row is never represented -- but it's nice to have some space + // between us and the great-beyond... + Pseudo_cols = Screen_cols + CLRBUFSIZ + 1; + if (Batch) Pseudo_size = ROWBUFSIZ + 1; + else Pseudo_size = Pseudo_cols * Screen_rows; + Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size); + + // force rebuild of column headers AND libproc/readproc requirements + Frames_libflags = 0; +} + + +// Set up the raw/incomplete field group windows -- +// they'll be finished off after startup completes. +// [ and very likely that will override most/all of our efforts ] +// [ --- life-is-NOT-fair --- ] +static void windows_stage1 (void) +{ + WIN_t *w; + int i; + + for (i = 0; i < GROUPSMAX; i++) { + w = &Winstk[i]; + w->winnum = i + 1; + w->rc = Rc.win[i]; + w->captab[0] = Cap_norm; + w->captab[1] = Cap_norm; + w->captab[2] = w->cap_bold; + w->captab[3] = w->capclr_sum; + w->captab[4] = w->capclr_msg; + w->captab[5] = w->capclr_pmt; + w->captab[6] = w->capclr_hdr; + w->captab[7] = w->capclr_rowhigh; + w->captab[8] = w->capclr_rownorm; + w->next = w + 1; + w->prev = w - 1; + ++w; + } + /* fixup the circular chains... */ + Winstk[3].next = &Winstk[0]; + Winstk[0].prev = &Winstk[3]; + Curwin = Winstk; +} + + +// This guy just completes the field group windows after the +// rcfiles have been read and command line arguments parsed +static void windows_stage2 (void) +{ + int i; + + for (i = 0; i < GROUPSMAX; i++) { + win_names(&Winstk[i], Winstk[i].rc.winname); + capsmk(&Winstk[i]); + } + // rely on this next guy to force a call (eventually) to reframewins + wins_resize(); +} + + +/*###### Main Screen routines ##########################################*/ + +// Process keyboard input during the main loop +static void do_key (unsigned c) +{ + // standardized 'secure mode' errors + static const char err_secure[] = "\aUnavailable in secure mode"; + static const char err_num_cpus[] = "\aSorry, terminal is not big enough"; +#ifdef WARN_NOT_SMP + // standardized 'smp' errors + static const char err_smp[] = "\aSorry, only 1 cpu detected"; +#endif + + switch (c) { + case '1': + if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) { + show_msg(err_num_cpus); + break; + } +#ifdef WARN_NOT_SMP + if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM); + else show_msg(err_smp); +#else + TOGw(Curwin, View_CPUSUM); +#endif + break; + + case 'a': + if (Rc.mode_altscr) Curwin = Curwin->next; + break; + + case 'A': + Rc.mode_altscr = !Rc.mode_altscr; + wins_resize(); + break; + + case 'b': + if (VIZCHKc) { + if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS)) + show_msg("\aNothing to highlight!"); + else { + TOGw(Curwin, Show_HIBOLD); + capsmk(Curwin); + } + } + break; + + case 'B': + TOGw(Curwin, View_NOBOLD); + capsmk(Curwin); + break; + + case 'c': + VIZTOGc(Show_CMDLIN); + break; + + case 'd': + case 's': + if (Secure_mode) + show_msg(err_secure); + else { + float tmp = + get_float(fmtmk("Change delay from %.1f to", Rc.delay_time)); + if (tmp > -1) Rc.delay_time = tmp; + } + break; + + case 'f': + if (VIZCHKc) fields_toggle(); + break; + + case 'F': + case 'O': + if (VIZCHKc) fields_sort(); + break; + + case 'g': + if (Rc.mode_altscr) { + char tmp[GETBUFSIZ]; + strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname))); + if (tmp[0]) win_names(Curwin, tmp); + } + break; + + case 'G': + win_select(0); + break; + + case 'H': + if (VIZCHKc) { + TOGw(Curwin, Show_THREADS); + show_msg(fmtmk("Show threads %s" + , CHKw(Curwin, Show_THREADS) ? "On" : "Off")); + } + break; + + case 'h': + case '?': + { char ch; + putp(Cap_clr_scr); + putp(Cap_curs_huge); + /* this string is well above ISO C89's minimum requirements! */ + show_special( + 1, + fmtmk( + KEYS_help, + procps_version, + Curwin->grpname, + CHKw(Curwin, Show_CTIMES) ? "On" : "Off", + Rc.delay_time, + Secure_mode ? "On" : "Off", + Secure_mode ? "" : KEYS_help_unsecured + ) + ); + chin(0, &ch, 1); + if ('?' == ch || 'h' == ch) { + do { + putp(Cap_clr_scr); + show_special(1, fmtmk(WINDOWS_help + , Curwin->grpname + , Winstk[0].rc.winname + , Winstk[1].rc.winname + , Winstk[2].rc.winname + , Winstk[3].rc.winname)); + chin(0, &ch, 1); + win_select(ch); + } while ('\n' != ch); + } + putp(Cap_curs_norm); + } + break; + + case 'i': + VIZTOGc(Show_IDLEPS); + break; + + case 'I': +#ifdef WARN_NOT_SMP + if (Cpu_tot > 1) { + Rc.mode_irixps = !Rc.mode_irixps; + show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off")); + } else + show_msg(err_smp); +#else + Rc.mode_irixps = !Rc.mode_irixps; + show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off")); +#endif + if(Rc.mode_irixps && smp_num_cpus>1){ + // good for 100 CPUs per process + pcpu_max_value = 9999.0; + Fieldstab[P_CPU].fmts = " %4.0f"; + } else { + pcpu_max_value = 99.9; + Fieldstab[P_CPU].fmts = " %#4.1f"; + } + break; + + case 'k': + if (Secure_mode) { + show_msg(err_secure); + } else { + int sig, pid = get_int("PID to kill"); + if (pid > 0) { + sig = signal_name_to_number( + ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL))); + if (sig == -1) sig = DEF_SIGNAL; + if (sig && kill(pid, sig)) + show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno))); + } + } + break; + + case 'l': + TOGw(Curwin, View_LOADAV); + break; + + case 'm': + TOGw(Curwin, View_MEMORY); + break; + + case 'n': + case '#': + if (VIZCHKc) { + int num = + get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks)); + if (num > -1) Curwin->rc.maxtasks = num; + } + break; + + case 'o': + if (VIZCHKc) fields_reorder(); + break; + + case 'q': + end_pgm(0); + + case 'r': + if (Secure_mode) + show_msg(err_secure); + else { + int val, pid = get_int("PID to renice"); + if (pid > 0) { + val = get_int(fmtmk("Renice PID %d to value", pid)); + if (setpriority(PRIO_PROCESS, (unsigned)pid, val)) + show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno))); + } + } + break; + + case 'R': + VIZTOGc(Qsrt_NORMAL); + break; + + case 'S': + if (VIZCHKc) { + TOGw(Curwin, Show_CTIMES); + show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off")); + } + break; + + case 't': + TOGw(Curwin, View_STATES); + break; + +// case 'u': +// if (VIZCHKc) +// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)")); +// break; + + case 'u': +// if (!VIZCHKc) break; + do { + const char *errmsg; + const char *answer; + answer = ask4str("Which user (blank for all)"); + // FIXME: do this better: + if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') { + selection_type = 0; + selection_uid = -1; + break; + } + errmsg = parse_uid(answer, &selection_uid); + if (errmsg) { + show_msg(errmsg); + // Change settings here? I guess not. + break; + } + selection_type = 'u'; + } while(0); + break; + + case 'U': +// if (!VIZCHKc) break; + do { + const char *errmsg; + const char *answer; + answer = ask4str("Which user (blank for all)"); + // FIXME: do this better: + if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') { + selection_type = 0; + selection_uid = -1; + break; + } + errmsg = parse_uid(answer, &selection_uid); + if (errmsg) { + show_msg(errmsg); + // Change settings here? I guess not. + break; + } + selection_type = 'U'; + } while(0); + break; + + case 'w': + if (Rc.mode_altscr) Curwin = Curwin->prev; + break; + + case 'W': + { const char *err = rc_write_whatever(); + if (err) + show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err)); + else + show_msg(fmtmk("Wrote configuration to '%s'", Rc_name)); + } + break; + + case 'x': + if (VIZCHKc) { + TOGw(Curwin, Show_HICOLS); + capsmk(Curwin); + } + break; + + case 'y': + if (VIZCHKc) { + TOGw(Curwin, Show_HIROWS); + capsmk(Curwin); + } + break; + + case 'z': + if (VIZCHKc) { + TOGw(Curwin, Show_COLORS); + capsmk(Curwin); + } + break; + + case 'Z': + wins_colors(); + break; + + case '-': + if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk); + break; + + case '_': + if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk); + break; + + case '=': + Curwin->rc.maxtasks = 0; + SETw(Curwin, Show_IDLEPS | VISIBLE_tsk); + Monpidsidx = 0; + selection_type = '\0'; + break; + + case '+': + if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo); + break; + + case '<': + if (VIZCHKc) { + FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1; + while (*p != Curwin->rc.sortindx) --p; + if (--p >= Curwin->procflags) + Curwin->rc.sortindx = *p; + } + break; + + case '>': + if (VIZCHKc) { + FLG_t *p = Curwin->procflags; + while (*p != Curwin->rc.sortindx) ++p; + if (++p < Curwin->procflags + Curwin->maxpflgs) + Curwin->rc.sortindx = *p; + } + break; + + case 'M': // these keys represent old-top compatability + case 'N': // -- grouped here so that if users could ever + case 'P': // be weaned, we would just whack this part... + case 'T': + { static struct { + const char *xmsg; + const unsigned xkey; + const FLG_t sort; + } xtab[] = { + { "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, }, + { "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, }; + int i; + for (i = 0; i < MAXTBL(xtab); ++i) + if (c == xtab[i].xkey) { + Curwin->rc.sortindx = xtab[i].sort; +// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg)); + break; + } + } + break; + + case '\n': // just ignore these, they'll have the effect + case ' ': // of refreshing display after waking us up ! + break; + + default: + show_msg("\aUnknown command - try 'h' for help"); + } + // The following assignment will force a rebuild of all column headers and + // the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are + // some keys that COULD require new column headers and/or libproc flags: + // 'A' - likely + // 'c' - likely when !Mode_altscr, maybe when Mode_altscr + // 'F' - maybe, if new field forced on + // 'f' - likely + // 'G' - likely + // 'O' - maybe, if new field forced on + // 'o' - maybe, if new field brought into view + // 'Z' - likely, if 'Curwin' changed when !Mode_altscr + // '-' - likely (restricted to Mode_altscr) + // '_' - likely (restricted to Mode_altscr) + // '=' - maybe, but only when Mode_altscr + // '+' - likely (restricted to Mode_altscr) + // ( At this point we have a human being involved and so have all the time ) + // ( in the world. We can afford a few extra cpu cycles every now & then! ) + Frames_libflags = 0; +} + + +// State display *Helper* function to calc and display the state +// percentages for a single cpu. In this way, we can support +// the following environments without the usual code bloat. +// 1) single cpu machines +// 2) modest smp boxes with room for each cpu's percentages +// 3) massive smp guys leaving little or no room for process +// display and thus requiring the cpu summary toggle +static void summaryhlp (CPU_t *cpu, const char *pfx) +{ + // we'll trim to zero if we get negative time ticks, + // which has happened with some SMP kernels (pre-2.4?) +#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz) + SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz; + float scale; + + u_frme = cpu->u - cpu->u_sav; + s_frme = cpu->s - cpu->s_sav; + n_frme = cpu->n - cpu->n_sav; + i_frme = TRIMz(cpu->i - cpu->i_sav); + w_frme = cpu->w - cpu->w_sav; + x_frme = cpu->x - cpu->x_sav; + y_frme = cpu->y - cpu->y_sav; + z_frme = cpu->z - cpu->z_sav; + tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme; + if (tot_frme < 1) tot_frme = 1; + scale = 100.0 / (float)tot_frme; + + // display some kinda' cpu state percentages + // (who or what is explained by the passed prefix) + show_special( + 0, + fmtmk( + States_fmts, + pfx, + (float)u_frme * scale, + (float)s_frme * scale, + (float)n_frme * scale, + (float)i_frme * scale, + (float)w_frme * scale, + (float)x_frme * scale, + (float)y_frme * scale, + (float)z_frme * scale + ) + ); + Msg_row += 1; + + // remember for next time around + cpu->u_sav = cpu->u; + cpu->s_sav = cpu->s; + cpu->n_sav = cpu->n; + cpu->i_sav = cpu->i; + cpu->w_sav = cpu->w; + cpu->x_sav = cpu->x; + cpu->y_sav = cpu->y; + cpu->z_sav = cpu->z; + +#undef TRIMz +} + + +// Begin a new frame by: +// 1) Refreshing the all important proc table +// 2) Displaying uptime and load average (maybe) +// 3) Displaying task/cpu states (maybe) +// 4) Displaying memory & swap usage (maybe) +// and then, returning a pointer to the pointers to the proc_t's! +static proc_t **summary_show (void) +{ + static proc_t **p_table = NULL; + static CPU_t *smpcpu = NULL; + + // whoa first time, gotta' prime the pump... + if (!p_table) { + p_table = procs_refresh(NULL, Frames_libflags); + putp(Cap_clr_scr); + putp(Cap_rmam); +#ifndef PROF + // sleep for half a second + tv.tv_sec = 0; + tv.tv_usec = 500000; + select(0, NULL, NULL, NULL, &tv); // ought to loop until done +#endif + } else { + putp(Batch ? "\n\n" : Cap_home); + } + p_table = procs_refresh(p_table, Frames_libflags); + + // Display Uptime and Loadavg + if (CHKw(Curwin, View_LOADAV)) { + if (!Rc.mode_altscr) { + show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime())); + } else { + show_special( + 0, + fmtmk( + CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line, + Curwin->grpname, + sprint_uptime() + ) + ); + } + Msg_row += 1; + } + + // Display Task and Cpu(s) States + if (CHKw(Curwin, View_STATES)) { + show_special( + 0, + fmtmk( + STATES_line1, + Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied + ) + ); + Msg_row += 1; + + smpcpu = cpus_refresh(smpcpu); + + if (CHKw(Curwin, View_CPUSUM)) { + // display just the 1st /proc/stat line + summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):"); + } else { + int i; + char tmp[SMLBUFSIZ]; + // display each cpu's states separately + for (i = 0; i < Cpu_tot; i++) { + snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id); + summaryhlp(&smpcpu[i], tmp); + } + } + } + + // Display Memory and Swap stats + meminfo(); + if (CHKw(Curwin, View_MEMORY)) { + show_special(0, fmtmk(MEMORY_line1 + , kb_main_total, kb_main_used, kb_main_free, kb_main_buffers)); + show_special(0, fmtmk(MEMORY_line2 + , kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached)); + Msg_row += 2; + } + + SETw(Curwin, NEWFRAM_cwo); + return p_table; +} + + +#define PAGES_TO_KB(n) (unsigned long)( (n) << page_to_kb_shift ) + +// the following macro is our means to 'inline' emitting a column -- next to +// procs_refresh, that's the most frequent and costly part of top's job ! +#define MKCOL(va...) do { \ + if(likely(!( CHKw(q, Show_HICOLS) && q->rc.sortindx==i ))) { \ + snprintf(cbuf, sizeof(cbuf), f, ## va); \ + } else { \ + snprintf(_z, sizeof(_z), f, ## va); \ + snprintf(cbuf, sizeof(cbuf), "%s%s%s", \ + q->capclr_rowhigh, \ + _z, \ + !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \ + ); \ + pad += q->len_rowhigh; \ + if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \ + } \ +} while (0) + +// Display information for a single task row. +static void task_show (const WIN_t *q, const proc_t *p) +{ + char rbuf[ROWBUFSIZ]; + char *rp = rbuf; + int j, x, pad; + + *rp = '\0'; + + pad = Rc.mode_altscr; +// if (pad) rp = scat(rp, " "); + + for (x = 0; x < q->maxpflgs; x++) { + char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ]; + FLG_t i = q->procflags[x]; // support for our field/column + const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt + unsigned s = Fieldstab[i].scale; // string must be altered ! + unsigned w = Fieldstab[i].width; + + int advance = (x==0) && !Rc.mode_altscr; + + switch (i) { + case P_CMD: + { char tmp[ROWBUFSIZ]; + unsigned flags; + int maxcmd = q->maxcmdln; + if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; + else flags = ESC_DEFUNCT; + escape_command(tmp, p, sizeof tmp, &maxcmd, flags); + MKCOL(q->maxcmdln, q->maxcmdln, tmp); + } + break; + case P_COD: + MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s)); + break; + case P_CPN: + MKCOL((unsigned)p->processor); + break; + case P_CPU: + { float u = (float)p->pcpu * Frame_tscale; + if (u > pcpu_max_value) u = pcpu_max_value; + MKCOL(u); + } + break; + case P_DAT: + MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s)); + break; + case P_DRT: + MKCOL(scale_num((unsigned)p->dt, w, s)); + break; + case P_FLG: + { char tmp[TNYBUFSIZ]; + snprintf(tmp, sizeof(tmp), f, (long)p->flags); + for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.'; + f = tmp; + MKCOL(""); + } + break; + case P_FLT: + MKCOL(scale_num(p->maj_flt, w, s)); + break; + case P_GRP: + MKCOL(p->egroup); + break; + case P_MEM: + MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total); + break; + case P_NCE: + MKCOL((int)p->nice); + break; + case P_PID: + MKCOL((unsigned)p->XXXID); + break; + case P_PPD: + MKCOL((unsigned)p->ppid); + break; + case P_PRI: + if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) { + f = " RT"; + MKCOL(""); + } else + MKCOL((int)p->priority); + break; + case P_RES: + MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s)); + break; + case P_SHR: + MKCOL(scale_num(PAGES_TO_KB(p->share), w, s)); + break; + case P_STA: + MKCOL(p->state); + break; + case P_SWP: + MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s)); + break; + case P_TME: + case P_TM2: + { TIC_t t = p->utime + p->stime; + if (CHKw(q, Show_CTIMES)) + t += (p->cutime + p->cstime); + MKCOL(scale_tics(t, w)); + } + break; + case P_TTY: + { char tmp[TNYBUFSIZ]; + dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV); + MKCOL(tmp); + } + break; + case P_UID: + MKCOL((unsigned)p->euid); + break; + case P_URE: + MKCOL(p->euser); + break; + case P_URR: + MKCOL(p->ruser); + break; + case P_VRT: + MKCOL(scale_num(PAGES_TO_KB(p->size), w, s)); + break; + case P_WCH: + if (No_ksyms) { + f = " %08lx "; + MKCOL((long)p->wchan); + } else { + MKCOL(lookup_wchan(p->wchan, p->XXXID)); + } + break; + + } /* end: switch 'procflag' */ + + rp = scat(rp, cbuf+advance); + } /* end: for 'maxpflgs' */ + + PUFF( + "\n%s%.*s%s%s", + (CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm, + Screen_cols + pad, + rbuf, + Caps_off, + "" /*Cap_clr_eol*/ + ); + +#undef MKCOL +} + + +// Squeeze as many tasks as we can into a single window, +// after sorting the passed proc table. +static void window_show (proc_t **ppt, WIN_t *q, int *lscr) +{ +#ifdef SORT_SUPRESS + // the 1 flag that DOES and 2 flags that MAY impact our proc table qsort +#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES ) + static FLG_t sav_indx = 0; + static int sav_flgs = -1; +#endif + int i, lwin; + + // Display Column Headings -- and distract 'em while we sort (maybe) + PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol); + +#ifdef SORT_SUPRESS + if (CHKw(Curwin, NEWFRAM_cwo) + || sav_indx != q->rc.sortindx + || sav_flgs != (q->rc.winflags & srtMASK)) { + sav_indx = q->rc.sortindx; + sav_flgs = (q->rc.winflags & srtMASK); +#endif + if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed! + else Frame_srtflg = -1; + Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe + Frame_cmdlin = CHKw(q, Show_CMDLIN); + qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort); +#ifdef SORT_SUPRESS + } +#endif + // account for column headings + (*lscr)++; + lwin = 1; + i = 0; + + while ( ppt[i]->tid != -1 && *lscr < Max_lines && (!q->winlines || (lwin <= q->winlines)) ) { + if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state)) + && good_uid(ppt[i]) ) { + // Display a process Row + task_show(q, ppt[i]); + (*lscr)++; + ++lwin; + } + ++i; + } + // for this frame that window's toast, cleanup for next time + q->winlines = 0; + OFFw(Curwin, FLGSOFF_cwo); + +#ifdef SORT_SUPRESS +#undef srtMASK +#endif +} + + +/*###### Entry point plus two ##########################################*/ + +// This guy's just a *Helper* function who apportions the +// remaining amount of screen real estate under multiple windows +static void framehlp (int wix, int max) +{ + int i, rsvd, size, wins; + + // calc remaining number of visible windows + total 'user' lines + for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) { + if (CHKw(&Winstk[i], VISIBLE_tsk)) { + rsvd += Winstk[i].rc.maxtasks; + ++wins; + if (max <= rsvd) break; + } + } + if (!wins) wins = 1; + // set aside 'rsvd' & deduct 1 line/window for the columns heading + size = (max - wins) - rsvd; + if (0 <= size) size = max; + size = (max - wins) / wins; + + // for remaining windows, set WIN_t winlines to either the user's + // maxtask (1st choice) or our 'foxized' size calculation + // (foxized adj. - 'fair and balanced') + for (i = wix ; i < GROUPSMAX; i++) { + if (CHKw(&Winstk[i], VISIBLE_tsk)) { + Winstk[i].winlines = + Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size; + } + } +} + + +// Initiate the Frame Display Update cycle at someone's whim! +// This routine doesn't do much, mostly he just calls others. +// +// (Whoa, wait a minute, we DO caretake those row guys, plus) +// (we CALCULATE that IMPORTANT Max_lines thingy so that the) +// (*subordinate* functions invoked know WHEN the user's had) +// (ENOUGH already. And at Frame End, it SHOULD be apparent) +// (WE am d'MAN -- clearing UNUSED screen LINES and ensuring) +// (the CURSOR is STUCK in just the RIGHT place, know what I) +// (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say) +// (THAT about THIS function again, Ok? Good that's better.) +static void frame_make (void) +{ + proc_t **ppt; + int i, scrlins; + + // note: all libproc flags are managed by + // reframewins(), who also builds each window's column headers + if (!Frames_libflags) { + reframewins(); + memset(Pseudo_scrn, '\0', Pseudo_size); + } + Pseudo_row = Msg_row = scrlins = 0; + ppt = summary_show(); + Max_lines = (Screen_rows - Msg_row) - 1; + + if (CHKw(Curwin, EQUWINS_cwo)) + wins_reflag(Flags_OFF, EQUWINS_cwo); + + // sure hope each window's columns header begins with a newline... + putp(tg2(0, Msg_row)); + + if (!Rc.mode_altscr) { + // only 1 window to show so, piece o' cake + Curwin->winlines = Curwin->rc.maxtasks; + window_show(ppt, Curwin, &scrlins); + } else { + // maybe NO window is visible but assume, pieces o' cakes + for (i = 0 ; i < GROUPSMAX; i++) { + if (CHKw(&Winstk[i], VISIBLE_tsk)) { + framehlp(i, Max_lines - scrlins); + window_show(ppt, &Winstk[i], &scrlins); + } + if (Max_lines <= scrlins) break; + } + } + // clear to end-of-screen (critical if last window is 'idleps off'), + // then put the cursor in-its-place, and rid us of any prior frame's msg + // (main loop must iterate such that we're always called before sleep) + PUTT( + "%s%s%s%s", + scrlins < Max_lines ? "\n" : "", + scrlins < Max_lines ? Cap_clr_eos : "", + tg2(0, Msg_row), + Cap_clr_eol + ); + fflush(stdout); +} + + +int main (int dont_care_argc, char *argv[]) +{ + (void)dont_care_argc; + before(*argv); + // +-------------+ + windows_stage1(); // top (sic) slice + configs_read(); // > spread etc, < + parse_args(&argv[1]); // > lean stuff, < + whack_terminal(); // > onions etc. < + windows_stage2(); // as bottom slice + // +-------------+ + signal(SIGALRM, end_pgm); + signal(SIGHUP, end_pgm); + signal(SIGINT, end_pgm); + signal(SIGPIPE, end_pgm); + signal(SIGQUIT, end_pgm); + signal(SIGTERM, end_pgm); + signal(SIGTSTP, suspend); + signal(SIGTTIN, suspend); + signal(SIGTTOU, suspend); + signal(SIGCONT, wins_resize_sighandler); + signal(SIGWINCH, wins_resize_sighandler); + + for (;;) { + if (need_resize){ + need_resize = 0; + wins_resize(); + } + frame_make(); + + if (Msg_awaiting) show_msg(Msg_delayed); + if (Loops > 0) --Loops; + if (!Loops) end_pgm(0); + + tv.tv_sec = Rc.delay_time; + tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000; + + if (Batch) { + select(0, NULL, NULL, NULL, &tv); // ought to loop until done + } else { + long file_flags; + int rc; + char c; + fd_set fs; + FD_ZERO(&fs); + FD_SET(STDIN_FILENO, &fs); + file_flags = fcntl(STDIN_FILENO, F_GETFL); + if(file_flags==-1) file_flags=0; + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags); + + // check 1st, in case tv zeroed (by sig handler) before it got set + rc = chin(0, &c, 1); + if (rc <= 0) { + // EOF is pretty much a "can't happen" except for a kernel bug. + // We should quickly die via SIGHUP, and thus not spin here. + // if (rc == 0) end_pgm(0); /* EOF from terminal */ + fcntl(STDIN_FILENO, F_SETFL, file_flags); + select(1, &fs, NULL, NULL, &tv); + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags); + } + if (chin(0, &c, 1) > 0) { + fcntl(STDIN_FILENO, F_SETFL, file_flags); + do_key((unsigned)c); + } else { + fcntl(STDIN_FILENO, F_SETFL, file_flags); + } + } + } + + return 0; +} diff --git a/top.h b/top.h new file mode 100644 index 0000000..b4ac079 --- /dev/null +++ b/top.h @@ -0,0 +1,567 @@ +// top.h - Header file: show Linux processes +// +// Copyright (c) 2002, by: James C. Warner +// All rights reserved. 8921 Hilloway Road +// Eden Prairie, Minnesota 55347 USA +// +// +// This file may be used subject to the terms and conditions of the +// GNU Library General Public License Version 2, or any later version +// at your option, as published by the Free Software Foundation. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Library General Public License for more details. +// +// For their contributions to this program, the author wishes to thank: +// Albert D. Cahalan, +// Craig Small, +// +// Changes by Albert Cahalan, 2002, 2004. + +#ifndef _Itop +#define _Itop + +// Defines intended to be experimented with ------------------------ +//#define CASEUP_HEXES // show any hex values in upper case +//#define CASEUP_SCALE // show scaled time/num suffix upper case +//#define CASEUP_SUMMK // show memory summary kilobytes with 'K' +//#define SORT_SUPRESS // *attempt* to reduce qsort overhead +//#define WARN_NOT_SMP // restrict '1' & 'I' commands to true smp + +// Development/Debugging defines ----------------------------------- +//#define ATEOJ_REPORT // report a bunch of stuff, at end-of-job +//#define PRETEND2_5_X // pretend we're linux 2.5.x (for IO-wait) +//#define PRETENDNOCAP // use a terminal without essential caps +//#define STDOUT_IOLBF // disable our own stdout _IOFBF override + +#ifdef PRETEND2_5_X +#define linux_version_code LINUX_VERSION(2,5,43) +#endif + +/*###### Some Miscellaneous constants ##################################*/ + +// The default delay twix updates +#define DEF_DELAY 3.0 + +// The length of time a 'message' is displayed +#define MSG_SLEEP 2 + +// The default value for the 'k' (kill) request +#define DEF_SIGNAL SIGTERM + +// Specific process id monitoring support (command line only) +#define MONPIDMAX 20 + +// Power-of-two sizes lead to trouble; the largest power of +// two factor should be the cache line size. It'll mean the +// array indexing math gets slower, but cache aliasing is +// avoided. +#define CACHE_TWEAK_FACTOR 64 + +// Miscellaneous buffer sizes with liberal values -- mostly +// just to pinpoint source code usage/dependancies +#define SCREENMAX ( 512 + CACHE_TWEAK_FACTOR) +// the above might seem pretty stingy, until you consider that with every +// one of top's fields displayed we're talking a 160 byte column header -- +// so that will provide for all fields plus a 350+ byte command line +#define WINNAMSIZ 4 +#define CAPTABMAX 9 +#define PFLAGSSIZ 32 +#define CAPBUFSIZ 32 +#define CLRBUFSIZ 64 +#define GETBUFSIZ 32 +#define TNYBUFSIZ 32 +#define SMLBUFSIZ ( 256 + CACHE_TWEAK_FACTOR) +#define OURPATHSZ (1024 + CACHE_TWEAK_FACTOR) +#define MEDBUFSIZ (1024 + CACHE_TWEAK_FACTOR) +#define BIGBUFSIZ (2048 + CACHE_TWEAK_FACTOR) +#define USRNAMSIZ GETBUFSIZ +#define ROWBUFSIZ SCREENMAX + CLRBUFSIZ + + +/*###### Some Miscellaneous Macro definitions ##########################*/ + +// Yield table size as 'int' +#define MAXTBL(t) (int)(sizeof(t) / sizeof(t[0])) + +// Used as return arguments in *some* of the sort callbacks +#define SORT_lt ( Frame_srtflg > 0 ? 1 : -1 ) +#define SORT_gt ( Frame_srtflg > 0 ? -1 : 1 ) +#define SORT_eq 0 + +// Used to create *most* of the sort callback functions +#define SCB_NUM1(f,n) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + if ( (*P)->n < (*Q)->n ) return SORT_lt; \ + if (likely( (*P)->n > (*Q)->n )) return SORT_gt; \ + return SORT_eq; } +#define SCB_NUM2(f,n1,n2) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + if ( ((*P)->n1 - (*P)->n2) < ((*Q)->n1 - (*Q)->n2) ) return SORT_lt; \ + if (likely( ((*P)->n1 - (*P)->n2) > ((*Q)->n1 - (*Q)->n2) )) return SORT_gt; \ + return SORT_eq; } +#define SCB_NUMx(f,n) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + return Frame_srtflg * ( (*Q)->n - (*P)->n ); } +#define SCB_STRx(f,s) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + return Frame_srtflg * strcmp((*Q)->s, (*P)->s); } + +// The following two macros are used to 'inline' those portions of the +// display process requiring formatting, while protecting against any +// potential embedded 'millesecond delay' escape sequences. + +// PUTT - Put to Tty (used in many places) +// - for temporary, possibly interactive, 'replacement' output +// - may contain ANY valid terminfo escape sequences +// - need NOT represent an entire screen row +#define PUTT(fmt,arg...) do { \ + char _str[ROWBUFSIZ]; \ + snprintf(_str, sizeof(_str), fmt, ## arg); \ + putp(_str); \ + } while (0) + +// PUFF - Put for Frame (used in only 3 places) +// - for more permanent frame-oriented 'update' output +// - may NOT contain cursor motion terminfo escapes +// - assumed to represent a complete screen ROW +// - subject to optimization, thus MAY be discarded + +// The evil version (53892 byte stripped top, oddly enough) +#define _PUFF(fmt,arg...) \ +do { \ + char _str[ROWBUFSIZ]; \ + int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ + putp ( Batch ? _str : \ + ({ \ + char *restrict const _pse = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ + memcmp(_pse, _str, _len) ? memcpy(_pse, _str, _len) : "\n"; \ + }) \ + ); \ +} while (0) + +// The good version (53876 byte stripped top) +#define PUFF(fmt,arg...) \ +do { \ + char _str[ROWBUFSIZ]; \ + char *_ptr; \ + int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ + if (Batch) _ptr = _str; \ + else { \ + _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ + if (memcmp(_ptr, _str, _len)) { \ + memcpy(_ptr, _str, _len); \ + } else { \ + _ptr = "\n"; \ + } \ + } \ + putp(_ptr); \ +} while (0) + + +/*------ Special Macros (debug and/or informative) ---------------------*/ + +// Orderly end, with any sort of message - see fmtmk +#define debug_END(s) { \ + static void std_err (const char *); \ + fputs(Cap_clr_scr, stdout); \ + std_err(s); \ + } + +// A poor man's breakpoint, if he's too lazy to learn gdb +#define its_YOUR_fault { *((char *)0) = '!'; } + + +/*###### Some Typedef's and Enum's #####################################*/ + +// This typedef just ensures consistent 'process flags' handling +typedef unsigned FLG_t; + +// These typedefs attempt to ensure consistent 'ticks' handling +typedef unsigned long long TIC_t; +typedef long long SIC_t; + +// Sort support, callback funtion signature +typedef int (*QFP_t)(const void *, const void *); + +// This structure consolidates the information that's used +// in a variety of display roles. +typedef struct FLD_t { + const char keys [4]; // order: New-on New-off Old-on Old-off + // misaligned on 64-bit, but part of a table -- oh well + const char *head; // name for col heads + toggle/reorder fields + const char *fmts; // sprintf format string for field display + const int width; // field width, if applicable + const int scale; // scale_num type, if applicable + const QFP_t sort; // sort function + const char *desc; // description for toggle/reorder fields + const int lflg; // PROC_FILLxxx flag(s) needed by this field +} FLD_t; + +// This structure stores one piece of critical 'history' +// information from one frame to the next -- we don't calc +// and save data that goes unused +typedef struct HST_t { + TIC_t tics; + int pid; +} HST_t; + +// This structure stores a frame's cpu tics used in history +// calculations. It exists primarily for SMP support but serves +// all environments. +typedef struct CPU_t { + TIC_t u, n, s, i, w, x, y, z; // as represented in /proc/stat + TIC_t u_sav, s_sav, n_sav, i_sav, w_sav, x_sav, y_sav, z_sav; // in the order of our display + unsigned id; // the CPU ID number +} CPU_t; + +// These 2 types support rcfile compatibility +typedef struct RCW_t { // the 'window' portion of an rcfile + FLG_t sortindx; // sort field, represented as a procflag + int winflags, // 'view', 'show' and 'sort' mode flags + maxtasks, // user requested maximum, 0 equals all + summclr, // color num used in summ info + msgsclr, // " in msgs/pmts + headclr, // " in cols head + taskclr; // " in task rows + char winname [WINNAMSIZ], // window name, user changeable + fieldscur [PFLAGSSIZ]; // fields displayed and ordered +} RCW_t; + +typedef struct RCF_t { // the complete rcfile (new style) + int mode_altscr; // 'A' - Alt display mode (multi task windows) + int mode_irixps; // 'I' - Irix vs. Solaris mode (SMP-only) + float delay_time; // 'd' or 's' - How long to sleep twixt updates + int win_index; // Curwin, as index + RCW_t win [4]; // a 'WIN_t.rc' for each of the 4 windows +} RCF_t; + +// The scaling 'type' used with scale_num() -- this is how +// the passed number is interpreted should scaling be necessary +enum scale_num { + SK_no, SK_Kb, SK_Mb, SK_Gb, SK_Tb +}; + +// Flags for each possible field +enum pflag { + P_PID, P_PPD, P_URR, P_UID, P_URE, P_GRP, P_TTY, + P_PRI, P_NCE, + P_CPN, P_CPU, P_TME, P_TM2, + P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR, + P_FLT, P_DRT, + P_STA, P_CMD, P_WCH, P_FLG +}; + + +/////////////////////////////////////////////////////////////////////////// +// Special Section: multiple windows/field groups ------------- +// (kind of a header within a header: constants, macros & types) + +#define GROUPSMAX 4 // the max number of simultaneous windows +#define GRPNAMSIZ WINNAMSIZ+2 // window's name + number as in: '#:...' + +#define Flags_TOG 1 // these are used to direct wins_reflag +#define Flags_SET 2 +#define Flags_OFF 3 + +// The Persistent 'Mode' flags! +// These are preserved in the rc file, as a single integer and the +// letter shown is the corresponding 'command' toggle + +// 'View_' flags affect the summary (minimum), taken from 'Curwin' +#define View_CPUSUM 0x8000 // '1' - show combined cpu stats (vs. each) +#define View_LOADAV 0x4000 // 'l' - display load avg and uptime summary +#define View_STATES 0x2000 // 't' - display task/cpu(s) states summary +#define View_MEMORY 0x1000 // 'm' - display memory summary +#define View_NOBOLD 0x0001 // 'B' - disable 'bold' attribute globally + +// 'Show_' & 'Qsrt_' flags are for task display in a visible window +#define Show_THREADS 0x10000 // 'H' - show threads in each task +#define Show_COLORS 0x0800 // 'z' - show in color (vs. mono) +#define Show_HIBOLD 0x0400 // 'b' - rows and/or cols bold (vs. reverse) +#define Show_HICOLS 0x0200 // 'x' - show sort column highlighted +#define Show_HIROWS 0x0100 // 'y' - show running tasks highlighted +#define Show_CMDLIN 0x0080 // 'c' - show cmdline vs. name +#define Show_CTIMES 0x0040 // 'S' - show times as cumulative +#define Show_IDLEPS 0x0020 // 'i' - show idle processes (all tasks) +#define Qsrt_NORMAL 0x0010 // 'R' - reversed column sort (high to low) + +// these flag(s) have no command as such - they're for internal use +#define VISIBLE_tsk 0x0008 // tasks are showable when in 'Mode_altscr' +#define NEWFRAM_cwo 0x0004 // new frame (if anyone cares) - in Curwin +#define EQUWINS_cwo 0x0002 // rebalance tasks next frame (off 'i'/ 'n') + // ...set in Curwin, but impacts all windows + +// Current-window-only flags -- always turned off at end-of-window! +#define FLGSOFF_cwo EQUWINS_cwo | NEWFRAM_cwo + +// Default flags if there's no rcfile to provide user customizations +#define DEF_WINFLGS ( \ + View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY | View_NOBOLD | \ + Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Qsrt_NORMAL | \ + VISIBLE_tsk \ +) + + // Used to test/manipulate the window flags +#define CHKw(q,f) (int)((q)->rc.winflags & (f)) +#define TOGw(q,f) (q)->rc.winflags ^= (f) +#define SETw(q,f) (q)->rc.winflags |= (f) +#define OFFw(q,f) (q)->rc.winflags &= ~(f) +#define VIZCHKc (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \ + ? 1 : win_warn() +#define VIZTOGc(f) (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \ + ? TOGw(Curwin, f) : win_warn() + +// This structure stores configurable information for each window. +// By expending a little effort in its creation and user requested +// maintainence, the only real additional per frame cost of having +// windows is an extra sort -- but that's just on ptrs! +typedef struct WIN_t { + struct WIN_t *next, // next window in window stack + *prev; // prior window in window stack + char *captab [CAPTABMAX]; // captab needed by show_special + int winnum, // window's num (array pos + 1) + winlines; // task window's rows (volatile) + FLG_t procflags [PFLAGSSIZ]; // fieldscur subset, as enum + int maxpflgs, // number of procflags (upcase fieldscur) + maxcmdln; // max length of a process' command line + int len_rownorm, // lengths of the corresponding terminfo + len_rowhigh; // strings to avoid repeated strlen calls + RCW_t rc; // stuff that gets saved in the rcfile + char capclr_sum [CLRBUFSIZ], // terminfo strings built from + capclr_msg [CLRBUFSIZ], // above clrs (& rebuilt too), + capclr_pmt [CLRBUFSIZ], // but NO recurring costs !!! + capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs + capclr_rowhigh [CLRBUFSIZ], // are only used when this + capclr_rownorm [CLRBUFSIZ]; // window is the 'Curwin'! + char cap_bold [CAPBUFSIZ]; // support for View_NOBOLD toggle + char grpname [GRPNAMSIZ], // window number:name, printable + columnhdr [SCREENMAX], // column headings for procflags + colusrnam [USRNAMSIZ]; // if selected by the 'u' command +} WIN_t; + + +/*###### Display Support *Data* ########################################*/ + +// Configuration files support +#define SYS_RCFILESPEC "/etc/toprc" +#define RCF_EYECATCHER "RCfile for " +#define RCF_DEPRECATED "Id:a, " + +// The default fields displayed and their order, +#define DEF_FIELDS "AEHIOQTWKNMbcdfgjplrsuvyzX" +// Pre-configured field groupss +#define JOB_FIELDS "ABcefgjlrstuvyzMKNHIWOPQDX" +#define MEM_FIELDS "ANOPQRSTUVbcdefgjlmyzWHIKX" +#define USR_FIELDS "ABDECGfhijlopqrstuvyzMKNWX" +// Used by fields_sort, placed here for peace-of-mind +#define NUL_FIELDS "abcdefghijklmnopqrstuvwxyz" + + +// The default values for the local config file +#define DEF_RCFILE { \ + 0, 1, DEF_DELAY, 0, { \ + { P_CPU, DEF_WINFLGS, 0, \ + COLOR_RED, COLOR_RED, COLOR_YELLOW, COLOR_RED, \ + "Def", DEF_FIELDS }, \ + { P_PID, DEF_WINFLGS, 0, \ + COLOR_CYAN, COLOR_CYAN, COLOR_WHITE, COLOR_CYAN, \ + "Job", JOB_FIELDS }, \ + { P_MEM, DEF_WINFLGS, 0, \ + COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLUE, COLOR_MAGENTA, \ + "Mem", MEM_FIELDS }, \ + { P_URE, DEF_WINFLGS, 0, \ + COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW, \ + "Usr", USR_FIELDS } \ + } } + + +// Summary Lines specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define LOADAV_line "%s -%s\n" +#define LOADAV_line_alt "%s\06 -%s\n" +#define STATES_line1 "Tasks:\03" \ + " %3u \02total,\03 %3u \02running,\03 %3u \02sleeping,\03 %3u \02stopped,\03 %3u \02zombie\03\n" +#define STATES_line2x4 "%s\03" \ + " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle\03\n" +#define STATES_line2x5 "%s\03" \ + " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle,\03 %#5.1f%% \02IO-wait\03\n" +#define STATES_line2x6 "%s\03" \ + " %#4.1f%% \02us,\03 %#4.1f%% \02sy,\03 %#4.1f%% \02ni,\03 %#4.1f%% \02id,\03 %#4.1f%% \02wa,\03 %#4.1f%% \02hi,\03 %#4.1f%% \02si\03\n" +#define STATES_line2x7 "%s\03" \ + "%#5.1f%%\02us,\03%#5.1f%%\02sy,\03%#5.1f%%\02ni,\03%#5.1f%%\02id,\03%#5.1f%%\02wa,\03%#5.1f%%\02hi,\03%#5.1f%%\02si,\03%#5.1f%%\02st\03\n" +#ifdef CASEUP_SUMMK +#define MEMORY_line1 "Mem: \03" \ + " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02buffers\03\n" +#define MEMORY_line2 "Swap:\03" \ + " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02cached\03\n" +#else +#define MEMORY_line1 "Mem: \03" \ + " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02buffers\03\n" +#define MEMORY_line2 "Swap:\03" \ + " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02cached\03\n" +#endif + +// Keyboard Help specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define KEYS_help \ + "Help for Interactive Commands\02 - %s\n" \ + "Window \01%s\06: \01Cumulative mode \03%s\02. \01System\06: \01Delay \03%.1f secs\02; \01Secure mode \03%s\02.\n" \ + "\n" \ + " Z\05,\01B\05 Global: '\01Z\02' change color mappings; '\01B\02' disable/enable bold\n" \ + " l,t,m Toggle Summaries: '\01l\02' load avg; '\01t\02' task/cpu stats; '\01m\02' mem info\n" \ + " 1,I Toggle SMP view: '\0011\02' single/separate states; '\01I\02' Irix/Solaris mode\n" \ + "\n" \ + " f,o . Fields/Columns: '\01f\02' add or remove; '\01o\02' change display order\n" \ + " F or O . Select sort field\n" \ + " <,> . Move sort field: '\01<\02' next col left; '\01>\02' next col right\n" \ + " R,H . Toggle: '\01R\02' normal/reverse sort; '\01H\02' show threads\n" \ + " c,i,S . Toggle: '\01c\02' cmd name/line; '\01i\02' idle tasks; '\01S\02' cumulative time\n" \ + " x\05,\01y\05 . Toggle highlights: '\01x\02' sort field; '\01y\02' running tasks\n" \ + " z\05,\01b\05 . Toggle: '\01z\02' color/mono; '\01b\02' bold/reverse (only if 'x' or 'y')\n" \ + " u . Show specific user only\n" \ + " n or # . Set maximum tasks displayed\n" \ + "\n" \ + "%s" \ + " W Write configuration file\n" \ + " q Quit\n" \ + " ( commands shown with '.' require a \01visible\02 task display \01window\02 ) \n" \ + "Press '\01h\02' or '\01?\02' for help with \01Windows\02,\n" \ + "any other key to continue " \ + "" + +// This guy goes into the help text (maybe) +#define KEYS_help_unsecured \ + " k,r Manipulate tasks: '\01k\02' kill; '\01r\02' renice\n" \ + " d or s Set update interval\n" \ + "" + +// Fields Reorder/Toggle specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions +// note: the leading newline below serves really dumb terminals; +// if there's no 'cursor_home', the screen will be a mess +// but this part will still be functional. +#define FIELDS_current \ + "\n%sCurrent Fields\02: \01 %s \04 for window \01%s\06\n%s " \ + "" + +// Some extra explanatory text which accompanies the Fields display. +// note: the newlines cannot actually be used, they just serve as +// substring delimiters for the 'display_fields' routine. +#define FIELDS_xtra \ + "Flags field:\n" \ + " 0x00000001 PF_ALIGNWARN\n" \ + " 0x00000002 PF_STARTING\n" \ + " 0x00000004 PF_EXITING\n" \ + " 0x00000040 PF_FORKNOEXEC\n" \ + " 0x00000100 PF_SUPERPRIV\n" \ + " 0x00000200 PF_DUMPCORE\n" \ + " 0x00000400 PF_SIGNALED\n" \ + " 0x00000800 PF_MEMALLOC\n" \ + " 0x00002000 PF_FREE_PAGES (2.5)\n" \ + " 0x00008000 debug flag (2.5)\n" \ + " 0x00024000 special threads (2.5)\n" \ + " 0x001D0000 special states (2.5)\n" \ + " 0x00100000 PF_USEDFPU (thru 2.4)\n" \ + "" +/* no room, sacrificed this one: 'Killed for out-of-memory' */ +/* " 0x00001000 PF_MEMDIE (2.5)\n" ....................... */ + +// Sort Select specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions +// note: the leading newline below serves really dumb terminals; +// if there's no 'cursor_home', the screen will be a mess +// but this part will still be functional. +#define SORT_fields \ + "\n%sCurrent Sort Field\02: \01 %c \04 for window \01%s\06\n%s " \ + "" + +// Some extra explanatory text which accompanies the Sort display. +// note: the newlines cannot actually be used, they just serve as +// substring delimiters for the 'display_fields' routine. +#define SORT_xtra \ + "Note1:\n" \ + " If a selected sort field can't be\n" \ + " shown due to screen width or your\n" \ + " field order, the '<' and '>' keys\n" \ + " will be unavailable until a field\n" \ + " within viewable range is chosen.\n" \ + "\n" \ + "Note2:\n" \ + " Field sorting uses internal values,\n" \ + " not those in column display. Thus,\n" \ + " the TTY & WCHAN fields will violate\n" \ + " strict ASCII collating sequence.\n" \ + " (shame on you if WCHAN is chosen)\n" \ + "" + +// Colors Help specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define COLOR_help \ + "Help for color mapping\02 - %s\n" \ + "current window: \01%s\06\n" \ + "\n" \ + " color - 04:25:44 up 8 days, 50 min, 7 users, load average:\n" \ + " Tasks:\03 64 \02total,\03 2 \03running,\03 62 \02sleeping,\03 0 \02stopped,\03\n" \ + " Cpu(s):\03 76.5%% \02user,\03 11.2%% \02system,\03 0.0%% \02nice,\03 12.3%% \02idle\03\n" \ + " \01 Nasty Message! \04 -or- \01Input Prompt\05\n" \ + " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \ + " 17284 \10pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bash \10\n" \ + " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R < color -b \07\n" \ + " 11005 \10? \07 9 0 0.0 0:02.50 2852 1008 S amor -ses \10\n" \ + " available toggles: \01B\02 =disable bold globally (\01%s\02),\n" \ + " \01z\02 =color/mono (\01%s\02), \01b\02 =tasks \"bold\"/reverse (\01%s\02)\n" \ + "\n" \ + "Select \01target\02 as upper case letter:\n" \ + " S\02 = Summary Data,\01 M\02 = Messages/Prompts,\n" \ + " H\02 = Column Heads,\01 T\02 = Task Information\n" \ + "Select \01color\02 as number:\n" \ + " 0\02 = black,\01 1\02 = red, \01 2\02 = green,\01 3\02 = yellow,\n" \ + " 4\02 = blue, \01 5\02 = magenta,\01 6\02 = cyan, \01 7\02 = white\n" \ + "\n" \ + "Selected: \01target\02 \01 %c \04; \01color\02 \01 %d \04\n" \ + " press 'q' to abort changes to window '\01%s\02'\n" \ + " press 'a' or 'w' to commit & change another, to commit and end " \ + "" + +// Windows/Field Group Help specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define WINDOWS_help \ + "Help for Windows / Field Groups\02 - \"Current Window\" = \01 %s \06\n" \ + "\n" \ + ". Use multiple \01windows\02, each with separate config opts (color,fields,sort,etc)\n" \ + ". The 'current' window controls the \01Summary Area\02 and responds to your \01Commands\02\n" \ + " . that window's \01task display\02 can be turned \01Off\02 & \01On\02, growing/shrinking others\n" \ + " . with \01NO\02 task display, some commands will be \01disabled\02 ('i','R','n','c', etc)\n" \ + " until a \01different window\02 has been activated, making it the 'current' window\n" \ + ". You \01change\02 the 'current' window by: \01 1\02) cycling forward/backward;\01 2\02) choosing\n" \ + " a specific field group; or\01 3\02) exiting the color mapping screen\n" \ + ". Commands \01available anytime -------------\02\n" \ + " A . Alternate display mode toggle, show \01Single\02 / \01Multiple\02 windows\n" \ + " G . Choose another field group and make it 'current', or change now\n" \ + " by selecting a number from: \01 1\02 =%s;\01 2\02 =%s;\01 3\02 =%s; or\01 4\02 =%s\n" \ + ". Commands \01requiring\02 '\01A\02' mode\01 -------------\02\n" \ + " g . Change the \01Name\05 of the 'current' window/field group\n" \ + " \01*\04 a , w . Cycle through all four windows: '\01a\05' Forward; '\01w\05' Backward\n" \ + " \01*\04 - , _ . Show/Hide: '\01-\05' \01Current\02 window; '\01_\05' all \01Visible\02/\01Invisible\02\n" \ + " The screen will be divided evenly between task displays. But you can make\n" \ + " some \01larger\02 or \01smaller\02, using '\01n\02' and '\01i\02' commands. Then later you could:\n" \ + " \01*\04 = , + . Rebalance tasks: '\01=\05' \01Current\02 window; '\01+\05' \01Every\02 window\n" \ + " (this also forces the \01current\02 or \01every\02 window to become visible)\n" \ + "\n" \ + "In '\01A\02' mode, '\01*\04' keys are your \01essential\02 commands. Please try the '\01a\02' and '\01w\02'\n" \ + "commands plus the 'G' sub-commands NOW. Press to make 'Current' " \ + "" + + +/*###### For Piece of mind #############################################*/ + + /* just sanity check(s)... */ +#if USRNAMSIZ < GETBUFSIZ +# error "Jeeze, USRNAMSIZ Must NOT be less than GETBUFSIZ !" +#endif + + + +#endif /* _Itop */ diff --git a/uptime.1 b/uptime.1 new file mode 100644 index 0000000..3aae739 --- /dev/null +++ b/uptime.1 @@ -0,0 +1,34 @@ +.\" -*-Nroff-*- +.\" +.TH UPTIME 1 "26 Jan 1993" "Cohesive Systems" "Linux User's Manual" +.SH NAME +uptime \- Tell how long the system has been running. +.SH SYNOPSIS +.B uptime +.br +.BR uptime " [" "\-V" ] +.SH DESCRIPTION +.B uptime +gives a one line display of the following information. +The current time, +how long the system has been running, +how many users are currently logged on, +and the system load averages for the past 1, 5, and 15 minutes. +.sp +This is the same information contained in the header line displayed by +.BR w (1). +.SH FILES +.IR /var/run/utmp " information about who is currently logged on" +.br +.IR /proc " process information" +.SH AUTHORS +.B uptime +was written by Larry Greenfield and +Michael K. Johnson . + +Please send bug reports to +.SH "SEE ALSO" +.BR ps (1), +.BR top (1), +.BR utmp (5), +.BR w (1) diff --git a/uptime.c b/uptime.c new file mode 100644 index 0000000..6a4d8bd --- /dev/null +++ b/uptime.c @@ -0,0 +1,17 @@ +#include +#include +#include "proc/whattime.h" +#include "proc/version.h" + +int main(int argc, char *argv[]) { + if(argc == 1) { + print_uptime(); + return 0; + } + if((argc == 2) && (!strcmp(argv[1], "-V"))) { + display_version(); + return 0; + } + fprintf(stderr, "usage: uptime [-V]\n -V display version\n"); + return 1; +} diff --git a/v b/v new file mode 100755 index 0000000..f23230b --- /dev/null +++ b/v @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with embedded spaces in arguments. +# The "$@" won't break that into 2 arguments. +# +LD_LIBRARY_PATH=proc exec ./vmstat "$@" diff --git a/vmstat.8 b/vmstat.8 new file mode 100644 index 0000000..c059ec0 --- /dev/null +++ b/vmstat.8 @@ -0,0 +1,198 @@ +.\" This page Copyright (C) 1994 Henry Ware +.\" Distributed under the GPL, Copyleft 1994. +.TH VMSTAT 8 "27 July 1994 " "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual" +.SH NAME +vmstat \- Report virtual memory statistics +.SH SYNOPSIS +.ft B +.B vmstat +.RB [ "\-a" ] +.RB [ "\-n" ] +.RI [ delay " [ " count ]] +.br +.B vmstat +.RB [ "\-f" ] +.RB [ "\-s" ] +.RB [ "\-m" ] +.br +.B vmstat +.RB [ "\-S unit"] +.br +.B vmstat +.RB [ "\-d"] +.br +.B vmstat +.RB [ "\-p disk partition"] +.br +.B vmstat +.RB [ "\-V" ] +.SH DESCRIPTION +\fBvmstat\fP reports information about processes, memory, paging, +block IO, traps, and cpu activity. + +The first report produced gives averages since the last reboot. Additional +reports give information on a sampling period of length \fIdelay\fP. +The process and memory reports are instantaneous in either case. + +.SS Options +The \fB-a\fP switch displays active/inactive memory, given a 2.5.41 kernel or better. +.PP +The \fB-f\fP switch displays the number of forks since boot. +This includes the fork, vfork, and clone system calls, and is +equivalent to the total number of tasks created. Each process +is represented by one or more tasks, depending on thread usage. +This display does not repeat. +.PP +The \fB-m\fP displays slabinfo. +.PP +The \fB-n\fP switch causes the header to be displayed only once rather than periodically. +.PP +The \fB-s\fP switch displays a table of various event counters +and memory statistics. This display does not repeat. +.PP +.I delay +is the delay between updates in seconds. If no delay is specified, +only one report is printed with the average values since boot. +.PP +.I count +is the number of updates. If no count is specified and delay is +defined, \fIcount\fP defaults to infinity. +.PP +The \fB-d\fP reports disk statistics (2.5.70 or above required) +.PP +The \fB-p\fP followed by some partition name for detailed statistics (2.5.70 or above required) +.PP +The \fB-S\fP followed by k or K or m or M switches outputs between 1000, 1024, 1000000, or 1048576 bytes +.PP +The \fB-V\fP switch results in displaying version information. +.PP +.SH FIELD DESCRIPTION FOR VM MODE +.SS +.B "Procs" +.nf +r: The number of processes waiting for run time. +b: The number of processes in uninterruptible sleep. +.fi +.PP +.SS +.B "Memory" +.nf +swpd: the amount of virtual memory used. +free: the amount of idle memory. +buff: the amount of memory used as buffers. +cache: the amount of memory used as cache. +inact: the amount of inactive memory. (-a option) +active: the amount of active memory. (-a option) +.fi +.PP +.SS +.B "Swap" +.nf +si: Amount of memory swapped in from disk (/s). +so: Amount of memory swapped to disk (/s). +.fi +.PP +.SS +.B "IO" +.nf +bi: Blocks received from a block device (blocks/s). +bo: Blocks sent to a block device (blocks/s). +.fi +.PP +.SS +.B "System" +.nf +in: The number of interrupts per second, including the clock. +cs: The number of context switches per second. +.if +.PP +.SS +.B "CPU " +These are percentages of total CPU time. +.nf +us: Time spent running non-kernel code. (user time, including nice time) +sy: Time spent running kernel code. (system time) +id: Time spent idle. Prior to Linux 2.5.41, this includes IO-wait time. +wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle. +st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown. + +.PP +.SH FIELD DESCRIPTION FOR DISK MODE +.SS +.B "Reads" +.nf +total: Total reads completed successfully +merged: grouped reads (resulting in one I/O) +sectors: Sectors read successfully +ms: milliseconds spent reading +.fi +.PP +.SS +.B "Writes" +.nf +total: Total writes completed successfully +merged: grouped writes (resulting in one I/O) +sectors: Sectors written successfully +ms: milliseconds spent writing +.fi +.PP +.SS +.B "IO" +.nf +cur: I/O in progress +s: seconds spent for I/O +.fi + +.PP +.SH FIELD DESCRIPTION FOR DISK PARTITION MODE +.nf +reads: Total number of reads issued to this partition +read sectors: Total read sectors for partition +writes : Total number of writes issued to this partition +requested writes: Total number of write requests made for partition + +.fi + +.PP +.SH FIELD DESCRIPTION FOR SLAB MODE +.nf +cache: Cache name +num: Number of currently active objects +total: Total number of available objects +size: Size of each object +pages: Number of pages with at least one active object +totpages: Total number of allocated pages +pslab: Number of pages per slab +.fi + +.SH NOTES +.B "vmstat " +does not require special permissions. +.PP +These reports are intended to help identify system bottlenecks. Linux +.B "vmstat " +does not count itself as a running process. +.PP +All linux blocks are currently 1024 bytes. Old kernels may report +blocks as 512 bytes, 2048 bytes, or 4096 bytes. +.PP +Since procps 3.1.9, vmstat lets you choose units (k, K, m, M) default is K (1024 bytes) in the default mode +.PP +vmstat uses slabinfo 1.1 FIXME +.SH FILES +.ta +.nf +/proc/meminfo +/proc/stat +/proc/*/stat +.fi + +.SH "SEE ALSO" +iostat(1), sar(1), mpstat(1), ps(1), top(1), free(1) +.PP +.SH BUGS +Does not tabulate the block io per device or count the number of system calls. +.SH AUTHORS +.nf +Written by Henry Ware . +Fabian Fr\('ed\('erick (diskstat, slab, partitions...) diff --git a/vmstat.c b/vmstat.c new file mode 100644 index 0000000..f022928 --- /dev/null +++ b/vmstat.c @@ -0,0 +1,689 @@ +// old: "Copyright 1994 by Henry Ware . Copyleft same year." +// most code copyright 2002 Albert Cahalan +// +// 27/05/2003 (Fabian Frederick) : Add unit conversion + interface +// Export proc/stat access to libproc +// Adapt vmstat helpfile +// 31/05/2003 (Fabian) : Add diskstat support (/libproc) +// June 2003 (Fabian) : -S -s & -s -S patch +// June 2003 (Fabian) : -Adding diskstat against 3.1.9, slabinfo +// -patching 'header' in disk & slab +// July 2003 (Fabian) : -Adding disk partition output +// -Adding disk table +// -Syncing help / usage + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/sysinfo.h" +#include "proc/version.h" + +static unsigned long dataUnit=1024; +static char szDataUnit [16]; +#define UNIT_B 1 +#define UNIT_k 1000 +#define UNIT_K 1024 +#define UNIT_m 1000000 +#define UNIT_M 1048576 + +#define VMSTAT 0 +#define DISKSTAT 0x00000001 +#define VMSUMSTAT 0x00000002 +#define SLABSTAT 0x00000004 +#define PARTITIONSTAT 0x00000008 +#define DISKSUMSTAT 0x00000010 + +static int statMode=VMSTAT; + +#define FALSE 0 +#define TRUE 1 + +static int a_option; /* "-a" means "show active/inactive" */ + +static unsigned sleep_time = 1; +static unsigned long num_updates; + +static unsigned int height; // window height +static unsigned int moreheaders=TRUE; + + +///////////////////////////////////////////////////////////////////////// + +static void usage(void) NORETURN; +static void usage(void) { + fprintf(stderr,"usage: vmstat [-V] [-n] [delay [count]]\n"); + fprintf(stderr," -V prints version.\n"); + fprintf(stderr," -n causes the headers not to be reprinted regularly.\n"); + fprintf(stderr," -a print inactive/active page stats.\n"); + fprintf(stderr," -d prints disk statistics\n"); + fprintf(stderr," -D prints disk table\n"); + fprintf(stderr," -p prints disk partition statistics\n"); + fprintf(stderr," -s prints vm table\n"); + fprintf(stderr," -m prints slabinfo\n"); + fprintf(stderr," -S unit size\n"); + fprintf(stderr," delay is the delay between updates in seconds. \n"); + fprintf(stderr," unit size k:1000 K:1024 m:1000000 M:1048576 (default is K)\n"); + fprintf(stderr," count is the number of updates.\n"); + exit(EXIT_FAILURE); +} + +///////////////////////////////////////////////////////////////////////////// + +#if 0 +// produce: " 6 ", "123 ", "123k ", etc. +static int format_1024(unsigned long long val64, char *restrict dst){ + unsigned oldval; + const char suffix[] = " kmgtpe"; + unsigned level = 0; + unsigned val32; + + if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do + val32 = val64; + return sprintf(dst,"%3u ",val32); + } + + while(val64 > 0xffffffffull){ + level++; + val64 /= 1024; + } + + val32 = val64; + + while(val32 > 999){ + level++; + oldval = val32; + val32 /= 1024; + } + + if(val32 < 10){ + unsigned fract = (oldval % 1024) * 10 / 1024; + return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]); + } + return sprintf(dst, "%3u%c ", val32, suffix[level]); +} + + +// produce: " 6 ", "123 ", "123k ", etc. +static int format_1000(unsigned long long val64, char *restrict dst){ + unsigned oldval; + const char suffix[] = " kmgtpe"; + unsigned level = 0; + unsigned val32; + + if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do + val32 = val64; + return sprintf(dst,"%3u ",val32); + } + + while(val64 > 0xffffffffull){ + level++; + val64 /= 1000; + } + + val32 = val64; + + while(val32 > 999){ + level++; + oldval = val32; + val32 /= 1000; + } + + if(val32 < 10){ + unsigned fract = (oldval % 1000) / 100; + return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]); + } + return sprintf(dst, "%3u%c ", val32, suffix[level]); +} +#endif + +//////////////////////////////////////////////////////////////////////////// + +static void new_header(void){ + printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n"); + printf( + "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s\n", + "r","b", + "swpd", "free", a_option?"inact":"buff", a_option?"active":"cache", + "si","so", + "bi","bo", + "in","cs", + "us","sy","id","wa" + ); +} + +//////////////////////////////////////////////////////////////////////////// + +static unsigned long unitConvert(unsigned int size){ + float cvSize; + cvSize=(float)size/dataUnit*((statMode==SLABSTAT)?1:1024); + return ((unsigned long) cvSize); +} + +//////////////////////////////////////////////////////////////////////////// + +static void new_format(void) { + const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n"; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i; + unsigned int hz = Hertz; + unsigned int running,blocked,dummy_1,dummy_2; + jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2], cpu_xxx[2], cpu_yyy[2], cpu_zzz[2]; + jiff duse, dsys, didl, diow, dstl, Div, divo2; + unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; + unsigned int intr[2], ctxt[2]; + unsigned int sleep_half; + unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul; + int debt = 0; // handle idle ticks running backwards + + sleep_half=(sleep_time/2); + new_header(); + meminfo(); + + getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl,cpu_iow,cpu_xxx,cpu_yyy,cpu_zzz, + pgpgin,pgpgout,pswpin,pswpout, + intr,ctxt, + &running,&blocked, + &dummy_1, &dummy_2); + + duse= *cpu_use + *cpu_nic; + dsys= *cpu_sys + *cpu_xxx + *cpu_yyy; + didl= *cpu_idl; + diow= *cpu_iow; + dstl= *cpu_zzz; + Div= duse+dsys+didl+diow+dstl; + divo2= Div/2UL; + printf(format, + running, blocked, + unitConvert(kb_swap_used), unitConvert(kb_main_free), + unitConvert(a_option?kb_inactive:kb_main_buffers), + unitConvert(a_option?kb_active:kb_main_cached), + (unsigned)( (*pswpin * unitConvert(kb_per_page) * hz + divo2) / Div ), + (unsigned)( (*pswpout * unitConvert(kb_per_page) * hz + divo2) / Div ), + (unsigned)( (*pgpgin * hz + divo2) / Div ), + (unsigned)( (*pgpgout * hz + divo2) / Div ), + (unsigned)( (*intr * hz + divo2) / Div ), + (unsigned)( (*ctxt * hz + divo2) / Div ), + (unsigned)( (100*duse + divo2) / Div ), + (unsigned)( (100*dsys + divo2) / Div ), + (unsigned)( (100*didl + divo2) / Div ), + (unsigned)( (100*diow + divo2) / Div ) /* , + (unsigned)( (100*dstl + divo2) / Div ) */ + ); + + for(i=1;ireads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes); + fflush(stdout); + free(disks); + free(partitions); + for(j=1; jreads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes); + fflush(stdout); + free(disks); + free(partitions); + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +static void diskheader(void){ + printf("disk- ------------reads------------ ------------writes----------- -----IO------\n"); + + printf("%5s %6s %6s %7s %7s %6s %6s %7s %7s %6s %6s\n", + " ", "total", "merged","sectors","ms","total","merged","sectors","ms","cur","sec"); + +} + +//////////////////////////////////////////////////////////////////////////// + +static void diskformat(void){ + FILE *fDiskstat; + struct disk_stat *disks; + struct partition_stat *partitions; + unsigned long ndisks,i,j,k; + const char format[]="%-5s %6u %6u %7llu %7u %6u %6u %7llu %7u %6u %6u\n"; + if ((fDiskstat=fopen("/proc/diskstats", "rb"))){ + fclose(fDiskstat); + ndisks=getdiskstat(&disks,&partitions); + for(k=0; k 0) + rows = win.ws_row; + + return rows; +} + +//////////////////////////////////////////////////////////////////////////// + +int main(int argc, char *argv[]) { + char partition[16]; + argc=0; /* redefined as number of integer arguments */ + for (argv++;*argv;argv++) { + if ('-' ==(**argv)) { + switch (*(++(*argv))) { + + case 'V': + display_version(); + exit(0); + case 'd': + statMode |= DISKSTAT; + break; + case 'a': + /* active/inactive mode */ + a_option=1; + break; + case 'f': + // FIXME: check for conflicting args + fork_format(); + exit(0); + case 'm': + statMode |= SLABSTAT; + break; + case 'D': + statMode |= DISKSUMSTAT; + break; + case 'n': + /* print only one header */ + moreheaders=FALSE; + break; + case 'p': + statMode |= PARTITIONSTAT; + if (argv[1]){ + char *cp = *++argv; + if(!memcmp(cp,"/dev/",5)) cp += 5; + snprintf(partition, sizeof partition, "%s", cp); + }else{ + fprintf(stderr, "-p requires an argument\n"); + exit(EXIT_FAILURE); + } + break; + case 'S': + if (argv[1]){ + ++argv; + if (!strcmp(*argv, "k")) dataUnit=UNIT_k; + else if (!strcmp(*argv, "K")) dataUnit=UNIT_K; + else if (!strcmp(*argv, "m")) dataUnit=UNIT_m; + else if (!strcmp(*argv, "M")) dataUnit=UNIT_M; + else {fprintf(stderr, "-S requires k, K, m or M (default is kb)\n"); + exit(EXIT_FAILURE); + } + strcpy(szDataUnit, *argv); + }else {fprintf(stderr, "-S requires an argument\n"); + exit(EXIT_FAILURE); + } + break; + case 's': + statMode |= VMSUMSTAT; + break; + default: + /* no other aguments defined yet. */ + usage(); + } + }else{ + argc++; + switch (argc) { + case 1: + if ((sleep_time = atoi(*argv)) == 0) + usage(); + num_updates = ULONG_MAX; + break; + case 2: + num_updates = atol(*argv); + break; + default: + usage(); + } /* switch */ + } +} + if (moreheaders) { + int tmp=winhi()-3; + height=((tmp>0)?tmp:22); + } + setlinebuf(stdout); + switch(statMode){ + case(VMSTAT): new_format(); + break; + case(VMSUMSTAT): sum_format(); + break; + case(DISKSTAT): diskformat(); + break; + case(PARTITIONSTAT): if(diskpartition_format(partition)==-1) + printf("Partition was not found\n"); + break; + case(SLABSTAT): slabformat(); + break; + case(DISKSUMSTAT): disksum_format(); + break; + default: usage(); + break; + } + return 0; +} + + diff --git a/w.1 b/w.1 new file mode 100644 index 0000000..d57a9b5 --- /dev/null +++ b/w.1 @@ -0,0 +1,84 @@ +.\" -*-Nroff-*- +.\" +.TH W 1 "8 Dec 1993 " " " "Linux User's Manual" +.SH NAME +w \- Show who is logged on and what they are doing. +.SH SYNOPSIS +.B w \- +.RB [ husfV ] +.RI [ user ] +.SH DESCRIPTION +.B "w " +displays information about the users currently on the machine, +and their processes. +The header shows, in this order, the current time, +how long the system has been running, +how many users are currently logged on, +and the system load averages for the past 1, 5, and 15 minutes. +.sp +The following entries are displayed for each user: +login name, the tty name, the remote host, login time, idle time, JCPU, PCPU, +and the command line of their current process. +.sp +The JCPU time is the time used by all processes attached to the tty. It +does not include past background jobs, but does include currently +running background jobs. +.sp +The PCPU time is the time used by the current process, named in the "what" +field. + +.PP +.SH "COMMAND\-LINE OPTIONS" +.TP 0.5i +.B "\-h " +Don't print the header. +.TP 0.5i +.B "\-u " +Ignores the username while figuring out the current process and cpu +times. To demonstrate this, do a "su" and do a "w" and a "w -u". +.TP 0.5i +.B "\-s " +Use the short format. +Don't print the login time, JCPU or PCPU times. +.TP 0.5i +.B "\-f " +Toggle printing the +.B from +(remote hostname) field. The default as +released is for the +.B from +field to not be printed, although your system administrator or +distribution maintainer may have compiled a version in which the +.B from +field is shown by default. +.TP 0.5i +.B "\-V " +Display version information. +.TP 0.5i +.B "user " +Show information about the specified user only. + +.SH FILES +.TP +.I /var/run/utmp +information about who is currently logged on +.TP +.I /proc +process information +.PP + +.SH "SEE ALSO" +.BR free (1), +.BR ps (1), +.BR top (1), +.BR uptime (1), +.BR utmp (5), +.BR who (1) + +.SH AUTHORS +.B w +was re-written almost entirely by Charles Blake, based on the version by Larry +Greenfield and Michael K. Johnson +. + +Please send bug reports to diff --git a/w.c b/w.c new file mode 100644 index 0000000..1b2a0fc --- /dev/null +++ b/w.c @@ -0,0 +1,307 @@ +/* w - show what logged in users are doing. Almost entirely rewritten from + * scratch by Charles Blake circa June 1996. Some vestigal traces of the + * original may exist. That was done in 1993 by Larry Greenfield with some + * fixes by Michael K. Johnson. + * + * Changes by Albert Cahalan, 2002. + */ +#include "proc/version.h" +#include "proc/whattime.h" +#include "proc/readproc.h" +#include "proc/devname.h" +#include "proc/procps.h" +#include "proc/sysinfo.h" +#include "proc/escape.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ignoreuser = 0; /* for '-u' */ +static proc_t **procs; /* our snapshot of the process table */ + +typedef struct utmp utmp_t; + +#ifdef W_SHOWFROM +# define FROM_STRING "on" +#else +# define FROM_STRING "off" +#endif + +/* Uh... same thing as UT_NAMESIZE */ +#define USERSZ (sizeof u->ut_user) + + +/* This routine is careful since some programs leave utmp strings + * unprintable. Always outputs at least 16 chars padded with spaces + * on the right if necessary. + */ +static void print_host(const char *restrict host, int len) { + const char *last; + int width = 0; + + /* FIXME: there should really be a way to configure this... */ + /* for now, we'll just limit it to the 16 that the libc5 version + * of utmp uses. + */ + if (len > 16) len = 16; + last = host + len; + for ( ; host < last ; host++){ + if (isprint(*host) && *host != ' ') { + fputc(*host, stdout); + ++width; + } else { + break; + } + } + // space-fill, and a '-' too if needed to ensure the column exists + if(width < 16) fputs("- "+width, stdout); +} + +/***** compact 7 char format for time intervals (belongs in libproc?) */ +static void print_time_ival7(time_t t, int centi_sec, FILE* fout) { + if((long)t < (long)0){ /* system clock changed? */ + printf(" ? "); + return; + } + if (t >= 48*60*60) /* > 2 days */ + fprintf(fout, " %2ludays", t/(24*60*60)); + else if (t >= 60*60) /* > 1 hour */ + fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60)); + else if (t > 60) /* > 1 minute */ + fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60); + else + fprintf(fout, " %2lu.%02us", t, centi_sec); +} + +/**** stat the device file to get an idle time */ +static time_t idletime(const char *restrict const tty) { + struct stat sbuf; + if (stat(tty, &sbuf) != 0) + return 0; + return time(NULL) - sbuf.st_atime; +} + +/***** 7 character formatted login time */ +static void print_logintime(time_t logt, FILE* fout) { + char weekday[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, + month [][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" }; + time_t curt; + struct tm *logtm, *curtm; + int today; + + curt = time(NULL); + curtm = localtime(&curt); + /* localtime returns a pointer to static memory */ + today = curtm->tm_yday; + logtm = localtime(&logt); + if (curt - logt > 12*60*60 && logtm->tm_yday != today) { + if (curt - logt > 6*24*60*60) + fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon], + logtm->tm_year % 100); + else + fprintf(fout, " %3s%02d ", weekday[logtm->tm_wday], logtm->tm_hour); + } else { + fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min); + } +} + + +/* This function scans the process table accumulating total cpu times for + * any processes "associated" with this login session. It also searches + * for the "best" process to report as "(w)hat" the user for that login + * session is doing currently. This the essential core of 'w'. + */ +static const proc_t *getproc(const utmp_t *restrict const u, const char *restrict const tty, unsigned long long *restrict const jcpu, int *restrict const found_utpid) { + int line; + proc_t **pptr = procs; + const proc_t *best = NULL; + const proc_t *secondbest = NULL; + unsigned uid = ~0U; + + *found_utpid = 0; + if(!ignoreuser){ + char buf[UT_NAMESIZE+1]; + struct passwd *passwd_data; /* pointer to static data */ + strncpy(buf,u->ut_user,UT_NAMESIZE); + buf[UT_NAMESIZE] = '\0'; + passwd_data = getpwnam(buf); + if(!passwd_data) return NULL; + uid = passwd_data->pw_uid; + /* OK to have passwd_data go out of scope here */ + } + line = tty_to_dev(tty); + *jcpu = 0; + for(; *pptr; pptr++) { + const proc_t *restrict const tmp = *pptr; + if(unlikely(tmp->tgid == u->ut_pid)) { + *found_utpid = 1; + best = tmp; + } + if(tmp->tty != line) continue; + (*jcpu) += tmp->utime + tmp->stime; + secondbest = tmp; + /* same time-logic here as for "best" below */ + if(! (secondbest && tmp->start_time <= secondbest->start_time) ){ + secondbest = tmp; + } + if(!ignoreuser && uid != tmp->euid && uid != tmp->ruid) continue; + if(tmp->tgid != tmp->tpgid) continue; + if(best && tmp->start_time <= best->start_time) continue; + best = tmp; + } + return best ? best : secondbest; +} + + +/***** showinfo */ +static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) { + unsigned long long jcpu; + int ut_pid_found; + unsigned i; + char uname[USERSZ + 1] = "", + tty[5 + sizeof u->ut_line + 1] = "/dev/"; + const proc_t *best; + + for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */ + if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/')) + tty[i+5] = u->ut_line[i]; + else + tty[i+5] = '\0'; + + best = getproc(u, tty + 5, &jcpu, &ut_pid_found); + + /* just skip if stale utmp entry (i.e. login proc doesn't exist). If there + * is a desire a cmdline flag could be added to optionally show it with a + * prefix of (stale) in front of cmd or something like that. + */ + if (!ut_pid_found) + return; + + strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */ + if (formtype) { + printf("%-9.8s%-9.8s", uname, u->ut_line); + if (from) + print_host(u->ut_host, sizeof u->ut_host); + print_logintime(u->ut_time, stdout); + if (*u->ut_line == ':') /* idle unknown for xdm logins */ + printf(" ?xdm? "); + else + print_time_ival7(idletime(tty), 0, stdout); + print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout); + if (best) { + unsigned long long pcpu = best->utime + best->stime; + print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout); + } else + printf(" ? "); + } else { + printf("%-9.8s%-9.8s", u->ut_user, u->ut_line); + if (from) + print_host(u->ut_host, sizeof u->ut_host); + if (*u->ut_line == ':') /* idle unknown for xdm logins */ + printf(" ?xdm? "); + else + print_time_ival7(idletime(tty), 0, stdout); + } + fputs(" ", stdout); + if (likely(best)) { + char cmdbuf[512]; + escape_command(cmdbuf, best, sizeof cmdbuf, &maxcmd, ESC_ARGS); + fputs(cmdbuf,stdout); + } else { + printf("-"); + } + fputc('\n', stdout); +} + +/***** main */ +int main(int argc, char **argv) { + char *user = NULL; + utmp_t *u; + struct winsize win; + int header=1, longform=1, from=1, args, maxcmd=80, ch; + +#ifndef W_SHOWFROM + from = 0; +#endif + + setlocale(LC_ALL, ""); + for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++) + switch (ch) { + case 'h': header = 0; break; + case 'l': longform = 1; break; + case 's': longform = 0; break; + case 'f': from = !from; break; + case 'V': display_version(); exit(0); + case 'u': ignoreuser = 1; break; + default: + printf("usage: w -hlsufV [user]\n" + " -h skip header\n" + " -l long listing (default)\n" + " -s short listing\n" + " -u ignore uid of processes\n" + " -f toggle FROM field (default %s)\n" + " -V display version\n", FROM_STRING); + exit(1); + } + + if ((argv[optind])) + user = (argv[optind]); + + if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) + maxcmd = win.ws_col; + if (maxcmd < 71) { + fprintf(stderr, "%d column window is too narrow\n", maxcmd); + exit(1); + } + maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0); + if (maxcmd < 3) + fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col); + + procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT); + + if (header) { /* print uptime and headers */ + print_uptime(); + printf("USER TTY "); + if (from) + printf("FROM "); + if (longform) + printf(" LOGIN@ IDLE JCPU PCPU WHAT\n"); + else + printf(" IDLE WHAT\n"); + } + + utmpname(UTMP_FILE); + setutent(); + if (user) { + for (;;) { + u = getutent(); + if (unlikely(!u)) break; + if (u->ut_type != USER_PROCESS) continue; + if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from); + } + } else { + for (;;) { + u = getutent(); + if (unlikely(!u)) break; + if (u->ut_type != USER_PROCESS) continue; + if (*u->ut_user) showinfo(u, longform, maxcmd, from); + } + } + endutent(); + + return 0; +} diff --git a/watch.1 b/watch.1 new file mode 100644 index 0000000..41323f4 --- /dev/null +++ b/watch.1 @@ -0,0 +1,87 @@ +.TH WATCH 1 "1999 Apr 3" " " "Linux User's Manual" +.SH NAME +watch \- execute a program periodically, showing output fullscreen +.SH SYNOPSIS +.B watch +.I [\-dhvt] [\-n ] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=] [\-\-no\-title] [\-\-version] +.SH DESCRIPTION +.BR watch +runs +.I command +repeatedly, displaying its output (the first screenfull). This allows you to +watch the program output change over time. By default, the program is run +every 2 seconds; use +.I -n +or +.I --interval +to specify a different interval. +.PP +The +.I -d +or +.I --differences +flag will highlight the differences between successive updates. The +.I --cumulative +option makes highlighting "sticky", presenting a running display of all +positions that have ever changed. The +.I -t +or +.I --no-title +option turns off the header showing the interval, command, and current +time at the top of the display, as well as the following blank line. +.PP +.BR watch +will run until interrupted. +.SH NOTE +Note that +.I command +is given to "sh -c" +which means that you may need to use extra quoting to get the desired effect. +.PP +Note that POSIX option processing is used (i.e., option processing stops at +the first non-option argument). This means that flags after +.I command +don't get interpreted by +.BR watch +itself. +.SH EXAMPLES +.PP +To watch for mail, you might do +.IP +watch \-n 60 from +.PP +To watch the contents of a directory change, you could use +.IP +watch \-d ls \-l +.PP +If you're only interested in files owned by user joe, you might use +.IP +watch \-d 'ls \-l | fgrep joe' +.PP +To see the effects of quoting, try these out +.IP +watch echo $$ +.IP +watch echo '$$' +.IP +watch echo "'"'$$'"'" +.PP +You can watch for your administrator to install the latest kernel with +.IP +watch uname -r +.PP +(Just kidding.) +.SH BUGS +Upon terminal resize, the screen will not be correctly repainted until the +next scheduled update. All +.I --differences +highlighting is lost on that update as well. +.PP +Non-printing characters are stripped from program output. Use "cat -v" as +part of the command pipeline if you want to see them. +.SH AUTHORS +The original +.B watch +was written by Tony Rems in 1991, with mods and +corrections by Francois Pinard. It was reworked and new features added by +Mike Coleman in 1999. diff --git a/watch.c b/watch.c new file mode 100644 index 0000000..5841168 --- /dev/null +++ b/watch.c @@ -0,0 +1,326 @@ +/* watch -- execute a program repeatedly, displaying output fullscreen + * + * Based on the original 1991 'watch' by Tony Rems + * (with mods and corrections by Francois Pinard). + * + * Substantially reworked, new features (differences option, SIGWINCH + * handling, unlimited command length, long line handling) added Apr 1999 by + * Mike Coleman . + * + * Changes by Albert Cahalan, 2002-2003. + */ + +#define VERSION "0.2.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/procps.h" + +#ifdef FORCE_8BIT +#undef isprint +#define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) ) +#endif + +static struct option longopts[] = { + {"differences", optional_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"interval", required_argument, 0, 'n'}, + {"no-title", no_argument, 0, 't'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} +}; + +static char usage[] = + "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=] [--no-title] [--version] \n"; + +static char *progname; + +static int curses_started = 0; +static int height = 24, width = 80; +static int screen_size_changed = 0; +static int first_screen = 1; +static int show_title = 2; // number of lines used, 2 or 0 + +#define min(x,y) ((x) > (y) ? (y) : (x)) + +static void do_usage(void) NORETURN; +static void do_usage(void) +{ + fprintf(stderr, usage, progname); + exit(1); +} + +static void do_exit(int status) NORETURN; +static void do_exit(int status) +{ + if (curses_started) + endwin(); + exit(status); +} + +/* signal handler */ +static void die(int notused) NORETURN; +static void die(int notused) +{ + (void) notused; + do_exit(0); +} + +static void +winch_handler(int notused) +{ + (void) notused; + screen_size_changed = 1; +} + +static char env_col_buf[24]; +static char env_row_buf[24]; +static int incoming_cols; +static int incoming_rows; + +static void +get_terminal_size(void) +{ + struct winsize w; + if(!incoming_cols){ // have we checked COLUMNS? + const char *s = getenv("COLUMNS"); + incoming_cols = -1; + if(s && *s){ + long t; + char *endptr; + t = strtol(s, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)666)) incoming_cols = (int)t; + width = incoming_cols; + snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width); + putenv(env_col_buf); + } + } + if(!incoming_rows){ // have we checked LINES? + const char *s = getenv("LINES"); + incoming_rows = -1; + if(s && *s){ + long t; + char *endptr; + t = strtol(s, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)666)) incoming_rows = (int)t; + height = incoming_rows; + snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height); + putenv(env_row_buf); + } + } + if (incoming_cols<0 || incoming_rows<0){ + if (ioctl(2, TIOCGWINSZ, &w) == 0) { + if (incoming_rows<0 && w.ws_row > 0){ + height = w.ws_row; + snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height); + putenv(env_row_buf); + } + if (incoming_cols<0 && w.ws_col > 0){ + width = w.ws_col; + snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width); + putenv(env_col_buf); + } + } + } +} + +int +main(int argc, char *argv[]) +{ + int optc; + int option_differences = 0, + option_differences_cumulative = 0, + option_help = 0, option_version = 0; + double interval = 2; + char *command; + int command_length = 0; /* not including final \0 */ + + setlocale(LC_ALL, ""); + progname = argv[0]; + + while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0)) + != EOF) { + switch (optc) { + case 'd': + option_differences = 1; + if (optarg) + option_differences_cumulative = 1; + break; + case 'h': + option_help = 1; + break; + case 't': + show_title = 0; + break; + case 'n': + { + char *str; + interval = strtod(optarg, &str); + if (!*optarg || *str) + do_usage(); + if(interval < 0.1) + interval = 0.1; + if(interval > ~0u/1000000) + interval = ~0u/1000000; + } + break; + case 'v': + option_version = 1; + break; + default: + do_usage(); + break; + } + } + + if (option_version) { + fprintf(stderr, "%s\n", VERSION); + if (!option_help) + exit(0); + } + + if (option_help) { + fprintf(stderr, usage, progname); + fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr); + fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr); + fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr); + fputs(" -n, --interval=\t\tseconds to wait between updates\n", stderr); + fputs(" -v, --version\t\t\t\tprint the version number\n", stderr); + fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr); + exit(0); + } + + if (optind >= argc) + do_usage(); + + command = strdup(argv[optind++]); + command_length = strlen(command); + for (; optind < argc; optind++) { + char *endp; + int s = strlen(argv[optind]); + command = realloc(command, command_length + s + 2); /* space and \0 */ + endp = command + command_length; + *endp = ' '; + memcpy(endp + 1, argv[optind], s); + command_length += 1 + s; /* space then string length */ + command[command_length] = '\0'; + } + + get_terminal_size(); + + /* Catch keyboard interrupts so we can put tty back in a sane state. */ + signal(SIGINT, die); + signal(SIGTERM, die); + signal(SIGHUP, die); + signal(SIGWINCH, winch_handler); + + /* Set up tty for curses use. */ + curses_started = 1; + initscr(); + nonl(); + noecho(); + cbreak(); + + for (;;) { + time_t t = time(NULL); + char *ts = ctime(&t); + int tsl = strlen(ts); + char *header; + FILE *p; + int x, y; + int oldeolseen = 1; + + if (screen_size_changed) { + get_terminal_size(); + resizeterm(height, width); + clear(); + /* redrawwin(stdscr); */ + screen_size_changed = 0; + first_screen = 1; + } + + if (show_title) { + // left justify interval and command, + // right justify time, clipping all to fit window width + asprintf(&header, "Every %.1fs: %.*s", + interval, min(width - 1, command_length), command); + mvaddstr(0, 0, header); + if (strlen(header) > (size_t) (width - tsl - 1)) + mvaddstr(0, width - tsl - 4, "... "); + mvaddstr(0, width - tsl + 1, ts); + free(header); + } + + if (!(p = popen(command, "r"))) { + perror("popen"); + do_exit(2); + } + + for (y = show_title; y < height; y++) { + int eolseen = 0, tabpending = 0; + for (x = 0; x < width; x++) { + int c = ' '; + int attr = 0; + + if (!eolseen) { + /* if there is a tab pending, just spit spaces until the + next stop instead of reading characters */ + if (!tabpending) + do + c = getc(p); + while (c != EOF && !isprint(c) + && c != '\n' + && c != '\t'); + if (c == '\n') + if (!oldeolseen && x == 0) { + x = -1; + continue; + } else + eolseen = 1; + else if (c == '\t') + tabpending = 1; + if (c == EOF || c == '\n' || c == '\t') + c = ' '; + if (tabpending && (((x + 1) % 8) == 0)) + tabpending = 0; + } + move(y, x); + if (option_differences) { + chtype oldch = inch(); + char oldc = oldch & A_CHARTEXT; + attr = !first_screen + && ((char)c != oldc + || + (option_differences_cumulative + && (oldch & A_ATTRIBUTES))); + } + if (attr) + standout(); + addch(c); + if (attr) + standend(); + } + oldeolseen = eolseen; + } + + pclose(p); + + first_screen = 0; + refresh(); + usleep(interval * 1000000); + } + + endwin(); + + return 0; +} -- 2.7.4