From 03a9b5a30fb773ee4d1568064b14e075ba186c90 Mon Sep 17 00:00:00 2001 From: csmall <> Date: Fri, 1 Feb 2002 22:47:29 +0000 Subject: [PATCH] procps 010114 --- .cvsignore.patch | 18 + BUGS | 81 ++ COPYING | 339 ++++++++ COPYING.LIB | 481 ++++++++++++ INSTALL | 73 ++ Makefile | 220 ++++++ NEWS | 645 +++++++++++++++ TODO | 176 +++++ XConsole | 5 + free.1 | 45 ++ free.c | 80 ++ gfinger.p | 216 +++++ glibc.patch | 32 + kill.1 | 96 +++ minimal.c | 486 ++++++++++++ oldps.1 | 356 +++++++++ oldps.c | 660 ++++++++++++++++ pgrep.1 | 158 ++++ pgrep.c | 605 ++++++++++++++ pkill.1 | 1 + pmap.c | 34 + proc/.cvsignore.patch | 7 + proc/COPYING | 481 ++++++++++++ proc/Makefile | 98 +++ proc/alloc.c | 49 ++ proc/compare.c | 304 +++++++ proc/compare.h | 9 + proc/devname.c | 211 +++++ proc/devname.h | 7 + proc/ksym.c | 548 +++++++++++++ proc/output.c | 52 ++ proc/procps.h | 27 + proc/pwcache.c | 74 ++ proc/readproc.c | 543 +++++++++++++ proc/readproc.h | 200 +++++ proc/sig.c | 234 ++++++ proc/sig.h | 19 + proc/status.c | 29 + proc/status.h | 4 + proc/sysinfo.c | 325 ++++++++ proc/sysinfo.h | 40 + proc/tree.h | 15 + proc/version.c | 43 + proc/version.h | 23 + proc/whattime.c | 89 +++ proc/whattime.h | 9 + procps.lsm | 16 + procps.spec | 105 +++ ps/.cvsignore.patch | 5 + ps/COPYING | 481 ++++++++++++ ps/HACKING | 53 ++ ps/Makefile | 35 + ps/TRANSLATION | 28 + ps/common.h | 324 ++++++++ ps/display.c | 398 ++++++++++ ps/escape.c | 106 +++ ps/global.c | 422 ++++++++++ ps/help.c | 49 ++ ps/it | 35 + ps/output.c | 1608 +++++++++++++++++++++++++++++++++++++ ps/p | 6 + ps/parser.c | 1123 ++++++++++++++++++++++++++ ps/ps.1 | 521 ++++++++++++ ps/regression | 26 + ps/select.c | 146 ++++ ps/sortformat.c | 875 +++++++++++++++++++++ ps/stacktrace.c | 165 ++++ skill.1 | 118 +++ skill.c | 557 +++++++++++++ snice.1 | 1 + sysctl.8 | 74 ++ sysctl.c | 437 +++++++++++ sysctl.conf.5 | 51 ++ t | 6 + tload.1 | 50 ++ tload.c | 156 ++++ tmp-junk.c | 730 +++++++++++++++++ top.1 | 456 +++++++++++ top.c | 1746 +++++++++++++++++++++++++++++++++++++++++ top.desktop | 6 + top.h | 228 ++++++ uptime.1 | 34 + uptime.c | 9 + utmp.c | 178 +++++ vmstat.8 | 105 +++ vmstat.c | 278 +++++++ w.1 | 84 ++ w.c | 298 +++++++ watch.1 | 82 ++ watch.c | 277 +++++++ 90 files changed, 20735 insertions(+) create mode 100644 .cvsignore.patch create mode 100644 BUGS create mode 100644 COPYING create mode 100644 COPYING.LIB create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 NEWS create mode 100644 TODO create mode 100755 XConsole create mode 100644 free.1 create mode 100644 free.c create mode 100644 gfinger.p create mode 100644 glibc.patch create mode 100644 kill.1 create mode 100644 minimal.c create mode 100644 oldps.1 create mode 100644 oldps.c create mode 100644 pgrep.1 create mode 100644 pgrep.c create mode 100644 pkill.1 create mode 100644 pmap.c create mode 100644 proc/.cvsignore.patch create mode 100644 proc/COPYING create mode 100644 proc/Makefile create mode 100644 proc/alloc.c create mode 100644 proc/compare.c create mode 100644 proc/compare.h create mode 100644 proc/devname.c create mode 100644 proc/devname.h create mode 100644 proc/ksym.c create mode 100644 proc/output.c create mode 100644 proc/procps.h create mode 100644 proc/pwcache.c 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/status.c create mode 100644 proc/status.h create mode 100644 proc/sysinfo.c create mode 100644 proc/sysinfo.h create mode 100644 proc/tree.h create mode 100644 proc/version.c create mode 100644 proc/version.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/.cvsignore.patch create mode 100644 ps/COPYING create mode 100644 ps/HACKING create mode 100755 ps/Makefile create mode 100644 ps/TRANSLATION create mode 100644 ps/common.h create mode 100644 ps/display.c create mode 100644 ps/escape.c create mode 100644 ps/global.c create mode 100644 ps/help.c create mode 100644 ps/it 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 ps/stacktrace.c create mode 100644 skill.1 create mode 100644 skill.c create mode 100644 snice.1 create mode 100644 sysctl.8 create mode 100644 sysctl.c create mode 100644 sysctl.conf.5 create mode 100755 t create mode 100644 tload.1 create mode 100644 tload.c create mode 100644 tmp-junk.c create mode 100644 top.1 create mode 100644 top.c create mode 100644 top.desktop create mode 100644 top.h create mode 100644 uptime.1 create mode 100644 uptime.c create mode 100644 utmp.c 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/.cvsignore.patch b/.cvsignore.patch new file mode 100644 index 00000000..216dc03b --- /dev/null +++ b/.cvsignore.patch @@ -0,0 +1,18 @@ +diff -Naur procps-2.0.6/.cvsignore procps-2.0.7/.cvsignore +--- procps-2.0.6/.cvsignore Wed Dec 31 19:00:00 1969 ++++ procps-2.0.7/.cvsignore Fri Jul 14 16:45:01 2000 +@@ -0,0 +1,14 @@ ++skill ++kill ++oldps ++uptime ++tload ++free ++w ++top ++vmstat ++watch ++snice ++pgrep ++pkill ++sysctl diff --git a/BUGS b/BUGS new file mode 100644 index 00000000..df7ced42 --- /dev/null +++ b/BUGS @@ -0,0 +1,81 @@ +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 acahalan@cs.uml.edu + + +What to send +============ +It is much more useful to me if a program really crases to recompile it +with make "CC=gcc -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. + +It might be nice to get rid of miscellaneous compiler warnings, but +don't bend over backwards to do it. + + +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/kvers.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(1,1,30)) + /* tty field is only a minor */ +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. + +If you send me patches which are specific to *compiling* on a particular +version of Linux include a "#if LINUX_VERSION_CODE > 1*0x10000+3*0x100+54" +markup of the patch so that the package may be compiled with older +kernels as well as the "latest and greatest". LINUX_VERSION_CODE is +#define'd in . + +Note that you should not make patches specific to *compiling* on a +particular version of Linux unless there is nothing else you can do. + +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 -u oldfile newfile +or + diff -Naur 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... + + +Code Structure +============== +My ultimate goal for this package is to be compilable with any kernel +headers and to be able to run under any kernel's /proc. (Don't bother +telling me that I'm not especially close to my ultimate goal... who +is? :-) + +Anyhow, another 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 00000000..a43ea212 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 + + Appendix: 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., 675 Mass Ave, Cambridge, MA 02139, 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 00000000..92b8903f --- /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/INSTALL b/INSTALL new file mode 100644 index 00000000..4be90ecc --- /dev/null +++ b/INSTALL @@ -0,0 +1,73 @@ +INSTALL for procps version 2.0.1 +================================ +Please read the NEWS and BUGS files, also. +========================================== + + +Re-compiling the package +======================== + +You want to examine the first 30 or so lines of the Makefile (up to the +configurability note). The destinations of various things attempt FSSTND +compliance, and are pretty standard. The CC/LD flags and which libraries you +use for curses/termcap are here also. It is all annotated there and in a +readily understood format: directories, program to installdir mapping, +sub-packages to build, and compilation options. A few extra points worth +mentioning are: + + o The SUBDIRS variable is essentially just a list of subdirectories to + perform a recursive make or make install in. Right now that's just + ps; the old xproc has been removed because it was entirely redundant. + o There is also an option to build and link against a libproc.so which + reduces 'ps' and 'top' sizes by a large fraction. It is on by default, + so change the value of SHARED if you want. + o 'make libinstall' will install the library and header files into standard + system directories for developers of /proc utilities. There are no + library man pages yet, but the headers are fairly well documented. + o You may need to change the INCDIRS definition if your system is + not very standard. The current definition has been tested to work + on several different systems, though. + +Once you are satisfied with the top-level Makefile options (and possibly those +in subdirectories) you compile and install the package like so: +(ignore innocuous `rcsid' defined-but-not-used warnings.) + + make distclean # clean-out everything to re-make from scratch + make # takes about 0.75 minutes on a PPro 200 with SCSI + su # for write/chown-perms on sys dirs + make install + ldconfig -v # update ld.so to use new libproc if SHARED=1 + exit + make distclean # remove anything that can be rebuilt + + +Miscellaneous Notes +=================== + +COMPATIBILITY + + This code is intended for use with Linux 2.0.xx and 2.2.xx. + Both libc 5 and libc 6 should work. + +PS/TOP WCHAN FIELD + + In past releases, in order to get WCHAN output for ps, you had to + have a psdatabase for each zImage kernel that you generated from + the vmlinux unstripped binary with /sbin/psupdate. With this release, + you should use the System.map automatically generated by Linus' makefile. + 'ps' and 'top' will first use the contents of an environment variable + "PS_SYSTEM_MAP" and then look for System.map files along + the followin search path: + /boot/System.map-V, /boot/System.map, /lib/modules/V/System.map, + Here 'V' is the output of "uname -r" and looks like "2.2.0". + Don't forget the dash! In those same locations, remove psdatabase* files. + + +PARTIAL INSTALLATION + + If you just want to install one program use the targets + install_progname and install_progname.X (where X is the man section) + instead. E.g. + make install_ps install_ps.1 + Likewise for component packages, e.g. + make install_psmisc diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..3cba70f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,220 @@ +# Makefile for procps. Chuck Blake. +# Portions of this are highly dependent upon features specific to GNU make + +export PREFIX = #proc# prefix for program names + +export DESTDIR = / +export MANDIR = /usr/man +export MAN1DIR = $(DESTDIR)$(MANDIR)/man1 +export MAN5DIR = $(DESTDIR)$(MANDIR)/man5 +export MAN8DIR = $(DESTDIR)$(MANDIR)/man8 +export BINDIR = $(DESTDIR)/bin +export SBINDIR = $(DESTDIR)/sbin +export XBINDIR = $(DESTDIR)/usr/X11R6/bin +export USRBINDIR = $(DESTDIR)/usr/bin +export PROCDIR = $(DESTDIR)/usr/bin# /usr/proc/bin for Solaris devotees +export APPLNK = $(DESTDIR)/etc/X11/applnk/Utilities +export OWNERGROUP = --owner 0 --group 0 +export INSTALLBIN = install --mode a=rx --strip +export INSTALLSCT = install --mode a=rx +export INSTALLMAN = install --mode a=r + +BPROG = kill # -> BINDIR +UPROG = oldps uptime tload free w top vmstat watch skill snice # -> USRBINDIR +PPROG = pgrep pkill# -> PROCDIR +SPROG = sysctl +MAN1 = oldps.1 uptime.1 tload.1 free.1 w.1 top.1 watch.1 skill.1 kill.1 snice.1 pgrep.1 pkill.1 +MAN5 = sysctl.conf.5 +MAN8 = vmstat.8 sysctl.8 +DESKTOP = top.desktop +XSCPT = XConsole # -> XBINDIR + +SUBDIRS = ps # sub-packages to build/install + +# easy to command-line override +export INCDIRS = -I/usr/include/ncurses -I/usr/X11R6/include + +export CC = gcc #-ggdb # this gets compiling and linking :-) +export OPT = -O2 +export CFLAGS = -D_GNU_SOURCE $(OPT) -I$(shell pwd) $(INCDIRS) -W -Wall -Wstrict-prototypes -Wshadow -Wcast-align -Wmissing-prototypes + +export SHARED = 1# build/install both a static and ELF shared library +export SHLIBDIR = $(DESTDIR)/lib# where to install the shared library + +export LDFLAGS = -Wl,-warn-common #-s recommended for ELF systems +#LDFLAGS = -qmagic -s# recommended for a.out systems +#LDFLAGS = -Xlinker -qmagic -s# older a.out systems may need this +#LDFLAGS = -N -s# still older a.out systems use this + +#BFD_CAPABLE = -DBFD_CAPABLE +#AOUT_CAPABLE = #-DAOUT_CAPABLE +#ELF_CAPABLE = #-DELF_CAPABLE + +#LIBBFD = -lbfd -liberty +LIBCURSES = -lncurses# watch is the only thing that needs this +#LIBCURSES = -lcurses -ltermcap# BSD Curses requires termcap +LIBTERMCAP = -lncurses# provides perfectly good termcap support +#LIBTERMCAP = -ltermcap +EXTRALIBS = # -lshadow + +W_SHOWFROM = -DW_SHOWFROM# show remote host users are logged in from. + +#----------------------------------------------------# +# End of user-configurable portion of the Makefile. # +# You should not need to modify anything below this. # +#----------------------------------------------------# +BUILD = $(BPROG) $(UPROG) $(PPROG) $(SPROG) $(SUBDIRS) $(DESKTOP) + +# BUILD LIBRARIES + PROGRAMS +all: $(BUILD) + +# INSTALL PROGRAMS + DOCS +install: $(patsubst %,install_%,$(BUILD) $(XSCPT) $(MAN1) $(MAN5) $(MAN8)) +ifeq ($(SHARED),1) + install $(OWNERGROUP) --mode a=rx $(LIB_TGT) $(SHLIBDIR) +endif + +# INSTALL LIBRARIES + HEADERS (OPTIONAL) +libinstall: + $(MAKE) -C proc install $(LIBPROCPASS) + +clean: + $(RM) -f $(OBJ) $(BPROG) $(UPROG) $(PPROG) $(SPROG) + for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done + +distclean: clean + for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done + $(RM) -f $(OBJ) $(BPROG) $(UPROG) $(SPROG) \ + proc/.depend + + +#-----------------------------------------------------# +# End of user-callable make targets. # +# You should not need to read anything below this. # +#-----------------------------------------------------# + +.PHONY: all install libinstall clean distclean +.PHONY: $(patsubst %,install_%, $(BPROG) $(UPROG) $(SPROG)) +.PHONY: proc ps +.PHONY: $(patsubst %,build_%, proc ps) +.PHONY: $(patsubst %,install_%, proc ps) + +VERSION = $(shell awk '/^%define major_version/ { print $$3 }' < procps.spec) +SUBVERSION = $(shell awk '/^%define minor_version/ { print $$3 }' < procps.spec) +MINORVERSION = $(shell awk '/^%define revision/ { print $$3 }' < procps.spec) + +# Note: LIBVERSION may be less than $(VERSION).$(SUBVERSION).$(MINORVERSION) +# LIBVERSION is only set to current $(VERSION).$(SUBVERSION).$(MINORVERSION) +# when an incompatible change is made in libproc. +LIBVERSION = 2.0.7 +ifdef MINORVERSION +LIBPROCPASS = SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) MINORVERSION=$(MINORVERSION) LIBVERSION=$(LIBVERSION) +else +LIBPROCPASS = SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) LIBVERSION=$(LIBVERSION) +endif + +# libproc setup + +ifeq ($(SHARED),1) + LIB_TGT = proc/libproc.so.$(LIBVERSION) +else + LIB_TGT = proc/libproc.a +endif + +$(LIB_TGT): $(wildcard proc/*.[ch]) + $(MAKE) -C proc `basename $(LIB_TGT)` $(LIBPROCPASS) + +# component package setup -- the pattern should be obvious: A build rule and +# unified executable+documentation install rule. (An extra makefile rule is +# needed for those packages which use Imake.) + +ps: build_ps +build_ps: ; $(MAKE) -C ps +install_ps: ps ; $(MAKE) -C ps install + +# executable dependencies +oldps kill skill snice top w uptime tload free vmstat utmp : $(LIB_TGT) + +# static pattern build/link rules: + +%.o : %.c + $(strip $(CC) $(CFLAGS) -c $^) + +oldps w uptime tload free vmstat utmp pgrep: % : %.o + $(strip $(CC) $(LDFLAGS) -o $@ $< $(LIB_TGT) $(EXTRALIBS)) + + +# special instances of link rules (need extra libraries/objects) + +top: % : %.o + $(strip $(CC) $(LDFLAGS) -o $@ $^ $(LIB_TGT) $(LIBTERMCAP) $(EXTRALIBS)) + +watch: % : %.o + $(strip $(CC) $(SLDFLAGS) -o $@ $< $(LIBCURSES) $(EXTRALIBS)) + + +# special instances of compile rules (need extra defines) +w.o: w.c + $(strip $(CC) $(CFLAGS) $(W_SHOWFROM) -c $<) + +top.o: top.c + $(strip $(CC) $(CFLAGS) -fwritable-strings -c $<) + +skill.o: skill.c + $(strip $(CC) $(CFLAGS) -DSYSV -c $<) + +snice: skill + ln -f skill snice + +kill: skill + ln -f skill kill + +pkill: pgrep + ln -f pgrep pkill + +# static pattern installation rules + +$(patsubst %,install_%,$(BPROG)): install_%: % + $(INSTALLBIN) $< $(BINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(SPROG)): install_%: % + $(INSTALLBIN) $< $(SBINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(UPROG)): install_%: % + $(INSTALLBIN) $< $(USRBINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(PPROG)): install_%: % + $(INSTALLBIN) $< $(PROCDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(XSCPT)): install_%: % + $(INSTALLSCT) $< $(XBINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(MAN1)) : install_%: % + $(INSTALLMAN) $< $(MAN1DIR)/$(PREFIX)$< +$(patsubst %,install_%,$(MAN5)) : install_%: % + $(INSTALLMAN) $< $(MAN5DIR)/$(PREFIX)$< +$(patsubst %,install_%,$(MAN8)) : install_%: % + $(INSTALLMAN) $< $(MAN8DIR)/$(PREFIX)$< +$(patsubst %,install_%,$(DESKTOP)) : install_%: % + $(INSTALLSCT $< $(APPLNK)/$(PREFIX)$< + +# special case install rules +install_snice: snice install_skill + cd $(USRBINDIR) && ln -f skill snice +install_kill: snice install_skill + cd $(USRBINDIR) && ln -f skill kill +install_pkill: pgrep install_pgrep + cd $(USRBINDIR) && ln -f pgrep pkill + +# Find all the source and object files in this directory + +SRC = $(sort $(wildcard *.c)) +OBJ = $(SRC:.c=.o) + +CVSTAG = ps_$(VERSION)_$(SUBVERSION)_$(MINORVERSION) +FILEVERSION = $(VERSION).$(SUBVERSION).$(MINORVERSION) +dist: archive +archive: + @cvs -Q tag -F $(CVSTAG) + @rm -rf /tmp/procps + @cd /tmp; cvs -Q -d $(CVSROOT) export -r$(CVSTAG) procps || echo GRRRrrrrr -- ignore [export aborted] + @mv /tmp/procps /tmp/procps-$(FILEVERSION) + @cd /tmp; tar czSpf procps-$(FILEVERSION).tar.gz procps-$(FILEVERSION) + @cd /tmp; cp procps-$(FILEVERSION)/procps.lsm procps-$(FILEVERSION).lsm + @rm -rf /tmp/procps-$(FILEVERSION) + @echo "The final archive is /tmp/procps-$(FILEVERSION).tar.gz" diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..b1bcf5ff --- /dev/null +++ b/NEWS @@ -0,0 +1,645 @@ +NEWS: what has changed recently with procps, in reverse cronological order. +Please send bug reports to acahalan@cs.uml.edu + +*** THIS FILE DOES NOT INCLUDE RECENT CHANGES *** + + +NEWS for version 2.0.7 of procps + +SMP support has been added to top. This adds one line to the screen +for every processor. There is also an (off by default) field to show +the last processor that a process used that replaced the long-dead +LIB field. Please send feedback, positive and negative, on these +changes to procps-bugs@redhat.com + +The libproc soname has changed to 2.0.7 because of a structure +change required to add SMP support. + +Two programs inspired by Solaris's /usr/proc/bin collection have +been added: pgrep and pkill. By default, for FSSTND/FHS/LSB compliance, +these are installed in /usr/bin; if you want, you can change PROCDIR +in Makefile to move them. You can also make a symlink from +/usr/proc/bin to /usr/bin if you like. + +The Makefiles have been sanitized a bit more; they are now less messy +than they were. There is less duplication between the Makefiles now. + +The man pages that use tables have been fixed to work work broken +versions of man. + +The old wmconfig file (specific to Red Hat) has been replaced with a +desktop file (common across GNOME and KDE). + +sysctl returns an error code in a condition in which it didn't before, +and handles EOF correctly. + +top now only loads System.map when it is actually going to make use +of it. + +vmstat has its buffer size increased and handles page size dynamically. + +w has its year display fixed to show the year 2000 as 00 instead of 0, +and to try harder to find a process to display. + +watch has a fix for ncurses in recent versions of glibc, and expands +tabs so that they display correctly. + +libproc tries to get the default page size from the system header +files, but still has a fallback. It also has been extended to allow +applications to handle their own error reporting in some important +cases. The pwcache has been expanded in size to correctly handle +user names longer than 8 characters. It has been expanded to +provide the lproc field that shows up as top's LC field. A segfault +when /proc is not mounted was fixed. Missing files will cause +applications to exit with an error code instead of good exit code. +A warning when libproc cannot calculate the HZ value (probably due +to a kernel bug) has been supressed by default because it broke +people's scripts unnecessarily. A 64-bit memory size reading +bug was fixed (/proc/meminfo was read incorrectly). + +A couple of error messages in ps had newlines added to them. ps +only opens System.map when it is going to make use of it. The +full-page error message has been replaced with a shorter usage +message; the full-page command summary is available with --help, +and the usage message tells about --help; the full-page summary +is now no longer an error message and so it is sent to stdout +insteada of stderr. Processes with run times longer than a day +now have their runtime displayed correctly. ruser output was fixed. +An attempt was made to support one more piece of BSD syntax in the +command line arguments, where pids can follow options with no +intervening space. The ps man page was made a bit more +internally consistent and typos fixed. Fixed a segfault when the +PS_FORMAT environment variable was set wrong. + + +NEWS for version 2.0.6 of procps + +Support for large-memory systems has been added. + +LIBVERSION has been incremented because of an incompatible change +in the libproc interface, necessitated by the large-memory support. + +A little more error checking in device idle time calculations. + +Fixed an almost-impossible file descriptor leak in libproc that +occasionally showed up in long-running top sessions. + +The fix for top no longer depending on NR_TASKS in 2.0.5 was +broken; top would die with more than 204 tasks. This has been +fixed. + + +NEWS for version 2.0.5 of procps + +procps can build against the 2.3.18 kernel source; top no longer +depends on NR_TASKS. + +sysctl no longer segfaults with -A; has a few parsing fixes + + +NEWS for version 2.0.4 of procps + +sysctl can save/restore settings using /etc/sysctl.conf file + +top has -p option and N and A commands. + +vmstat doesn't mind interrupt counter overflow on long-running machines + +ps can now sort by PCPU. + + +NEWS for version 2.0.3 of procps + +Time calculations fixed (or at least improved...) for SMP machines. +In particular, hertz is calculated correctly. + +ps doesn't mind terminal resizing happening while it runs. + +sysctl is a nifty new program! Try it! Carefully! :-) + +w PCPU and WHAT output fixed. + +top batch mode now works without a tty (for instance, via rsh). + +new version of watch has a few new features like --differences. + +sessreg removed; it belongs where it has been for a long time, in X. + + +NEWS for version 2.0.2 of procps + +Removed xproc entirely; the only thing left there was XConsole, which +was equivalent to xconsole -file /proc/kmsg. Added an XConsole shell +script which does +exec xconsole -file /proc/kmsg "$@" +so that folks who depend on XConsole won't be left behind. This also +fixes the bug that XConsole effectively removed limitations on which +users were allowed to read /proc/kmsg without root having much choice +in the matter other than to remove XConsole... + +Removed unused psupdate code (still available as part of procps-1.2.x +for anyone who wants to play with it). + +Removed sessreg, as it is included in XFree86 and there is no reason +for the duplication. + +Fixed version number generation so that it happens in one place. I'm +tired of releasing versions that misreport their version number... + + +NEWS for version 2.0.1 of procps + +Reverted my changes that had broken Albert's Unix98 compliance. + +Major bugs fixed: + o ps now returns failure for "ps " + o ps h has reverted to old linux behaviour except in BSD personality; + --headers and --no-headers long options have been added + o watch buffer overrun fixed (no, not a security hole). + +top has -b and -n options added. + + +NEWS for version 2.0 of procps + +Thanks to Albert Cahalan for his rewrite of ps, and for making +the time for new development of ps that I just didn't have. His +research into how ps worked on lots of different UNIX systems +really made this version much more usable for a lot of people. + +Read ALL of Albert's comments regarding his 1.9.0 release. If +you do not, you may be surprised when scripts fail. I've tried +to prepare people for the worst of this by giving out the much-detested + warning: `-' deprecated; use `ps aux', not `ps -aux' +message for roughly a year and a half. + + +NEWS for version 1.9.0 of procps + +The ps command now supports simultaneous BSD and Unix98 syntax. +There have been many other changes in ps. (complete rewrite) + +Red Hat users should check /etc/sysconfig/network-scripts/ifdown-ppp +for the bogus command "ps aul" and change it to "ps axl". + +Scripts that make assumptions about character position are and +were broken because fields may overflow. Scripts should parse by +whitespace and use -o to get the best results. Command names for +swapped out processes are now shown in square brackets instead of +parentheses, as required by the Unix98 standard. This problem can +be avoided entirely by using a SysV format without -f, using the +BSD "c" option, or using the format specifier "comm" with -o. + +Some uses of ps in /etc/rc.d/init.d/functions should probably +eventually be changed to stuff somewhat like the following, which +would eliminate the need for awk: + +PS_PERSONALITY=linux dead=`ps -p $pid -o pid=` +PS_PERSONALITY=linux echo -n `ps -C $1 -o pid=` +PS_PERSONALITY=linux pid=`ps -C $1 -o pid=` + +(or use the 'start-stop-daemon' program that Debian has) + +Obsolete files you may have: /etc/psdatabase, /etc/psdevtab, +~/.psdevtab, /usr/man/man8/psupdate.8 and /usr/sbin/psupdate. + + +NEWS for version 1.2.9 of procps + +psupdate has been REMOVED from the default install. You can put it back +if you want to by removing two comment characters in the Makefile, but +I'm not going to tell you which ones. If you can't figure it out, +there's a good chance that it won't build on your machine and I don't +want the bug reports. One of the main reasons I have removed it is that +it is not necessary on a properly-maintained system (that is, System.map +exists and is correct) and 4/5ths of the bug reports I have fielded in +the past are from users who are not able to maintain a working system +complaining that it doesn't work, despite the fact that the INSTALL file +explains how to build it in different ways and how to disable it. +I will delete without response all email asking me how to build psupdate. + +ps now accepts and ignores the g option for those with fingers tied to +the g key from too much exposure to BSD. :-) + +Should build properly on ultrasparc -- two people sent the patch and +the second time I didn't notice that the patch went in reverted. So +I have now reverted the reverted patch to get the right behavior. + +More reliable command-line parsing in vmstat has been added. If vmstat +didn't die for you before, you won't notice the difference... + +Fixed a bug where arbitrary users might have been able to corrupt the +screen of arbitrary other users running top by creating processes with +weird arguments. + +"ps ce" was missing a space character; fixed. + + +NEWS for version 1.2.8 of procps + +procps.spec now uses buildroot, and can be built by non-root users. +This means that the default install targets had to change in the +makefiles so that the default install does not require root privs +otherwise. Also changed all "bin" ownership to "root" ownership, +as kmem-ps has died out and folks won't be automatically doing +"chmod u+s /bin/ps" and making their systems insecure thereby. + +XConsole no longer installed setuid root by default. This is safer, +if less convenient. Change it if you want to use it as non-root. + +top had disgustingly buggy ^Z handling: you could only suspend once, +and if you had suspended, you could no longer read WCHAN information. +Both bugs are now fixed. + +xcpustate removed -- it was a very old, obsolete version. +Get a new version from ftp://ftp.cs.toronto.edu/pub/jdd/xcpustate + +Now looks for kernel symbols in loadable modules. + +psupdate no longer included in RPM package; the theory is that +systems maintained with RPM probably are reasonably recent and +up-to-date and so psupdate is extraneous on those systems, only +adding an unhelpful dependency on a particular version of +binutils. + +top printed two spaces instead of just one between command-line arguments. + + +NEWS for version 1.2.7 of procps + +Security hole in XConsole fixed. + +Works better with very long uptimes. + +Fixed RPM spec file to have correct libproc soname. + +skill -i works. + +Knows about more major numbers for smart serial boards. + +Fixed some small problems in top. + + +NEWS for version 1.2.6 of procps + +Signal handling in top fixed in several ways. Main effect: suspending +works correctly with glibc now. + +Patch from Linus for change in some kernel structures to 64-bit. +This means that libproc has changed soname, as the proc_t data +structure has changed incompatibly. + +File descriptor leaks in libproc were fixed. + + +NEWS for version 1.2.5 of procps + +Potential security hole fixed: if there was no /etc/psdevtab on +a system, a user could put a link from /tmp/psdevtab to a file +owned by another user, and when that other user ran ps, the +file it pointed to would be killed. This wouldn't work for +root because root would be able to create /etc/psdevtab first, +so arbitrary system files would not be killable. This was fixed +by avoiding psdevtab files that are symlinks or have link counts +higher than 1, and by not looking for /tmp/psdevtab anymore, as +the reason for it to exist is really obsolete. + +w -s output has been fixed. Scripts that depend on w -s output +should be examined. The output was broken enough to warrant this +change; it was extremely buggy. + + +NEWS for version 1.2.4 of procps + +I_WANT_A_BROKEN_PS environment variable turns off usage warning. + +"w " sense corrected. + +argument order problem fixed. + +XConsole doesn't dump core on exit any more (only fclose if the +file is open...) + +NEWS for version 1.2.3 of procps + +psupdate moved to /usr/sbin, since it is no longer necessary. + +Added wmconfig for Red Hat systems (others can use it if they +want; if/when wmconfig becomes standard, I'll move it into the +install target of the makefile). + +Ugly hack to get around a problem some people were getting with +a too-small variable. + +The usage message no longer recommends a deprecated syntax. :-) + +NEWS for version 1.2.2 of procps + +Made procps report its version number correctly. +Fixed typo in w.1 man page. + +NEWS for version 1.2.1 of procps + +New address for bug reports against procps: procps-bugs@redhat.com +(That's not an official service of Red Hat Software, just an alias +for me so that bugs get filed properly where I don't lose them...) + +Better memory checks. +Fixed a file descriptor leak. +In top, the space key updates immediately. +Fixed broken signal code in top. +Fixed broken screen size calculations in top. +Fixed broken user count calculations in top. +Fixed broken time difference calculations in w. +Link libproc against libc explicitly for better co-existance with libc. + +NEWS for version 1.2 of procps + +Original author/maintainer, Michael K. Johnson , +has taken over maintenance again. + +psmisc removed, as it is better maintained separately. xload removed, +as it is better maintained as part of XFree86. mknewpty removed, as +it duplicates /dev/MAKEDEV and MAKEDEV is maintained. + +Support for 2.1.x kernels and for glibc 2.0.5. + +Lots of documentation updates. + +BFD support and shared library enabled by default. + +Binaries are no longer included in the archive -- get them from your +favorite Linux distributor. + +free's display fixed in several ways: no such thing as cached swap, +buffer+ display was incorrectly calculated, and buffer calculations +didn't include cached memory. + +Removed mknewpty, because it wasn't perfect and collides with MAKEDEV. + +NEWS for version 1.01 of procps + +top bugfix release. Fixes memory leak, extra line after loadavg and broken +no-idle mode. Also be a little more aggressive utmp 'from host' filtering in w. + +NEWS for version 1.00 of procps + +Fixed the ps -t without a -a segfault bug. Royal screwup on my part. +Updated libproc parsing routine for the new vsize output as of 1.3.91-ish. +Expanded the room for the FLAGS in ps -l due to high order bits now being set. + +Added a brand new top to the distribution. Highly run-time configurable. The +old top is available for at least a release or so as "top.old". See the new man +page for details, or just run it and type 'h'. This is courtesy of Helmut +Geyer. Thanks Helmut! + +egrep -n '\<(tgetent|cm|top_clrtoeol|top_clrtobot|cl|ho|me|md|mr|tgoto)\>' on +top.c and cleaned up all terminal strings being just dumped to stdout. All such +strings are now tputs'd out with putchar. This should fix a lot of problems +people have been having. + +Also cleaned up non-HZ based references to jiffies -> real time conversion. I +may have missed one or two, though... + +Cleaned up some Makefile things. make distclean; make should really work now. +removed function pointer warnings in xcpustate.c. .depend is properly removed +as are the imake generated makefiles. Shared lib generation bug for libproc +fixed. In general the build should be a lot cleaner, but may still have a glitch +or two. + +Fixed a few (but probably not all) Alpha compatibility bugs dealing with memory +alignment. Please let me know if I missed any, or if you like give me a test +account on some Alpha. I don't have access to any Linux other than x86 which is +notoriously forgiving about unaligned memory accesses. Thanks to Alfred Arnold +(a.arnold@kfa-juelich.de) and Harald Koenig for their help so far with this. + +Completely (well almost) re-wrote 'w'. From on by default, J/PCPU display is +accurate to 0.01s. w and top use readproc, so snap.c has gone away completely. + +'skill' should actually work now, but you may have to use '-c', '-u', etc. to +actually get it to parse the command line correctly. + +*** Significant changes in psdevtab inverse device name resolution +------------------------------------------------------------------ +Extended semantics of the -n (numeric) option to output the full device number +in hexadecimal for the tty field. That way if /etc/psdevtab cannot be created +rescanning the /dev directory can be short-circuited. Fixed incorrect file +creation mode for /etc/psdevtab. I just forgot to alter my original 0 mode to +something reasonable like 0664. Added fallback locations for psdevtab. First +it tries /etc/psdevtab, then /tmp/psdevtab and then $HOME/.psdevtab. Also, +decided to go ahead and create the devtab file with regular old write instead of +the rw mmap. I still read it with mmap, but there should be no trouble with +that even in ancient kernels. Also changed the semantics such that if any +devtab file is found, it is assumed to be correct regardless of the relative +timestamps of /dev and the file. Hopefully all this will avoid any unnecessary +slowness. I'm still willing to reactivate the older somewhat broken code to +do the mapping without any file as a fallback if the file doesn't exist. + +*** Significant changes in psdatabase/WCHAN inverse name resolution +------------------------------------------------------------------- +libproc reads directly from System.map, so psupdate and /etc/psdatabase are no +longer necessary. Hopefully this will make kernel configuration management +simpler requiring only the zImage and System.map, which being the stripped +(compressed) kernel and the symbol table before stripping are natural +complements. This is probably how things should have been all along even going +back to kmem ps days four years ago. Oh well. This change should make dealing +with x86, AXP, Sparc, etc binary formats for kernels a lot easier. All that is +required of System.map is that there be exactly 3 space delimited columns: + "address[single space]anything_with_no_spaces[single space]symbol[newline]" +[ Well, ok, the addresses have to be zero padded so that lexicographic order is +the same as numerical order and the addresses all have to be the same ascii +length and I haven't tested to see how resilient it is to bogus internal records +like multiple newlines in a row. Hopefully aren't editting and commenting their +System.map files. ;-) This could actually work on "sort < /proc/ksyms" also if +we generalize the behavior to work with either two or three columns. ] + +We take advantage of the following files in this order: + PS_SYSTEM_MAP # may only point to System.map, not psdatabase + /boot/System.map-`uname -r`, # Note: this is the preferred path + /boot/System.map + /lib/modules/`uname -r`/System.map + /etc/psdatabase + /boot/psdatabase-`uname -r`, + /boot/psdatabase, + /lib/modules/`uname -r`/psdatabase + +The reason for the /lib/modules/*/file location is that I imagine many people +have enough trouble keeping track of kernel version-dependent files in the +filesystem as it is, so I didn't want to invent a new place. I keep things in +/boot myself and don't usually have more than 8 or so kernel versions, so this +works for me. I know not everyone uses modules, but it's just a search path +folks... In case it isn't obvious from all of the above, this means that +psupdate is no longer necessary. I include it in this release and include +recognition of it because I realize that some distributions may have scripts +which depend on the old semantics. I doubt there will ever be a compelling +reason to not include support for generating or reading the old psdatabase +files, but they aren't necessary anymore if you have the System.map files from +the kernel builds. + +I'm not 100% sold on the ordering of the search path, but I think it makes +sense. If you have reasons why it should be different I may be convincable. +Also I'm interested in exactly what the output of 'nm' looks like on Alpha, +Sparc, etc., architectures. It should be easy enough to adapt the code if it is +basically the same format of <0 padded hex> ... \n. + +Thanks to Helmut Geyer for the idea of living off the System.map file natively +and Mike Dean for the idea of using an approximate binary search instead of +padding out the lines of System.map with tons of spaces to get equal record +lengths. The cost of doing it approximately is mild, mostly consisting of lots +of scans to get to the next or last newline and a logarithmically few extra +steps to get to the desired record. Only an order of magnitude guess for the +record length is necessary. The code is all mine, though, so all bugs are due +to me alone. + +NEWS for version 0.99a of procps +================================ +This is a quick bug-fix release to solve a few thorny problems with my probably +overzealous attempt to use the inline-assembly string.h and a Makefile bug or +two. It also fixes free to +/- the cached column too and makes it ignore the +new extra-pretty non-numeric lines that /proc/meminfo is spitting out. It also +fixes the lack of set_linux_verion() in 'w' that caused no command line to be +displayed. I am still working on a much condensed 'w' that should be a lot +easier to maintain and a readproctree that should be usable by both 'ps', +'pstree', and 'w'. + +Apparently memory-mapped files are pretty broken before the early 1.3.X kernels +so I may have to rewrite devname.c to not use MAP_WRITE to create psdevtab. Some +people have been claiming that /dev changes at boot-up in some rc scripts. I'm +not really sure why they would want to do that. Seems kind of paranoid to +continually re-make /dev/log. Anyhow, I'm open to suggestions for psdevtab +behaviors. I've been thinking a /tmp/psdevtab fallback (with a careful world +readable umask to avoid repetition) or maybe a $HOME/.psdevtab fallback too. + +NEWS for version 0.99 of procps +=============================== +This file is a brief catalog of new features or developments in the package. +For general information about using the programs see their man pages. + +NEW PACKAGES OR PROGRAMS +======================== + +LIBPROC + I've modularized some routines and fixed some long standing bugs. Replaced + the regex() recognition of /proc/PID with a simple check of the first + character of the filename being a digit which should be just as safe. + + Added an opendir/readdir/closedir style interface to the process table. The + new interface seems cleaner, more intuitive and generally more applicable + (to me anyway). The only program which uses the new interface is ps. 'w' + will follow soon. 'top' may take a while longer... openproc,readproc, and + closeproc are implemented. I still want to do readproctab and rewindproc, + too though. + + Added some kernel and package versioning things to make it easier to be run + time adaptable. Also updated sysinfo to understand any /proc/meminfo. A + /proc/stat parser should probably be in there as well with the appropriate + updates to vmstat and xcpustate. + + The general direction procps should move in is lightweight command-line or + X11/Motif display/format programs and compartmentalized libproc routines to + parse all of the /proc files. This isolates the utilities from kernel + versioning. + +TTY DEVICE NUMBER TO NAME RESOLUTION + Tty device name <-> number mapping has been completely generalized. It now + stat's every character special file in /dev and builds a memory mapped table + of device names indexed in a way that makes lookup of name from number a + fast, constant time process. The extra overhead incurred by building + /etc/psdevtab is non-negligible if you have a large /dev and permissions to + write the file (or its directory) are required to update the file (which is + done if it does not exist or if /dev is newer than /etc/psdevtab). + + Hence `root' should `ps' shortly after any modification to /dev (or chmod + 666 /etc/psdevtab :-) to avoid ordinary users rebuilding it over and over. + Since such modifications are rare, hopefully having a fallback $HOME + location will not be necessary. If the file is up to date, the overhead + incurred is very small. The generality bought is essentially optimal since + `ps' tailors its notion of name<->devno mapping according to the /dev of the + local system which is the canonical repository of this information. + + In principle the name database could encompass all device majors. The file + would be large, but since I use mmap to access it, only the pages with the + major of interest are ever actually read off the disk. Right now I just use + the majors 2,3,4,5,19,20 which should cover both old and new systems with + both master and slave devices (I know... no reason for the masters... :-) + and the multiport serial devices. Also the 'mknewpty' script is provided + to update your /dev directory to the new pty master/slave devices. + + The tty abbreviation scheme has been rationalized to match device special + files. The leading "tty" or "cu" is stripped, so cua3 -> a3, tty1 -> 1 and + ttyp9 -> p9. The t flag in ps now works with a full device names and to + pick up processes even if they aren't owned by the owner of ps, e.g. + "ps tcua0" picks up gpm for everyone. This seems desirable. + +WATCH + A little program similar to another called 'vis' which simply re-displays + in a polling fashion the output of other programs. "watch ps --sort:utime" + might be dubbed a poor man's 'top'. Though this has been included in procps + for some time it hasn't been built or installed by default. It is now. + +SKILL/SNICE + I have written the necessary machine-dependant file for 'skill' and tested + it somewhat under Linux. It seems to mostly work, but there are probably a + few glitches. This is a generalization of the 'killall' concept. You can + send signals or change priority based upon user, command basename (the same + that 'ps c' gives), terminal, etc. If you have a user named 'satan' "skill + -u satan" will kill all their processes. :-) See the man page for more + details. + + An annoyance of the current implementation is that although permission to + send signals is based upon the real user id, /proc only gives the effective + uids of processes. Hence processes which you *could* kill because they're + suid-root (X say) won't be detected as kill-able. Either /proc + readproc + need to be updated to report the *real* uid to skil or skill needs to try to + send the signal even if the uid doesn't match. + + +CHANGES TO OLD PROGRAMS +======================= + +MAKEFILE + The directory hierarchy has been restructured. It is now easier to have the + multiple components to the suite under nearly autonomous administration. + The library code has also been moved to a subdirectory. The best thing + about the new setup is that things like Imake generated Makefiles with + preconceived notions of 'make install' can be used without getting into the + business of re-writing component package makefiles. + + There is now an option to build a shared libproc.a which reduces 'ps' and + 'top' sizes by about 10K apiece. Simply change the value of SHARED. Also, + one may optionally install the library header files and archive/shared + object files into standard system directories. There are no library man + pages yet, but the headers are fairly descriptive. + +PS + Several long standing bugs have been fixed and much of the internal code was + re-written to use my new directory-style interface to the process table. In + particular if sorting is disabled (with '-o') the process entries are output + to the terminal as soon as possible (making it more helpful under heavy + system load). + + I am considering several new additional features to `ps' including + regex filtering of which processes to list, + "grep -s" silent testing for existing of processes matching criteria, + run-time/user-defined output formats. + I would also like to completely phase the w,top code which uses the snapshot + interface instead of the 'readproc' interface. And of course adding long + options for the rest of the options would be nice too (I may not get around + to doing this anytime really soon so patches which implement any of these + things would most likely be gleefully accepted). + +PSUPDATE + psupdate has been updated to work with ELF kernels. If you compile it as + an ELF binary it will handle both a.out and ELF kernels. If you compile it + as an a.out binary it will only handle a.out kernels. Many thanks to Jeff + Uphoff. + + psupdate has also been updated to work with any binary type supported + by libbfd. This is the default configuration. + Many thanks to David Mossberger-Tang. + +TOP + A user-defined format would be nice here too. Alternate sorting criteria + (top memory users instead of top CPU users, etc.) may be another interesting + alternative. Of course the sorting in ps can do all of that, but it doesn't + have any optimal screen update action going down... :-) diff --git a/TODO b/TODO new file mode 100644 index 00000000..a66d295b --- /dev/null +++ b/TODO @@ -0,0 +1,176 @@ +-------------------------- general ------------------------ + +Implement /usr/proc/bin tools like Solaris has. + +The main Makefile is broken. Try 'touch top.h' to see. +The Makefile is too clever, so it's still not fixed. + +Don't these really belong in the procps package? + killall pstree fuser lsof who +(they are maintained elsewhere, which causes version problems) + +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. + +Add a thread group ID, to be shared by all tasks that are related by +the clone() system call. This ID might be made unique from boot to +shutdown, perhaps being a 16-bit CPU number and 48-bit per-CPU +serial number. + +Make the kernel group /proc listing output by thread group. +Without this, a thread-aware ps must always sort processes. + +Supply the task ID (the "PID"/"TID") of the thread group leader. +I define "leader" as the first process of a thread group. + +Don't reuse the task ID of a thread group leader until all threads +are dead. Better yet, don't let the leader exit. + +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. + +Mark threads, and supply a list of other threads. + +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. + +Put unique ID at the top of System.map and in /proc, to make sure +there is never a mismatch. + +Maybe just put the symbols in the kernel like some other systems do. +Raul Miller writes: You might want to implement this as user data which just +happens to come from a wierd source -- instead of just freeing the memory +(like happens with init code), you want it to wind up in the buffer cache. + +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. + +The kernel should report these task_struct members: + has_cpu + processor + +---------------------- w -------------------------- + +The LOGIN@ column sometimes has a space in it. This makes correct +scripting difficult. + +Verify that DNS control does not give a user the power to specify +arbitrary data for the FROM column. (could set root's VGA color map!) + +---------------------- vmstat -------------------------- + +Extract /proc/stat 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. + +---------------------- top ------------------------- + +Share more stuff with ps. + +---------------- 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_*_ HP_fl JFMT OL_m +(jobc,cpu,sl,re,cpu,prmgrp,cp,m_size,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. Actually, avoid +reading lots of stuff! Avoidance got disabled. (at least don't read the +environment!!!) + +Maybe ps should put a 'C' in front of fields when they are affected +by cumulative mode. Debian ps did that. (move flag to common.h...) + +Support printing the client hostname (the FROM that w(1) uses) in place +of a pty. Maybe do this when PS_PERSONALITY=linux. + +Disgusting color support, for completeness. (Debian ps had it) + +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 ----------------------- + +Solve the "ps reports wrong date" bug. (recent processes on an SMP system +are reported as being 5 days old, while boot processes look normal) +(reported for procps-2.0.2 in May 1999) + +Show real-time priorities. (type & number) + +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 isn't too bad + +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. + +Try to make -jl fit in 80 columns. Do we need more than 1000 pty devices, +9 flag bits, etc.? (hmmm, Linux supports 2048 pty devices now, and we +might also want to steal whitespace there when the time column overflows) + +When not in strict Unix98 mode, let foo=8 specify that foo is 8 +characters wide. Debian did that. Then foo=8=bar and foo=bar=8 +could change both header and width. + +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/XConsole b/XConsole new file mode 100755 index 00000000..b4ecf22a --- /dev/null +++ b/XConsole @@ -0,0 +1,5 @@ +#!/bin/sh +# xconsole is capable of doing everything XConsole did, and is maintained... +# it is not setuid root, so we just exit if called as non-root +[ $(id -u) = 0 ] || exit 0 +exec xconsole -file /proc/kmsg "$@" diff --git a/free.1 b/free.1 new file mode 100644 index 00000000..f33b8d10 --- /dev/null +++ b/free.1 @@ -0,0 +1,45 @@ +.\" -*-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. Unless +specified \fBfree\fP subtracts/adds buffer memory from/to the used/free memory +reports (respectively!). +.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 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 00000000..c788d79e --- /dev/null +++ b/free.c @@ -0,0 +1,80 @@ +/* free.c - a /proc implementation of free */ +/* Dec14/92 by Brian Edmonds */ +/* Thanks to Rafal Maszkowski for the Total line */ + +#include "proc/sysinfo.h" +#include "proc/version.h" +#include +#include +#include +#include +#include +#include + +#define S(X) ( ((unsigned long long)(X) << 10) >> byteshift) + +static int byteshift = 10; +static int total = 0; + +int main(int argc, char *argv[]){ + int i; + int old_fmt = 0; + int rtime = 0; + + /* check startup flags */ + while( (i = getopt(argc, argv, "bkmos:tV") ) != -1 ) + switch (i) { + case 'b': byteshift = 0; break; + case 'k': byteshift = 10; break; + case 'm': byteshift = 20; break; + case 'o': old_fmt = 1; break; + case 's': rtime = 1000000 * atof(optarg); break; + case 't': total = 1; break; + case 'V': display_version(); exit(0); + default: + fprintf(stderr, "usage: %s [-b|-k|-m] [-o] [-s delay] [-t] [-V]\n", argv[0]); + return 1; + } + + do { + meminfo(); + printf(" total used free shared buffers cached\n"); + printf( + "%-7s %10Ld %10Ld %10Ld %10Ld %10Ld %10Ld\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) + ); + if(!old_fmt){ + printf( + "-/+ buffers/cache: %10Ld %10Ld\n", + S(kb_main_used-kb_main_buffers-kb_main_cached), + S(kb_main_free+kb_main_buffers+kb_main_cached) + ); + } + printf( + "%-7s %10Ld %10Ld %10Ld\n", "Swap:", + S(kb_swap_total), + S(kb_swap_used), + S(kb_swap_free) + ); + if(total == 1){ + printf( + "%-7s %10Ld %10Ld %10Ld\n", "Total:", + S(kb_main_total + kb_swap_total), + S(kb_main_used + kb_swap_used), + S(kb_main_free + kb_swap_free) + ); + } + if(rtime){ + fputc('\n', stdout); + fflush(stdout); + usleep(rtime); + } + } while(rtime); + + return 0; +} diff --git a/gfinger.p b/gfinger.p new file mode 100644 index 00000000..9ff9b315 --- /dev/null +++ b/gfinger.p @@ -0,0 +1,216 @@ +Return-Path: +Date: Wed, 24 Feb 93 23:37:45 -0500 +From: qpliu@ulch.Princeton.EDU (Quowong P Liu) +To: johnsonm@stolaf.edu +Subject: Suggestions for procps +Reply-To: qpliu@princeton.edu + +[...] + +Since you are collecting programs that use the proc-fs, I have included +some patches to GNU finger that allow it to read the proc information +that it uses. + +[...] + +Here the diffs for GNU finger begin. +-------------BEGIN--------------- +diff -ru finger-1.37.orig/lib/os.c finger-1.37/lib/os.c +--- finger-1.37.orig/lib/os.c Thu Oct 22 17:01:10 1992 ++++ finger-1.37/lib/os.c Mon Feb 8 00:56:32 1993 +@@ -23,7 +23,9 @@ + #include + #include + #include ++#ifndef linux + #include ++#endif + #include + #include + +@@ -43,9 +45,11 @@ + + #ifdef HAVE_PROC_FS + #include ++#ifndef linux + #include + #include + #include ++#endif /*linux*/ + #endif + + #include +@@ -193,6 +197,7 @@ + long idle, current_time, get_last_access (); + char *hostname, *ttyloc, *tty_location (); + #ifdef HAVE_PROC_FS ++#ifndef linux + prpsinfo_t procinfo; + struct procent + { +@@ -202,6 +207,16 @@ + char fname[sizeof (procinfo.pr_fname)]; + } + *proctable, *tableptr; ++#else ++ struct procent ++ { ++ time_t start; ++ int uid; ++ dev_t tty; ++ char fname[15]; ++ } ++ *proctable, *tableptr; ++#endif /* linux */ + int nprocs = 0; + #endif /* HAVE_PROC_FS */ + +@@ -235,6 +250,7 @@ + searching through user list. */ + + #ifdef HAVE_PROC_FS ++#ifndef linux + { + extern char *savedir (); + char *dirstring = savedir ("/proc", 2*1024, &nprocs); +@@ -280,6 +296,64 @@ + free (dirstring); + } + } ++#else /* linux */ ++ { ++ ++ extern char *savedir (); ++ char *dirstring = savedir ("/proc", 2*1024, &nprocs); ++ char *proc; ++ int procfd; ++ char procfile[17], procstatbuffer[1024]; ++ struct stat proc_fs_stat; ++ char procstate; ++ int tmp_tty; /* dev_t is unsigned, -1 means no controlling tty */ ++ ++ /* Allocate process table */ ++ if (nprocs) ++ proctable = (struct procent *) xmalloc (sizeof *proctable * nprocs); ++ ++ /* Walk processes */ ++ if (dirstring && nprocs) ++ { ++ for (tableptr = proctable, proc = dirstring; ++ *proc; proc += strlen (proc) + 1) ++ { ++ sprintf (procfile, "/proc/%.5s/stat", proc); ++ ++ if ((procfd = open (procfile, O_RDONLY)) >= 0 ++ && read (procfd, procstatbuffer, 1024)) ++ { ++ /* see /usr/src/linux/fs/proc/array.c ++ * this is for 0.99.4 ++ */ ++ sscanf (procstatbuffer, "%*d (%14s %c %*d %*d %*d %d " ++ "%*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u " ++ "%*u %hd" /* " %*u %*u %*u %*u %*u %*u %*u %*u %*d %*d " ++ "%*d %*d %*u" */, ++ tableptr->fname, &procstate, &tmp_tty, ++ &tableptr->start); ++ tableptr->fname[strlen(tableptr->fname)-1] = 0; /*trailing )*/ ++ tableptr->tty = tmp_tty; ++ if (fstat (procfd, &proc_fs_stat) == 0) ++ tableptr->uid = proc_fs_stat.st_uid; ++ ++ /* Only collect non-zombies with controlling tty */ ++ if (tmp_tty > 0 && procstate != 'Z') ++ { ++ tableptr++; ++ } ++ else ++ nprocs--; ++ ++ close (procfd); ++ } ++ else ++ nprocs--; ++ } ++ free (dirstring); ++ } ++ } ++#endif /* linux */ + #endif /* HAVE_PROC_FS */ + + +@@ -332,10 +406,14 @@ + + if (stat (device_name, &finfo) != -1) + { ++#ifdef linux ++ dev_t tty = finfo.st_rdev&0xff; ++#else + #ifdef HAVE_ST_RDEV + dev_t tty = finfo.st_rdev; + #else + dev_t tty = finfo.st_dev; ++#endif + #endif + int file; + +diff -ru finger-1.37.orig/lib/packet.c finger-1.37/lib/packet.c +--- finger-1.37.orig/lib/packet.c Wed Oct 21 18:04:38 1992 ++++ finger-1.37/lib/packet.c Mon Feb 8 01:10:00 1993 +@@ -348,7 +348,9 @@ + perror (filename); + } + ++#ifndef linux /* doesn't have fsync (as of .99.4) */ + fsync (file); ++#endif + close (file); + } + +diff -ru finger-1.37.orig/lib/savedir.c finger-1.37/lib/savedir.c +--- finger-1.37.orig/lib/savedir.c Fri Oct 16 21:52:40 1992 ++++ finger-1.37/lib/savedir.c Mon Feb 8 01:06:47 1993 +@@ -132,7 +132,7 @@ + char * + stpcpy (dest, source) + char *dest; +- char *source; ++ const char *source; /* gcc insists on prototype consistency */ + { + while ((*dest++ = *source++) != '\0') + /* Do nothing. */ ; +diff -ru finger-1.37.orig/src/finger.c finger-1.37/src/finger.c +--- finger-1.37.orig/src/finger.c Wed Oct 21 20:41:13 1992 ++++ finger-1.37/src/finger.c Mon Feb 8 00:56:32 1993 +@@ -192,6 +192,9 @@ + long addr; + char *finger_server = NULL; + int suppress_hostname = 0; ++#if defined(linux) /* gethostbyaddr evidently returns pointer to static area */ ++ int do_hostfree = 0; ++#endif /* linux */ + + + username = savestring (arg); +@@ -237,6 +240,9 @@ + { + host = (struct hostent *) xmalloc (sizeof (struct hostent)); + host->h_name = hostname; ++#if defined (linux) ++ do_hostfree = 1; ++#endif /* linux */ + } + } + else +@@ -296,6 +302,11 @@ + if (finger_server) + free (finger_server); + ++#if !defined (linux) + if (host) + free (host); ++#else ++ if (host && do_hostfree) ++ free (host); ++#endif + } +-------------END------------- diff --git a/glibc.patch b/glibc.patch new file mode 100644 index 00000000..5ac7f146 --- /dev/null +++ b/glibc.patch @@ -0,0 +1,32 @@ +From: Nix +Date: Tue, 2 Mar 1999 05:25:08 +0000 (GMT) +Subject: Re: [PATCH] Re: that fputs() thing + + +1999-03-02 Nix + + * stdlib/fputs.c (fputs): Return `len' rather than 0 on success. + +*** 2.1/stdio/fputs.c.orig Tue Mar 2 05:17:12 1999 +--- 2.1/stdio/fputs.c Tue Mar 2 05:18:07 1999 +*************** +*** 27,35 **** + { + const size_t len = strlen (s); + if (len == 1) +! return putc (*s, stream) == EOF ? EOF : 0; + if (fwrite ((void *) s, 1, len, stream) != len) + return EOF; +! return 0; + } + weak_alias (fputs, fputs_unlocked) +--- 27,35 ---- + { + const size_t len = strlen (s); + if (len == 1) +! return putc (*s, stream) == EOF ? EOF : len; + if (fwrite ((void *) s, 1, len, stream) != len) + return EOF; +! return len; + } + weak_alias (fputs, fputs_unlocked) diff --git a/kill.1 b/kill.1 new file mode 100644 index 00000000..66197dfe --- /dev/null +++ b/kill.1 @@ -0,0 +1,96 @@ +,\" 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 \- report process status + +.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. +.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. + +.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 +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 +kill -9 -1 +.br +kill -l 11 +.br +kill -L +.br +kill 123 543 2341 3453 + +.SH "SEE ALSO" +top(1) skill(1) kill(2) renice(1) nice(1) + +.SH STANDARDS +This command meets appropriate standards. The -L flag is Linux-specific. + +.SH AUTHOR +Albert Cahalan wrote kill in 1999 to replace the +version that was not standards compliant. Michael K. Johnson + is the current maintainer of the procps collection. + +Please send bug reports to diff --git a/minimal.c b/minimal.c new file mode 100644 index 00000000..0582d348 --- /dev/null +++ b/minimal.c @@ -0,0 +1,486 @@ +/* + * Copyright 1998 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 + +#include /* HZ */ +#include /* PAGE_SIZE */ + +static int P_euid; +static int P_pid; +static char P_cmd[16]; +static char P_state; +static int P_ppid, P_pgrp, P_session, P_tty, 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_it_real_value; +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] */ + + +#ifndef PAGE_SIZE +#warning PAGE_SIZE not defined, assuming it is 4096 +#define PAGE_SIZE 4096 +#endif + +#ifndef HZ +#warning HZ not defined, assuming it is 100 +#define HZ 100 +#endif + + + +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>0x7fff) goto bad; /* Linux PID limit */ + 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 +/* 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; +} + +/* 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, &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_it_real_value, + &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); + if(num < 30) return 0; + if(P_pid != pid) return 0; + return 1; +} + +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){ + char tty[16]; + snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty>>8)&0xff, P_tty&0xff); + switch(ps_format){ + case 0: + printf("%5d %s %s", P_pid, tty, do_time(P_utime+P_stime)); + break; + case 'o': + printf("%d\n", P_pid); + return; /* don't want the command */ + case 'l': + printf( + "%03x %c %5d %5d %5d %s %3d %3d - " + "%5ld %06x %s %s", + (unsigned)P_flags&0x777, 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), tty, 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(), tty, do_time(P_utime+P_stime) + ); + break; + case 'j': + printf( + "%5d %5d %5d %s %s", + P_pid, P_pgrp, P_session, tty, 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, tty, P_state, + do_stime(), do_time(P_utime+P_stime) + ); + break; + case 'v'|0x80: + printf( + "%5d %s %c %s %6d - - %5d %s", + P_pid, tty, 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, tty, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime) + ); + break; + case 'l'|0x80: + printf( + "%03x %5d %5d %5d %3d %3d " + "%5ld %4ld %06x %c %s %s", + (unsigned)P_flags&0x777, P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice, + P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, tty, 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 direct *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==-1) continue; + if(!select_all && P_euid!=ouruid) continue; + } + found_a_proc++; + print_proc(); + } + closedir(dir); + exit(!found_a_proc); + } + return 0; +} diff --git a/oldps.1 b/oldps.1 new file mode 100644 index 00000000..69e6f74f --- /dev/null +++ b/oldps.1 @@ -0,0 +1,356 @@ +.\" This file Copyright 1992, 1997 Michael K. Johnson +.\" and 1996 Charles L. Blake +.\" It may be distributed under the GNU Public License, version 2, or +.\" any higher version. See section COPYING of the GNU Public license +.\" for conditions under which this file may be redistributed. +.TH PS 1 "3 Sep 1997" "Cohesive Systems" "Linux User's Manual" +.SH NAME +ps \- report process status +.SH SYNOPSIS +ps [\fBlujsvmaxScewhrnu\fR] [\fBt\fIxx\fR] \ +[\fBO\fR[\fB+\fR|\fB-\fR]\fIk1\fR[[\fB+\fR|\fB-\fR]\fIk2\fR...]] \ +[\fIpids\fR] + +there are also three long options: + +.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]] + +.B "\-\-help" + +.B "\-\-version" + +More long options are on the way... +.SH DESCRIPTION +.B "ps " +gives a snapshot of the current processes. If you want a repetitive +update of this status, use +.BR top . +This man page documents the +.IR /proc -based +version of +.BR ps , +or tries to. +.PP +.SH "COMMAND-LINE OPTIONS" +The command-line options for this version of +.B ps +are derived from the BSD version of +.BR ps , +not the System V version. + +The command-line arguments should \fBnot\fP be preceeded by a `\-' character, +because in the future, a `\-' will be used to indicate Unix98-standard +command-line arguments, while no `\-' will indicate the current +``extended BSD'' style of command line arguments. + +For now, ps will give you a warning if you use a `\-' for a short option, +but it will still work. If you have shell scripts which use BSD-style +arguments to ps, take heed of the warning and fix them, or else your +scripts will fail to function correctly at some point in the future. +If you want to turn off the warnings, set the +.B I_WANT_A_BROKEN_PS +environment variable. + +There are also some ``long options'' in GNU style; see below for those. +.PP +.PD 0 +.TP 0.5i +.B "l " +long format +.TP 0.5i +.B "u " +user format: gives user name and start time +.TP 0.5i +.B "j " +jobs format: pgid sid +.TP 0.5i +.B "s" +signal format +.TP 0.5i +.B "v " +vm format +.TP 0.5i +.B "m " +displays memory info (combine with +.B p +flag to get number of pages). +.TP 0.5i +.B "f " +"forest" family tree format for command line +.TP 0.5i +.B "a " +show processes of other users too +.TP 0.5i +.B "x " +show processes without controlling terminal +.TP 0.5i +.B "S " +add child cpu time and page faults +.TP 0.5i +.B "c " +command name from task_struct +.TP 0.5i +.B "e " +show environment after command line and ` + ' +.TP 0.5i +.B "w " +wide output: don't truncate command lines to fit on one line. +To be exact, every w that is specified will add another possible +line to the output. If the space isn't needed it isn't used. You +may up to 100 +.BR w 's. +.TP 0.5i +.B "h " +no header +.TP 0.5i +.B "r " +running procs only +.TP 0.5i +.B "n " +numeric output for +.BR USER " and " WCHAN . +.PD 1 +.TP 0.5i +.BI t xx +only procs with controlling tty \fIxx\fR; for \fIxx\fR you may use either the +name of a device file under "/dev" or that name with either +.IR tty " or " cu +sliced off. This is the reverse heuristic that ps uses to print out the +abbreviated tty name in the \fBTT\fR field, e.g. +.BR "ps t1" . +.TP 0.5i +.BR O [ + | - ] \fIk1 [,[ + | - ] \fIk2 [, ... ]] +Order the process listing according to the multi-level sort specified by +the sequence of \fIshort\fR keys from \fBSORT KEYS\fR, \fIk1\fR, \fIk2\fR, ... +Default order specifications exist for each of the various formats of \fBps\fR. +These are over-ridden by a user specified ordering. The `+' is quite optional, +merely re-iterating the default direction on a key. `-' reverses direction only +on the key it precedes. As with \fBt\fR and \fIpids\fR, the O option must be +the last option in a single command argument, but specifications in successive +arguments are catenated. +.TP 0.5i +.I pids +List only the specified processes; they are comma-delimited. The +list must be given immediately after the last option in a single command-line +argument, with no intervening space, e.g. +.BR "ps j1,4,5" . +Lists specified in subsequent arguments are catenated, e.g. +.B ps l 1,2 3,4 5 6 +will list all of the processes 1-6 in long format. If pids are given, they +are listed no matter what. If a tty is given matching processes are listed +no matter what. These two features override the 'a' and 'x' flags. +.SH "LONG COMMAND\-LINE OPTIONS" +These options are preceeded by a double\-hyphen. +.TP 0.5i +.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]] +Choose a \fImulti-letter key\fR from the \fBSORT KEYS\fR section. \fIX\fR may be +any convenient separator character. To be GNU-ish use `='. The `+' is really +optional since default direction is increasing numerical or lexicographic order. +E.g.: +.B ps jax --sort=uid,-ppid,+pid +.TP 0.5i +.B "\-\-help" +Get a help message that summarizes the usage and gives a list of +supported sort keys. This list may be more up to date than this man +page. +.TP 0.5i +.B "\-\-version" +Display version and source of this program. +.SH "SORT KEYS" +Note that the values used in sorting are the internal values \fBps\fR uses and +\fInot\fR the `cooked' values used in some of the output format fields. If +someone wants to volunteer to write special comparison functions for the cooked +values, ... ;-) + +SHORT LONG DESCRIPTION +.PD 0 +.TP 0.5i +c cmd simple name of executable +.TP 0.5i +C cmdline full command line +.TP 0.5i +f flags flags as in long format F field +.TP 0.5i +g pgrp process group ID +.TP 0.5i +G tpgid controlling tty process group ID +.TP 0.5i +j cutime cumulative user time +.TP 0.5i +J cstime cumulative system time +.TP 0.5i +k utime user time +.TP 0.5i +K stime system time +.TP 0.5i +m min_flt number of minor page faults +.TP 0.5i +M maj_flt number of major page faults +.TP 0.5i +n cmin_flt cumulative minor page faults +.TP 0.5i +N cmaj_flt cumulative major page faults +.TP 0.5i +o session session ID +.TP 0.5i +p pid process ID +.TP 0.5i +P ppid parent process ID +.TP 0.5i +r rss resident set size +.TP 0.5i +R resident resident pages +.TP 0.5i +s size memory size in kilobytes +.TP 0.5i +S share amount of shared pages +.TP 0.5i +t tty the minor device number of tty +.TP 0.5i +T start_time time process was started +.TP 0.5i +U uid user ID number +.TP 0.5i +u user user name +.TP 0.5i +v vsize total VM size in bytes +.TP 0.5i +y priority kernel scheduling priority +.PD 1 +.SH "FIELD DESCRIPTIONS" +.TP 0.5i +.B "PRI " +This is the counter field in the task struct. It is the time in +.B HZ +of the process's possible timeslice. +.TP 0.5i +.B "NI " +Standard unix nice value; a positive value means less cpu time. +.TP 0.5i +.B "SIZE " +Virtual image size; size of text+data+stack. +.TP 0.5i +.B "RSS " +Resident set size; kilobytes of program in memory. +.TP 0.5i +.B "WCHAN " +Name of the kernel function where the process is sleeping, with the +.RB ` sys_ ' +stripped from the function name. If +.I /etc/psdatabase +does not exist, it is just a hex number instead. +.TP 0.5i +.B "STAT " +Information about the status of the process. The first field is +.B R +for runnable, +.B S +for sleeping, +.B D +for uninterruptible sleep, +.B T +for stopped or traced, or +.B Z +for a zombie process. The second field contains +.B W +if the process has no resident pages. The third field is +.B N +if the process has a positive nice value +.RB ( NI +field). +.TP 0.5i +.B "TT " +Controlling tty. +.TP 0.5i +.B "PAGEIN " +Number of major page faults (page faults that cause pages to be read +from disk, including pages read from the buffer cache). +.TP 0.5i +.B "TRS " +Text resident size. +.TP 0.5i +.B "SWAP " +Kilobytes (or pages if +.B p +is used) on swap device. +.TP 0.5i +.B "SHARE " +Shared memory. +.SH UPDATING +This +.BR proc -based +.B ps +works by reading the files in the +.B proc +filesystem, mounted on +.BR /proc . +This +.B ps +does not need to be suid +.B kmem +or have any privileges to run. +.I "Do not give this ps any special permissions." +.PP +You will need to put in place the appropriate System.map file +when you install a new kernel in order +to get meaningful information from the +.B WCHAN +field. This should be done every time you compile a new kernel. You should +also run 'ps' as root once and then any time the tty devices in the "/dev" +directory change. + +As of procps-1.00, ps/top read System.map directly if it is available. The +search path for kernel address-to-symbol resolution is: +.nf + $PS_SYSTEM_MAP + /boot/System.map-`uname -r` + /boot/System.map + /lib/modules/`uname -r`/System.map + /etc/psdatabase + /boot/psdatabase-`uname -r` + /boot/psdatabase, + /lib/modules/`uname -r`/psdatabase +.fi +.PP +.SH NOTES +The member +.B used_math +of +.B task_struct +is not shown, since +.B crt0.s +checks to see if math is present. This causes the math flag to be set +for all processes, and so it is worthless. +.PP +Programs swapped out to disk will be shown without command line +arguments, and unless the +.B c +option is given, in parentheses. +.PP +.B %CPU +shows the cputime/realtime percentage. It will not add up to 100% +unless you are lucky. It is time used divided by the time the process +has been running. +.PP +The +.B SIZE +and +.B RSS +fields don't count the page tables and the +.B task_struct +of a proc; this is at least 12k of memory that is always resident. +.B SIZE +is the virtual size of the proc (code+data+stack). +.SH AUTHOR +.B ps +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. Michael K. Johnson + is the current maintainer. + +Please send bug reports to diff --git a/oldps.c b/oldps.c new file mode 100644 index 00000000..2ed865f3 --- /dev/null +++ b/oldps.c @@ -0,0 +1,660 @@ +/* + * ps.c - show process status + * + * Copyright (c) 1992 Branko Lankester + * + * Snarfed and HEAVILY modified for procps by Michael K. Johnson, + * (johnsonm@sunsite.unc.edu). What is used is what is required to have a + * common interface. + * + * Massive modifications by Charles Blake (cblake@bbn.com). Rewrite + * of the system process table code, multilevel sorting, device number + * database, forest feature (contributed by ...), environment variables, GNU + * style long options, pid list filtering (contributed by Michael Shields). + * + * Michael K. Johnson again in charge, merging + * patches that have accumulated over a long time. + * + * Changes Copyright (C) 1993, 1994, 1997 Michael K. Johnson, + * and Copyright (C) 1995, 1996 Charles Blake + * See file COPYING for copyright details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static void show_short(char*, proc_t*); +static void show_long (char*, proc_t*); +static void show_user (char*, proc_t*); +static void show_jobs (char*, proc_t*); +static void show_sig (char*, proc_t*); +static void show_vm (char*, proc_t*); +static void show_m (char*, proc_t*); +static void show_regs (char*, proc_t*); + +/* this struct replaces the previously parallel fmt_fnc and hdrs */ +static struct { + void (*format)(char*,proc_t*); + char CL_option_char; + const char* default_sort; + const char* header; +} mode[] = { + { show_short, 0 , "Up", " PID TTY STAT TIME COMMAND" }, + { show_long, 'l', "Pp", " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND" }, + { show_user, 'u', "up", "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND" }, + { show_jobs, 'j', "gPp"," PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND" }, + { show_sig, 's', "p", " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND" }, + { show_vm, 'v', "r", " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND" }, + { show_m, 'm', "r", " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND" }, + { show_regs, 'X', "p", "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND" }, + { NULL, 0, NULL, NULL } +}; + +/* some convenient integer constants corresponding to the above rows. */ +enum PS_MODALITY { PS_D = 0, PS_L, PS_U, PS_J, PS_S, PS_V, PS_M, PS_X }; + +static char * prtime (char *s, unsigned long t, unsigned long rel); +static void usage (char* context); +static void set_cmdspc (int w_opts); +static void show_procs (unsigned maxcmd, int do_header, int, void*,int); +static void show_cmd_env (char* tskcmd, char** cmd, char** env, unsigned maxch); +static void show_a_proc (proc_t* the_proc, unsigned maxcmd); +static void show_time (char *s, proc_t * p); +static void add_node (char *s, proc_t *task); +static int node_cmp (const void *s1, const void *s2); +static void show_tree (int n, int depth, char *continued); +static void show_forest (void); + +static int CL_fmt = 0; + +/* process list filtering command line options */ + +static int CL_all, + CL_kern_comm, + CL_no_ctty, + CL_run_only; +static char * CL_ctty; +static pid_t * CL_pids; /* zero-terminated list, dynamically allocated */ + +/* process display modification command line options */ + +static int CL_show_env, + CL_num_outp, /* numeric fields for user, wchan, tty */ + CL_sort = 1, + CL_forest, + CL_Sum, + CL_pg_shift = (PAGE_SHIFT - 10); /* default: show k instead of pages */ + +/* Globals */ + +static unsigned cmdspc = 80; /* space left for cmd+env after table per row */ +static int GL_current_time; /* some global system parameters */ +static long GL_time_now; +static int GL_wchan_nout; /* this is can also be set on the command-line */ + + +/*****************************/ +int main(int argc, char **argv) { + char *p; + int width = 0, + do_header = 1, + user_ord = 0, + next_arg = 0, + toppid = 0, + pflags, N = 1; + void* args = NULL; + dev_t tty[2] = { 0 }; + uid_t uid[1]; + + do { + --argc; /* shift to next arg. */ + ++argv; + for (p = *argv; p && *p; ++p) { + switch (*p) { + case '-': /* "--" ==> long name options */ + if (*(p+1) == '-') { + if (strncmp(p+2,"sort",4)==0) { + if (parse_long_sort(p+7)) + usage("unrecognized long sort option\n"); + user_ord = 1; + next_arg = 1; + break; + } else if (strncmp(p+2, "help", 4) == 0) { + usage(NULL); + return 0; + } else if (strncmp(p+2, "version", 6) == 0) { + display_version(); + return 0; + } else if (*(p+2) != '\0') /* just '-- '; not an error */ + usage("ps: unknown long option\n"); + } + if (!getenv("I_WANT_A_BROKEN_PS")) { + fprintf(stderr, + "warning: `-' deprecated; use `ps %s', not `ps %s'\n", + (p+1), p); + } + break; + case 'l': CL_fmt = PS_L; /* replaceable by a */ break; + case 'u': CL_fmt = PS_U; /* loop over mode[] */ break; + case 'j': CL_fmt = PS_J; break; + case 's': CL_fmt = PS_S; break; + case 'v': CL_fmt = PS_V; break; + case 'm': CL_fmt = PS_M; break; + case 'X': CL_fmt = PS_X; /* regs */ break; + + case 'r': CL_run_only = 1; /* list filters */ break; + case 'a': CL_all = 1; break; + case 'x': CL_no_ctty = 1; break; + case 't': CL_ctty = p + 1; + next_arg = 1; break; + + case 'e': CL_show_env = 1; /* output modifiers */ break; + case 'f': CL_forest = 1; + CL_kern_comm = 0; break; + case 'c': CL_kern_comm = 1; break; + case 'w': ++width; break; + case 'h': do_header = 0; break; + case 'n': CL_num_outp = 1; + GL_wchan_nout = 1; break; + case 'S': CL_Sum = 1; break; + case 'p': CL_pg_shift = 0; break; + case 'o': CL_sort = !CL_sort; break; + case 'O': + if (parse_sort_opt(p+1)) + usage("short form sort flag parse error\n"); + user_ord = 1; + next_arg = 1; + break; + case 'V': display_version(); exit(0); + default: + /* Step through, reading+alloc space for comma-delim pids */ + if (isdigit(*p)) { + while (isdigit(*p)) { + CL_pids = xrealloc(CL_pids, (toppid + 2)*sizeof(pid_t)); + CL_pids[toppid++] = atoi(p); + while (isdigit(*p)) + p++; + if (*p == ',') + p++; + } + CL_pids[toppid] = 0; + next_arg = 1; + } + if (*p) + usage("unrecognized option or trailing garbage\n"); + } + if (next_arg) { + next_arg = 0; + break; /* end loop over chars in this argument */ + } + } + } while (argc > 1); + + if (!CL_sort) /* since the unsorted mode is intended to be speedy */ + CL_forest = 0; /* turn off the expensive forest option as well. */ + + if (CL_fmt == PS_L) { + if (open_psdb(NULL)) GL_wchan_nout = 1; + } + + set_cmdspc(width); + + if (!(GL_current_time = uptime(0,0))) + return 1; + GL_time_now = time(0L); + + if (CL_sort && !user_ord) + parse_sort_opt(mode[CL_fmt].default_sort); + + /* NOTE: all but option parsing has really been done to enable + * multiple uid/tty/state filtering as well as multiple pid filtering + */ + pflags = PROC_ANYTTY; /* defaults */ + + if (!CL_kern_comm) pflags |= PROC_FILLCMD; /* verbosity flags */ + if (CL_fmt == PS_M) pflags |= PROC_FILLMEM; + if (CL_show_env) pflags |= PROC_FILLENV; + if (!CL_num_outp) pflags |= PROC_FILLUSR; + + if (CL_no_ctty) pflags &= ~PROC_ANYTTY; /* filter flags */ + if (CL_run_only) { pflags |= PROC_STAT; args = "RD"; } + else if (!CL_all) { pflags |= PROC_UID; args = uid; uid[0] = getuid(); pflags &= ~PROC_STAT; } + if (CL_pids) { pflags |= PROC_PID; args = CL_pids; pflags &= ~PROC_UID; pflags &= ~PROC_STAT; } + if (CL_ctty) { + if ((tty[0] = tty_to_dev(CL_ctty)) == (dev_t)-1) { + fprintf(stderr, "the name `%s' is not a tty\n", CL_ctty); + exit(1); + } + pflags = (pflags | PROC_TTY) & ~(PROC_ANYTTY|PROC_STAT|PROC_UID|PROC_PID); + args = tty; + } + show_procs(cmdspc, do_header, pflags, args, N); + return 0; +} + +/***************************/ +/* print a context dependent usage message and maybe exit */ +static void usage(char* context) { + fprintf(stderr, + "%s" + "usage: ps acehjlnrsSuvwx{t|#|O[-]u[-]U..} \\\n" + " --sort:[-]key1,[-]key2,...\n" + " --help gives you this message\n" + " --version prints version information\n", + context ? context : ""); + if (context) + exit(1); /* prevent bad exit status by calling usage(NULL) */ +} + +/*****************************/ +/* set maximum chars displayed on a line based on screen size. + * Always allow for the header, with n+1 lines of output per row. + */ +static void set_cmdspc(int n) { + struct winsize win; + int h = strlen(mode[CL_fmt].header), + c = strlen("COMMAND"); + + if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) + cmdspc = win.ws_col; + if (n > 100) n = 100; /* max of 100 'w' options */ + if (cmdspc > h) + cmdspc = cmdspc*(n+1) - h + c; + else + cmdspc = cmdspc*n + c; +} + +/*****************************/ +static const char *ttyc(int tty, int pid){ + static char outbuf[8]; + if(CL_num_outp) + snprintf(outbuf, sizeof outbuf, "%4.4x", tty); + else + dev_to_tty(outbuf, 4, tty, pid, ABBREV_TTY|ABBREV_DEV|ABBREV_PTS); + return outbuf; +} + +/*****************************/ +/* This is the main driver routine that iterates over the process table. + */ +static void show_procs(unsigned maxcmd, int do_header, int pflags, void* args, int N) { + static proc_t buf; /* less dynamic memory allocation when not sorting */ + PROCTAB* tab; + proc_t **ptable = NULL, *next, *retbuf = NULL; + int n = 0; + + /* initiate process table scan */ + tab = openproc(pflags, args, N); + if (!tab) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + + if (do_header) puts(mode[CL_fmt].header); /* print header */ + + if (!(CL_sort || CL_forest)) /* when sorting and forest are both */ + retbuf = &buf; /* off we can use a static buffer */ + + while ((next = readproc(tab,retbuf))) { /* read next process */ + n++; /* Now either: */ + if (CL_forest) { /* add process to tree */ + /* FIXME: this static is disgusting, but to keep binary + * compatibility within the 1.2 series I'll just go from + * 256 bytes to 1024 bytes, which should be good enough + * for most people.... :-( In the future, the format + * format function should probably dynamically allocate + * strings. + */ + static char s[1024]; + (mode[CL_fmt].format)(s, next); + if (CL_fmt != PS_V && CL_fmt != PS_M) + show_time(s+strlen(s), next); + add_node(s, next); + } else if (CL_sort) { /* add process to table */ + ptable = xrealloc(ptable, n*sizeof(proc_t*)); + ptable[n-1] = next; + } else { /* or show it right away */ + show_a_proc(&buf, maxcmd); + if (buf.cmdline) free((void*)(buf.cmdline[0])); + if (buf.environ) free((void*)(buf.environ[0])); + } + } + if (!n) { + fprintf(stderr, "No processes available.\n"); + exit(1); + } + if (CL_sort && !CL_forest) { /* just print sorted table */ + int i; + qsort(ptable, n, sizeof(proc_t*), (void*)mult_lvl_cmp); + for (i = 0; i < n; i++) { + show_a_proc(ptable[i], maxcmd); + freeproc(ptable[i]); + } + free(ptable); + } else if (CL_forest) + show_forest(); +} + +/*****************************/ +/* show the trailing command and environment in available space. + * use abbreviated cmd if requested, NULL list, or singleton NULL string + */ +static void show_cmd_env(char* tskcmd, char** cmd, char** env, unsigned maxch) { + if (CL_kern_comm) /* no () when explicit request for tsk cmd */ + maxch = print_str(stdout, tskcmd, maxch); + else if (!cmd || !*cmd || (!cmd[1] && !*cmd)) { + /* no /proc//cmdline ==> bounding () */ + if (maxch) { + fputc('(', stdout); + maxch--; + } + maxch = print_str(stdout, tskcmd, maxch); + if (maxch) { + fputc(')', stdout); + maxch--; + } + } else + maxch = print_strlist(stdout, cmd, " ", maxch); + if (CL_show_env && env) + print_strlist(stdout, env, " ", maxch); + fputc('\n', stdout); +} + + +/*****************************/ +/* format a single process for output. + */ +static void show_a_proc(proc_t* p, unsigned maxch) { + static char s[2048]; + (mode[CL_fmt].format)(s, p); + if (CL_fmt != PS_V && CL_fmt != PS_M) + show_time(s+strlen(s), p); + printf("%s", s); + show_cmd_env(p->cmd, p->cmdline, p->environ, maxch); +} + +/****** The format functions for the various formatting modes follow *****/ + +/*****************************/ +static void show_short(char *s, proc_t *p) { + sprintf(s, "%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p)); +} + + +/*****************************/ +static void show_long(char *s, proc_t *p) { + char wchanb[32]; + + if (GL_wchan_nout) + sprintf(wchanb, " %-9lx ", p->wchan & 0xffffffff); + else + sprintf(wchanb, "%-11.11s", wchan(p->wchan)); + sprintf(s, "%6lx %5d %5d %5d %3ld %3ld %6ld %5ld %-11.11s %s%3s", + p->flags, p->euid, p->pid, p->ppid, p->priority, p->nice, + p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), wchanb, status(p), + ttyc(p->tty,p->pid)); +} + + +/*****************************/ +static void show_jobs(char *s, proc_t *p) { + sprintf(s, "%5d %5d %5d %5d %3s %5d %s %5d ", + p->ppid, p->pid, p->pgrp, p->session, ttyc(p->tty,p->pid), p->tpgid, status(p), + p->euid); +} + + +/*****************************/ +static void show_user(char *s, proc_t *p) { + long pmem, total_time, seconds; + time_t start; + unsigned int pcpu; + + if (CL_num_outp) + s += sprintf(s, "%5d ", p->euid); + else + s += sprintf(s, "%-8s ", p->euser); + /* Hertz, being UL, forces 64-bit calculation on 64-bit machines */ + seconds = (((GL_current_time * Hertz) - p->start_time) / Hertz); + start = GL_time_now - seconds; + total_time = (p->utime + p->stime + + (CL_Sum ? p->cutime + p->cstime : 0)); + pcpu = seconds ? + (total_time * 10 * 100 / Hertz) / seconds : + 0; + if (pcpu > 999) pcpu = 999; + pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10)); + sprintf(s, "%5d %2u.%u %2ld.%ld %5ld %5ld %3s %s %.6s ", + p->pid, pcpu / 10, pcpu % 10, pmem / 10, pmem % 10, + p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), ttyc(p->tty,p->pid), status(p), + ctime(&start) + (GL_time_now - start > 3600*24 ? 4 : 10)); +} + + +/*****************************/ +/* TODO: this is broken... but does anyone care? */ +static void show_sig(char *s, proc_t *p) { +#undef SIGNAL_STRING /* not filled in by the function oldps calls */ +#ifdef SIGNAL_STRING +#define TAIL(s) ((s)+(long)strlen(s)-8) + sprintf(s, "%5d %5d %s %s %s %s %s %3s ", + p->euid, p->pid, + TAIL(p->signal), TAIL(p->blocked), TAIL(p->sigignore), TAIL(p->sigcatch), + status(p), ttyc(p->tty,p->pid) + ); +#else + sprintf(s, "%5d %5d %08x %08x %08x %08x %s %3s ", + p->euid, p->pid, + (unsigned)p->signal, (unsigned)p->blocked, (unsigned)p->sigignore, (unsigned)p->sigcatch, + status(p), ttyc(p->tty,p->pid) + ); +#endif +} + + +/*****************************/ +static void show_vm(char *s, proc_t *p) { + int pmem; + + s += sprintf(s,"%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p)); + show_time(s, p); + s += strlen(s); + s += sprintf(s, " %6ld %4ld %4ld %4ld ", + p->maj_flt + (CL_Sum ? p->cmaj_flt : 0), + p->vsize ? (p->end_code - p->start_code) >> 10 : 0, + p->vsize ? (p->vsize - p->end_code + p->start_code) >> 10 : 0, + p->rss << (PAGE_SHIFT - 10)); + if(p->rss_rlim == RLIM_INFINITY) + s += sprintf(s, " xx "); + else + s += sprintf(s, "%5ld ", p->rss_rlim >> 10); + pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10)); + sprintf(s, "%2d.%d ", pmem / 10, pmem % 10); +} + + + +/*****************************/ +static void show_m(char *s, proc_t *p) { + sprintf(s, "%5d %3s %6ld %6ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld %3ld ", + p->pid, ttyc(p->tty,p->pid), + p->maj_flt + (CL_Sum ? p->cmaj_flt : 0), + p->min_flt + (CL_Sum ? p->cmin_flt : 0), + p->trs << CL_pg_shift, + p->drs << CL_pg_shift, + p->size << CL_pg_shift, + (p->size - p->resident) << CL_pg_shift, + p->resident << CL_pg_shift, + p->share << CL_pg_shift, + p->lrs << CL_pg_shift, + p->dt); +} + + +/*****************************/ +static void show_regs(char *s, proc_t *p) { + char time1[16], time2[16]; + + sprintf(s, "%2ld %5d %8lx %8lx %8lx %s %s %s %3s ", + p->start_code >> 26, p->pid, p->start_stack, + p->kstk_esp, p->kstk_eip, + prtime(time1, p->timeout, GL_current_time*Hertz), + prtime(time2, p->it_real_value, 0), + status(p), ttyc(p->tty,p->pid)); +} + + +/*****************************/ +static char *prtime(char *s, unsigned long t, unsigned long rel) { + if (t == 0) { + sprintf(s, " "); + return s; + } + if ((long) t == -1) { + sprintf(s, " xx"); + return s; + } + if ((long) (t -= rel) < 0) + t = 0; + if (t > 9999) + sprintf(s, "%5lu", t / 100); + else + sprintf(s, "%2lu.%02lu", t / 100, t % 100); + return s; +} + + +/*****************************/ +static void show_time(char *s, proc_t * p) { + unsigned t; + t = (p->utime + p->stime) / Hertz; + if (CL_Sum) t += (p->cutime + p->cstime) / Hertz; + sprintf(s, "%3d:%02d ", t / 60, t % 60); +} + + + +/*****************************/ +/* fancy process family tree based cmdline printing. Building the tree + should be relegated to libproc and only the printing logic should + remain here. +*/ +static struct tree_node * node; /* forest mode globals */ +static int nodes = 0; +static int maxnodes = 0; + +/*****************************/ + +static void add_node(char *s, proc_t *task) { + if (maxnodes == 0) { + maxnodes = 64; + node = (struct tree_node *) + xmalloc(sizeof(struct tree_node) * maxnodes); + } + if (nodes >= maxnodes) { + maxnodes *= 2; + node = (struct tree_node *) + xrealloc(node, sizeof(struct tree_node) * maxnodes); + } + node[nodes].proc = task; + node[nodes].pid = task->pid; + node[nodes].ppid = task->ppid; + node[nodes].line = strdup(s); + node[nodes].cmd = task->cmd; + node[nodes].cmdline = task->cmdline; + node[nodes].environ = task->environ; + node[nodes].children = 0; + node[nodes].have_parent = 0; + nodes++; +} + + +/*****************************/ +static int node_cmp(const void *s1, const void *s2) { + struct tree_node *n1 = (struct tree_node *) s1; + struct tree_node *n2 = (struct tree_node *) s2; + return n1->pid - n2->pid; +} + + +/*****************************/ +static void show_tree(int n, int depth, char *continued) { + int i, cols = 0; + + fprintf(stdout, "%s", node[n].line); + for (i = 0; i < depth; i++) { + if (cols + 4 >= cmdspc - 1) + break; + if (i == depth - 1) + printf(" \\_ "); + else if (continued[i]) + printf(" | "); + else + printf(" "); + cols += 4; + } + show_cmd_env(node[n].cmd, node[n].cmdline, node[n].environ, cmdspc - cols); + for (i = 0; i < node[n].children; i++) { + continued[depth] = i != node[n].children - 1; + show_tree(node[n].child[i], depth + 1, continued); + } +} + + +/*****************************/ +static void show_forest() { + register int i, j; + int parent; + char continued[1024]; + + if (CL_sort) + qsort((void*)node, nodes, sizeof(struct tree_node), (void*)node_mult_lvl_cmp); + + for (i = 0; i < nodes; i++) { + if (node[i].ppid > 1 && node[i].pid != node[i].ppid) { + parent = -1; + for (j=0; j= 0) { + node[i].have_parent++; + if (node[parent].children == 0) { + node[parent].child = (int*)xmalloc(16 * sizeof(int*)); + node[parent].maxchildren = 16; + } + else if (node[parent].children == node[parent].maxchildren) { + node[parent].maxchildren *= 2; + node[parent].child = (int*)xrealloc(node[parent].child, + node[parent].maxchildren + * sizeof(int*)); + } + node[parent].child[node[parent].children++] = i; + } + } + + for (i = 0; i < nodes; i++) { + if (!node[i].have_parent) + show_tree(i, 0, continued); + } +} diff --git a/pgrep.1 b/pgrep.1 new file mode 100644 index 00000000..346c5d08 --- /dev/null +++ b/pgrep.1 @@ -0,0 +1,158 @@ +.\" 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 [\-flnvx] [\-d \fIdelimiter\fP] [\-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] [\-fnvx] [\-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 +\-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 \-v can not be combined. Let me know if you need +to do this. + +Defunct processes are reported. + +.SH "SEE ALSO" +ps(1) proc(5) regex(5) + +.SH STANDARDS +\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This +implementation is fully compatible. + +.SH AUTHOR +Kjetil Torgrim Homme + +Michael K. Johnson 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 00000000..91f6a3a5 --- /dev/null +++ b/pgrep.c @@ -0,0 +1,605 @@ +/* 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 + */ + + +#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" + +static int i_am_pkill = 0; +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_newest = 0; +static int opt_negate = 0; +static int opt_exact = 0; +static int opt_signal = SIGTERM; + +static char *opt_delim = "\n"; +static union el *opt_pgrp = NULL; +static union el *opt_gid = 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_uid = NULL; +static char *opt_pattern = NULL; + +/* Prototypes */ + +static union el *split_list (const char *, char, int (*)(const char *, union el *)); +static int conv_uid (const char *, union el *); +static int conv_gid (const char *, union el *); +static int conv_sid (const char *, union el *); +static int conv_pgrp (const char *, union el *); +static int conv_num (const char *, union el *); +static int conv_str (const char *, union el *); +static int match_numlist (long, const union el *); +static int match_strlist (const char *, const union el *); + + +static int +usage (int opt) +{ + if (i_am_pkill) + fprintf (stderr, "Usage: pgrep [-flnvx] [-d DELIM] "); + else + fprintf (stderr, "Usage: pkill [-SIGNAL] [-fnvx] "); + fprintf (stderr, "[-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n" + "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] " + "[PATTERN]\n"); + exit (opt == '?' ? 0 : 2); +} + + +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, "fnvxP:g:s:u:U:G:t:?"); + + while ((opt = getopt (argc, argv, opts)) != -1) { + switch (opt) { + case 'f': + opt_full = 1; + break; + case 'l': + opt_long = 1; + break; + case 'n': + opt_newest = 1; + ++criteria_count; + break; + case 'v': + opt_negate = 1; + break; + case 'x': + opt_exact = 1; + break; + case 'd': + opt_delim = strdup (optarg); + break; + case 'P': + opt_ppid = split_list (optarg, ',', conv_num); + if (opt_ppid == NULL) + usage (opt); + ++criteria_count; + break; + case 'g': + opt_pgrp = split_list (optarg, ',', conv_pgrp); + if (opt_pgrp == NULL) + usage (opt); + break; + case 's': + opt_sid = split_list (optarg, ',', conv_sid); + if (opt_sid == NULL) + usage (opt); + ++criteria_count; + break; + case 'u': + opt_euid = split_list (optarg, ',', conv_uid); + if (opt_euid == NULL) + usage (opt); + ++criteria_count; + break; + case 'U': + opt_uid = split_list (optarg, ',', conv_uid); + if (opt_uid == NULL) + usage (opt); + ++criteria_count; + break; + case 'G': + opt_gid = split_list (optarg, ',', conv_gid); + if (opt_gid == NULL) + usage (opt); + ++criteria_count; + break; + case 't': + opt_term = split_list (optarg, ',', conv_str); + if (opt_term == NULL) + usage (opt); + ++criteria_count; + break; + case '?': + usage (opt); + break; + } + } + 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); + } +} + + +static union el * +split_list (const char *str, char sep, int (*convert)(const char *, union el *)) +{ + char *copy = strdup (str); + char *ptr = copy; + char *sep_pos; + int i = 1, size = 32; + union el *list; + + list = malloc (size * sizeof (union el)); + if (list == NULL) + exit (3); + + do { + sep_pos = strchr (ptr, sep); + if (sep_pos) + *sep_pos = 0; + if (convert (ptr, &list[i])) + ++i; + else + exit (2); + if (i == size) { + size *= 2; + list = realloc (list, size * sizeof (union el)); + if (list == NULL) + exit (3); + } + if (sep_pos) + ptr = sep_pos + 1; + } while (sep_pos); + + free (copy); + if (i == 1) { + free (list); + list = NULL; + } else { + list[0].num = i - 1; + } + 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 *str, long *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); +} + +static int +conv_uid (const char *name, union el *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 *name, union el *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 *name, union el *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 *name, union el *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 *name, union el *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 *name, union el *e) +{ + e->str = strdup (name); + return (1); +} + + +static int +match_numlist (long value, const union el *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 *value, const union el *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 *list) +{ + int i; + for (i = 1; i < list[0].num; i++) + printf ("%ld%s", list[i].num, opt_delim); + + if (list[0].num) + printf ("%ld\n", list[i].num); +} + +static void +output_strlist (const union el *list) +{ + int i; + for (i = 1; i < list[0].num; i++) + printf ("%s%s", list[i].str, opt_delim); + + if (list[0].num) + printf ("%s\n", list[i].str); +} + +static PROCTAB * +do_openproc () +{ + PROCTAB *ptp; + int flags = PROC_FILLANY; + + if (opt_pattern || opt_full) + flags |= PROC_FILLCMD; + if (opt_uid) + flags |= PROC_FILLSTATUS; + 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 (3); + 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 () +{ + regex_t *preg = NULL; + + if (opt_pattern) { + char *re; + char errbuf[256]; + int re_err; + + preg = malloc (sizeof (regex_t)); + if (preg == NULL) + exit (3); + if (opt_exact) { + re = malloc (strlen (opt_pattern) + 5); + if (re == NULL) + exit (3); + sprintf (re, "^(%s)$", opt_pattern); + } else { + re = opt_pattern; + } + + re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB); + if (re_err) { + regerror (re_err, preg, errbuf, sizeof(errbuf)); + fprintf (stderr, errbuf); + exit (2); + } + } + return preg; +} + +#ifdef NOT_USED +static time_t +jiffies_to_time_t (long jiffies) +{ + static time_t time_of_boot = 0; + if (time_of_boot == 0) { + time_of_boot = time (NULL) - uptime (0, 0); + } + return (time_of_boot + jiffies / Hertz); +} +#endif + +static union el * +select_procs () +{ + PROCTAB *ptp; + proc_t task; + long newest_start_time = 0; + pid_t newest_pid = 0; + int matches = 0; + int size = 32; + regex_t *preg; + pid_t myself = getpid(); + union el *list; + char cmd[4096]; + + list = malloc (size * sizeof (union el)); + if (list == NULL) + exit (3); + + ptp = do_openproc (); + preg = do_regcomp (); + + memset (&task, 0, sizeof (task)); + while (readproc (ptp, &task)) { + int match = 1; + + if (task.pid == myself) + continue; + else if (opt_newest && task.start_time < newest_start_time) + match = 0; + else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid)) + 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_uid && ! match_numlist (task.ruid, opt_uid)) + match = 0; + else if (opt_gid && ! match_numlist (task.rgid, opt_gid)) + match = 0; + else if (opt_sid && ! match_numlist (task.session, opt_sid)) + match = 0; + else if (opt_term) { + if (task.tty == -1) { + match = 0; + } else { + char tty[256]; + dev_to_tty (tty, sizeof(tty) - 1, + task.tty, task.pid, 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 (newest_start_time == task.start_time && + newest_pid > task.pid) + continue; + newest_start_time = task.start_time; + newest_pid = task.pid; + matches = 0; + } + if (opt_long) { + char buff[4096]; + sprintf (buff, "%d %s", task.pid, cmd); + list[++matches].str = strdup (buff); + } else { + list[++matches].num = task.pid; + } + if (matches == size) { + size *= 2; + list = realloc (list, + size * sizeof (union el)); + if (list == NULL) + exit (3); + } + } + + memset (&task, 0, sizeof (task)); + } + closeproc (ptp); + + list[0].num = matches; + return (list); +} + + +int +main (int argc, char **argv) +{ + union el *procs; + + parse_opts (argc, argv); + + procs = select_procs (); + if (i_am_pkill) { + int i; + for (i = 1; i <= procs[0].num; i++) { + if (kill (procs[i].num, opt_signal) == -1) + fprintf (stderr, "pkill: %ld - %s\n", + procs[i].num, strerror (errno)); + } + } else { + if (opt_long) + output_strlist (procs); + else + output_numlist (procs); + } + return ((procs[0].num) == 0 ? 1 : 0); +} diff --git a/pkill.1 b/pkill.1 new file mode 100644 index 00000000..0e94b52c --- /dev/null +++ b/pkill.1 @@ -0,0 +1 @@ +.so man1/pgrep.1 diff --git a/pmap.c b/pmap.c new file mode 100644 index 00000000..ad01e1ec --- /dev/null +++ b/pmap.c @@ -0,0 +1,34 @@ +#include +#include +#include + +void usage(void){ + fprintf(stderr, + "Usage: pmap [-r] [-x] pid...\n" + "-r ignored (compatibility option)\n" + "-x show details\n" + ); + exit(1); +} + +int one_proc(unsigned pid){ + char cmdbuf[64]; + char buf[32]; + int fd; + sprintf(buf,"/proc/%u/cmdline",pid); + if( ((fd=open(buf)) == -1) return 1; + count = read(fd, cmdbuf, sizeof(cmdbuf)-1); + if(count<1) return 1; + cmdbuf[count] = '\0'; + while(count--) if(!isprint(cmdbuf[count])) cmdbuf[count]=' '; + close(fd); + sprintf(buf,"/proc/%u/maps",pid); + if( ((fd=open(buf)) == -1) return 1; + printf("%u: %s\n", pid, cmdbuf); + if(x_option) + printf("Address kB Resident Shared Private Permissions Name\n"); +@@@ FIXME FIXME FIXME @@@ +} + +int main(int argc, char *argv[]){ +} diff --git a/proc/.cvsignore.patch b/proc/.cvsignore.patch new file mode 100644 index 00000000..f12054b0 --- /dev/null +++ b/proc/.cvsignore.patch @@ -0,0 +1,7 @@ +diff -Naur procps-2.0.6/proc/.cvsignore procps-2.0.7/proc/.cvsignore +--- procps-2.0.6/proc/.cvsignore Wed Dec 31 19:00:00 1969 ++++ procps-2.0.7/proc/.cvsignore Fri Jul 14 16:45:01 2000 +@@ -0,0 +1,3 @@ ++.depend ++signames.h ++libproc.so.* diff --git a/proc/COPYING b/proc/COPYING new file mode 100644 index 00000000..92b8903f --- /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/Makefile b/proc/Makefile new file mode 100644 index 00000000..bc41bf14 --- /dev/null +++ b/proc/Makefile @@ -0,0 +1,98 @@ +# Auto-adaptive C library Makefile adapted for libproc, Chuck Blake. +# Assumptions are basically that all the .c files in the CWD are modules +# for the library and that all .h files are the interface to the library. + +# PROJECT SPECIFIC MACROS +NAME = proc + +# INSTALLATION OPTIONS +TOPDIR = /usr +HDRDIR = $(TOPDIR)/include/$(NAME)# where to put .h files +LIBDIR = $(TOPDIR)/lib# where to put library files +SHLIBDIR = /lib# where to put shared library files +HDROWN = $(OWNERGROUP) # owner of header files +LIBOWN = $(OWNERGROUP) # owner of library files +INSTALL = install + +# ----------------------------------------------------------------# +# The rest is the auto-magic section -- highly GNU make dependent # +# You should never need to edit this. # +# ----------------------------------------------------------------# + +VC_SUF = ,v +VC_PFX = RCS/ +RCSFILES = $(patsubst $(VC_PFX)%$(VC_SUF),%,$(wildcard $(VC_PFX)*$(VC_SUF))) + +# We take the union of RCS files and other files in CWD so that new files do +# not need to alter this makefile. 'sort' removes duplicates. This allows the +# convenience of compiling and testing new files before the initial check-in. + +SRC = $(sort $(wildcard *.c) $(filter %.c,$(RCSFILES))) +HDR = $(sort $(wildcard *.h) $(filter %.h,$(RCSFILES))) + +OBJ = $(SRC:.c=.o) +SONAME = lib$(NAME).so.$(LIBVERSION) + +ifeq ($(SHARED),1) +CFLAGS += -fpic +all: lib$(NAME).a $(SONAME) +else +all: lib$(NAME).a +endif + +lib$(NAME).a: $(OBJ) + $(AR) rcs $@ $^ + +$(SONAME): $(OBJ) + gcc -shared -Wl,-soname,$(SONAME) -o $@ $^ -lc + ln -sf $(SONAME) lib$(NAME).so + +# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT + +.depend: + $(strip $(CC) $(CFLAGS) -MM -MG $(SRC) > .depend) +-include .depend + +# INSTALLATION + +install: all + if ! [ -d $(HDRDIR) ] ; then mkdir $(HDRDIR) ; fi + $(INSTALL) $(HDROWN) $(HDR) $(TOPDIR)/include/$(NAME) + $(INSTALL) $(LIBOWN) lib$(NAME).a $(LIBDIR) +ifeq ($(SHARED),1) + $(INSTALL) $(LIBOWN) $(SONAME) $(SHLIBDIR) + cd $(SHLIBDIR) && ln -sf $(SONAME) lib$(NAME).so + ldconfig +endif + +# VARIOUS SHORT CUT TARGETS +.PHONY: all install dep clean distclean checkout checkclean + +dep: .depend + +clean: + $(RM) lib$(NAME).* *.o DEADJOE + +distclean: clean + $(RM) .depend *~ + +checkout: + $(CO) $(RCSFILES) + +checkclean: + $(RM) $(RCSFILES) + +# CUSTOM c -> o rule so that command-line has minimal whitespace + +%.o : %.c + $(strip $(CC) $(CFLAGS) -c $<) + +# PROJECT SPECIFIC DEPENDENCIES/BUILD RULES + + +version.o: version.c version.h +ifdef MINORVERSION + $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c version.c) +else + $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -c version.c) +endif diff --git a/proc/alloc.c b/proc/alloc.c new file mode 100644 index 00000000..0a616cec --- /dev/null +++ b/proc/alloc.c @@ -0,0 +1,49 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* 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 + +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); + } else { + 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/compare.c b/proc/compare.c new file mode 100644 index 00000000..46695ac5 --- /dev/null +++ b/proc/compare.c @@ -0,0 +1,304 @@ +/* + * + * Copyright 1994 Charles Blake and Michael K. Johnson + * This file is a part of procps, which is distributable + * under the conditions of the GNU Library General Public License. + * See the file COPYING for details. + * + */ + +#include /* for strcmp */ +#include /* for parse error output */ + +#include "proc/readproc.h" /* for proc_t */ +#include "proc/tree.h" /* for struct tree_node */ + +#include "proc/compare.h" /* for this code */ + + +/* + This module was written by Charles Blake for procps. + +mult_lvl_cmp: + slick general purpose multi-level compare function I invented. +sort_depth: + the number of levels of functions *to use*. This means many more levels + can be defined than mult_lvl_cmp tres out. If this is 1 then mult_lvl_cmp + is just a trivial wrapper around (*sort_function[0]). +sort_direction: + multiplicative factor for the output of cmp_whatever. + 1 ==> default order, -1 ==> reverse order, 0 ==> forced equality + The 0 bit is the neat part. Since a value of zero is the code for equality + multiplying the output of cmp_foo(a,b) forces a==b to be true. This is a + convenient way to turn sorting off in middle levels of a multi-level sort. + If time is a problem, reforming the whole sort_function array to not include + these unsorted middle levels will be faster since then cmp_foo won't even + be called. It might simplify some code depending upon how you organize it. +sort_function[]: + array of function pointers that points to our family of comparison functions + (I have named them cmp_* but mult_lvl_cmp doesn't care what they're named). + This may be declared and initialized like so: + int (*sort_function[])(void* a, void* b)={&cmp_foo, &cmp_bar, &cmp_hiho}; + You could also use my command line '-O' parser below. + +Note that we only descend levels until the order is determined. If we descend +all levels, that means that the items are equal at all levels, so we return 0. +Otherwise we return whatever the level's cmp_foo function would have returned. +This allows whatever default behavior you want for cmp_foo. sort_direction[] +reverses this default behavior, but mult_lvl_cmp doesn't decide that ascending +or descending is the default. That is the job of your cmp_foo's. +*/ + +/* the only reason these are global is because qsort(3) likes it that way. + It's also a little more efficient if mult_lvl_cmp() is called many times. +*/ + +static int sort_depth = 0; +static int sort_direction[10]; /* storage for 10 levels, but 4 would be plenty!*/ +static int (*sort_function[10])(void* a, void* b); + +int mult_lvl_cmp(void* a, void* b) { + int i, cmp_val; + for(i = 0; i < sort_depth; i++) { + cmp_val = sort_direction[i] * (*sort_function[i])(a,b); + if (cmp_val != 0) + return cmp_val; + } + return 0; +} + +int node_mult_lvl_cmp(void* a, void* b) { + int i, cmp_val; + for(i = 0; i < sort_depth; i++) { + cmp_val = sort_direction[i] * (*sort_function[i])(&(((struct tree_node *)a)->proc),&(((struct tree_node *)b)->proc)); + if (cmp_val != 0) + return cmp_val; + } + return 0; +} + +/* qsort(3) compliant comparison functions for all members of the ps_proc + structure (in the same order in which they appear in the proc_t declaration) + return is {-1,0,1} as {ab} + default ordering is ascending for all members. (flip 1,-1 to reverse) +*/ +/* pre-processor macros to cut down on source size (and typing!) + Note the use of the string concatenation operator ## +*/ +#define CMP_STR(NAME) \ +static int cmp_ ## NAME(proc_t** P, proc_t** Q) { \ + return strcmp((*P)->NAME, (*Q)->NAME); \ +} + +#define CMP_INT(NAME) \ +static int cmp_ ## NAME (proc_t** P, proc_t** Q) { \ + if ((*P)->NAME < (*Q)->NAME) return -1; \ + if ((*P)->NAME > (*Q)->NAME) return 1; \ + return 0; \ +} + +/* Define the (46!) cmp_ functions with the above macros for every element + of proc_t. If the binary gets too big, we could nuke inessentials. +*/ + +/* CMP_STR(cmdline) */ +CMP_STR(ruser) +CMP_STR(euser) +CMP_STR(cmd) +/* CMP_INT(state) */ +/* CMP_STR(ttyc) */ +CMP_INT(euid) +CMP_INT(pid) +CMP_INT(ppid) +CMP_INT(pgrp) +CMP_INT(session) +CMP_INT(tty) +CMP_INT(tpgid) +CMP_INT(utime) +CMP_INT(stime) +CMP_INT(cutime) +CMP_INT(cstime) +/* CMP_INT(priority) */ +CMP_INT(nice) +CMP_INT(start_time) +/* CMP_INT(signal) */ +/* CMP_INT(blocked) */ +/* CMP_INT(sigignore) */ +/* CMP_INT(sigcatch) */ +CMP_INT(flags) +CMP_INT(min_flt) +CMP_INT(cmin_flt) +CMP_INT(maj_flt) +CMP_INT(cmaj_flt) +/* CMP_INT(timeout) */ +CMP_INT(vsize) +CMP_INT(rss) +/* CMP_INT(rss_rlim) */ +/* CMP_INT(start_code) */ +/* CMP_INT(end_code) */ +/* CMP_INT(start_stack) */ +/* CMP_INT(kstk_esp) */ +/* CMP_INT(kstk_eip) */ +/* CMP_INT(wchan) */ +CMP_INT(pcpu) +CMP_INT(size) +CMP_INT(resident) +CMP_INT(share) +/* CMP_INT(trs) */ +/* CMP_INT(lrs) */ +/* CMP_INT(drs) */ +/* CMP_INT(dt) */ + +/* define user interface to sort keys. Fairly self-explanatory. */ + +static struct cmp_fun_struct { + char letter; /* single option-letter for key */ + char name[15]; /* long option name for key */ + int (*fun)(proc_t**, proc_t**); /* pointer to cmp_key */ +} cmp[] = { +/* { '?', "cmdline", &cmp_cmdline }, */ + { 'u', "user", &cmp_euser }, +/* { '?', "ruser", &cmp_ruser }, */ + { 'c', "cmd", &cmp_cmd }, +/* { '?', "state", &cmp_state }, */ +/* { '?', "ttyc", &cmp_ttyc }, */ + { 'U', "uid", &cmp_euid }, + { 'p', "pid", &cmp_pid }, + { 'P', "ppid", &cmp_ppid }, + { 'g', "pgrp", &cmp_pgrp }, + { 'o', "session", &cmp_session }, + { 't', "tty", &cmp_tty }, + { 'G', "tpgid", &cmp_tpgid }, + { 'k', "utime", &cmp_utime }, + { 'K', "stime", &cmp_stime }, + { 'j', "cutime", &cmp_cutime }, + { 'J', "cstime", &cmp_cstime }, +/* { '?', "counter", &cmp_counter }, */ + { 'y', "priority", &cmp_nice }, + { 'T', "start_time", &cmp_start_time }, +/* { '?', "signal", &cmp_signal }, */ +/* { '?', "blocked", &cmp_blocked }, */ +/* { '?', "sigignore", &cmp_sigignore }, */ +/* { '?', "sigcatch", &cmp_sigcatch }, */ + { 'f', "flags", &cmp_flags }, + { 'm', "min_flt", &cmp_min_flt }, + { 'n', "cmin_flt", &cmp_cmin_flt }, + { 'M', "maj_flt", &cmp_maj_flt }, + { 'N', "cmaj_flt", &cmp_cmaj_flt }, +/* { 'C', "timeout", &cmp_timeout }, */ + { 'v', "vsize", &cmp_vsize }, + { 'r', "rss", &cmp_rss }, +/* { '?', "rss_rlim", &cmp_rss_rlim }, */ +/* { '?', "start_code", &cmp_start_code }, */ +/* { '?', "end_code", &cmp_end_code }, */ +/* { '?', "start_stack", &cmp_start_stack }, */ +/* { '?', "kstk_esp", &cmp_kstk_esp }, */ +/* { '?', "kstk_eip", &cmp_kstk_eip }, */ +/* { '?', "wchan", &cmp_wchan }, */ + { 'C', "pcpu", &cmp_pcpu }, + { 's', "size", &cmp_size }, + { 'R', "resident", &cmp_resident }, + { 'S', "share", &cmp_share }, +/* { '?', "trs", &cmp_trs }, */ +/* { '?', "lrs", &cmp_lrs }, */ +/* { '?', "drs", &cmp_drs }, */ +/* { '?', "dt", &cmp_dt }, */ + { '\0',"terminator", NULL } +}; + +/* command line option parsing. Assign sort_{depth,direction[],function[]} + based upon a string of the form: + [+-]a[+-]b[+-]c... + with a,b,c,... being letter flags corresponding to a particular sort + key and the optional '-' specifying a reverse sort on that key. + doesn't + mean anything, but it keeps things looking balanced... +*/ +const char *parse_sort_opt(const char* opt) { + int i, next_dir=1; + for(; *opt ; ++opt) { + if (*opt == '-' || *opt == '+') { + if (*opt == '-') + next_dir = -1; + opt++; + continue; + } + for (i = 0; cmp[i].letter; i++) + if (*opt == cmp[i].letter) + break; + if (!cmp[i].letter) { /* failed, clear and return */ + sort_depth=0; + for (i=0;i<10;i++){ + sort_direction[i]=0; + sort_function[i]=(cmp_t)NULL; + } + return "Unknown sort key."; + } else { +#ifdef DEBUG + fprintf(stderr, + "sort level %d: key %s, direction % d\n", + sort_depth, cmp[i].name, next_dir); +#endif + sort_function[sort_depth] = (cmp_t)cmp[i].fun; + sort_direction[sort_depth++] = next_dir; + next_dir = 1; + } + } + return NULL; +} + +const char *parse_long_sort(const char* opt) { + char* comma; + int i, more_keys, next_dir=1; + do { + if (*opt == '-' || *opt == '+') { + if (*opt == '-') + next_dir = -1; + more_keys = 1; + opt++; + continue; + } + more_keys = ((comma=index(opt,',')) != NULL); + /* keys are ',' delimited */ + if (more_keys) + *comma='\0'; /* terminate for strcmp() */ + for(i = 0; cmp[i].letter; ++i) + if (strcmp(opt, cmp[i].name) == 0) + break; + if (!cmp[i].letter) { /* failed, clear and return */ + sort_depth=0; + for (i=0;i<10;i++){ + sort_direction[i]=0; + sort_function[i]=(cmp_t)NULL; + } + return "Unknown sort key."; + } else { +#ifdef DEBUG + fprintf(stderr, + "sort level %d: key %s, direction % d\n", + sort_depth, cmp[i].name, next_dir); +#endif + sort_function[sort_depth] = (cmp_t)cmp[i].fun; + sort_direction[sort_depth++] = next_dir; + next_dir = 1; + } + opt = comma + 1; /* do next loop on next key, if more keys, else done*/ + } while (more_keys); + return NULL; +} + +void reset_sort_options (void) +{ + int i; + + sort_depth=0; + for (i=0;i<10;i++){ + sort_direction[i]=0; + sort_function[i]=(cmp_t)NULL; + } +} + +void register_sort_function (int dir, cmp_t func) +{ + sort_function[sort_depth] = func; + sort_direction[sort_depth++] = dir; +} diff --git a/proc/compare.h b/proc/compare.h new file mode 100644 index 00000000..153717a9 --- /dev/null +++ b/proc/compare.h @@ -0,0 +1,9 @@ +typedef int (*cmp_t)(void*,void*); /* for function pointer casts */ + +extern void register_sort_function (int dir, cmp_t func); +extern void reset_sort_options(void); +extern int mult_lvl_cmp(void* a, void* b); +extern int node_mult_lvl_cmp(void* a, void* b); +extern const char *parse_sort_opt(const char* opt); +extern const char *parse_long_sort(const char* opt); + diff --git a/proc/devname.c b/proc/devname.c new file mode 100644 index 00000000..0a2520d9 --- /dev/null +++ b/proc/devname.c @@ -0,0 +1,211 @@ +/* + * Copyright 1998 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 "devname.h" + +#include +#ifndef PAGE_SIZE +#define PAGE_SIZE (sizeof(long)*1024) +#endif + +/* Who uses what: + * + * tty_to_dev oldps, w (there is a fancy version in ps) + * dev_to_tty oldps, top, ps + */ + +typedef struct tty_map_node { + struct tty_map_node *next; + int major_number; /* not unsigned! Ugh... */ + char name[4]; +} 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, 9999); + if(bytes == -1) goto fail; + buf[bytes] = '\0'; + p = buf; + while(( p = strstr(p, " /dev/tty") )){ + tty_map_node *tmn; + int len; + p += 9; + len = strspn(p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if(!len) continue; + if(len>3) continue; + if((len=1) && (*p=='S')) continue; + tmn = malloc(sizeof(tty_map_node)); + tmn->next = tty_map; + tty_map = tmn; + memset(tmn->name, '\0', 4); + strncpy(tmn->name, p, len); + p += len; + tmn->major_number = atoi(p); + } +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 * const buf, int maj, int 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) break; + tmn = tmn->next; + } + sprintf(buf, "/dev/tty%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */ + if(stat(buf, &sbuf) < 0) return 0; + if(min != minor(sbuf.st_rdev)) return 0; + if(maj != major(sbuf.st_rdev)) return 0; + return 1; +} + +/* Try to guess the device name (useful until /proc/PID/tty is added) */ +static int guess_name(char * const buf, int maj, int min){ + struct stat sbuf; + int t0, t1; + int tmpmin = min; + switch(maj){ + case 4: + if(min<64){ + sprintf(buf, "/dev/tty%d", min); + break; + } + if(min<128){ /* to 255 on newer systems */ + sprintf(buf, "/dev/ttyS%d", min-64); + break; + } + tmpmin = min & 0x3f; /* FALL THROUGH */ + case 3: /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */ + t0 = "pqrstuvwxyzabcde"[tmpmin>>4]; + t1 = "0123456789abcdef"[tmpmin&0x0f]; + sprintf(buf, "/dev/tty%c%c", t0, t1); + 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 */ + default: return 0; + } + if(stat(buf, &sbuf) < 0) return 0; + if(min != minor(sbuf.st_rdev)) return 0; + if(maj != major(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 * const buf, int maj, int min, int pid, char *name){ + struct stat sbuf; + char path[32]; + int count; + sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */ + count = readlink(path,buf,PAGE_SIZE-1); + if(count == -1) return 0; + buf[count] = '\0'; + if(stat(buf, &sbuf) < 0) return 0; + if(min != minor(sbuf.st_rdev)) return 0; + if(maj != major(sbuf.st_rdev)) return 0; + return 1; +} + +/* number --> name */ +int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags) { + static char buf[PAGE_SIZE]; + char *tmp = buf; + int i = 0; + int c; + if((short)dev == (short)-1) goto fail; + if( link_name(tmp, major(dev), minor(dev), pid, "tty" )) goto abbrev; + if( link_name(tmp, major(dev), minor(dev), pid, "fd/2" )) goto abbrev; + if( guess_name(tmp, major(dev), minor(dev) )) goto abbrev; + if( link_name(tmp, major(dev), minor(dev), pid, "fd/255")) goto abbrev; + if(driver_name(tmp, major(dev), minor(dev) )) goto abbrev; +fail: + 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; + tmp[chop] = '\0'; + 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(char *name) { + struct stat sbuf; + static char buf[32]; + if(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 00000000..f7b60f1f --- /dev/null +++ b/proc/devname.h @@ -0,0 +1,7 @@ +#define ABBREV_DEV 1 /* remove /dev/ */ +#define ABBREV_TTY 2 /* remove tty */ +#define ABBREV_PTS 4 /* remove pts/ */ + +int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags); + +int tty_to_dev(char *name); diff --git a/proc/ksym.c b/proc/ksym.c new file mode 100644 index 00000000..8925bd5a --- /dev/null +++ b/proc/ksym.c @@ -0,0 +1,548 @@ +/* + * Copyright 1998 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/procps.h" +#include "proc/version.h" +#include "proc/sysinfo.h" /* smp_num_cpus */ + +#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 { + const char *name; + unsigned long addr; +} symb; + +static const symb fail = { "?", 0 }; +static const char *dash = "-"; + +/* These mostly rely on POSIX to make them zero. */ + +static const 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 int 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 void 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: + return; + case '0' ... '9': + case 'a' ... 'f': + len++; + continue; + case 'g' ... 'z': + case '_': + len=0; + continue; + } + } + if(len<8) break; + cp[-1] = '\0'; + } +} +#endif +static void 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'; + } +} + +/***********************************/ + +static const symb *search(unsigned long 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 *filename, char **bufp, unsigned *roomp) { + int fd = 0; + ssize_t done; + char *buf; + ssize_t total = 0; + unsigned room = *roomp; + buf = *bufp; + 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); + } + *bufp = buf; + *roomp = room; + close(fd); + return; +hell: + if(buf) free(buf); + *bufp = NULL; + *roomp = 0; /* this function will never work again */ + total = 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 = strtoul(endp, &endp, 16); + if(endp==saved || *endp != ' ') goto bad_parse; + endp++; + ksyms_index[ksyms_count].name = endp; + saved = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + 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 *filename, void (*message)(const char *, ...)) { + 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){ /* 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 = strtoul(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++; + sysmap_index[sysmap_count].name = endp; + vstart = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + ++endp; + chop_version(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 *format, ...) { + va_list arg; + + va_start (arg, format); + vfprintf (stderr, format, arg); + va_end (arg); +} + +/*********************************/ + +int open_psdb_message(const char *override, void (*message)(const char *, ...)) { + 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 utsname uts; + char path[64]; + const char **fmt = sysmap_paths; + const char *env; + read_and_parse(); +#ifdef SYSMAP_FILENAME /* debug feature */ + override = SYSMAP_FILENAME; +#endif + if(override){ /* ought to search some path */ + if(sysmap_mmap(override, message)) return 0; + return -1; /* ought to return "Namelist not found." */ + /* failure is better than ignoring the user & using bad data */ + } + /* Arrrgh, the old man page and code did not match. */ + if ((env = getenv("PS_SYSMAP")) && sysmap_mmap(env, message)) return 0; + if ((env = getenv("PS_SYSTEM_MAP")) && sysmap_mmap(env, message)) return 0; + uname(&uts); + do{ + snprintf(path, sizeof path, *fmt, uts.release); + 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 *override) { + return open_psdb_message(override, default_message); +} + +/***************************************/ + +#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */ + +/* return pointer to temporary static buffer with function name */ +const char * wchan(unsigned long address) { + const symb *mod_symb; + const symb *map_symb; + const symb *good_symb; + const char *ret; + unsigned hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */ + if(!address) return dash; + read_and_parse(); + + 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; + 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/output.c b/proc/output.c new file mode 100644 index 00000000..66e4e407 --- /dev/null +++ b/proc/output.c @@ -0,0 +1,52 @@ +/* + Some output conversion routines for libproc + Copyright (C) 1996, Charles Blake. See COPYING for details. +*/ +#include +#include +#include + +/* output a string, converting unprintables to octal as we go, and stopping after + processing max chars of output (accounting for expansion due to octal rep). +*/ +unsigned print_str(FILE* file, char *s, unsigned max) { + int i; + for (i=0; s[i] && i < max; i++) + if (isprint(s[i]) || s[i] == ' ') + fputc(s[i], file); + else { + if (max - i > 3) { + fprintf(file, "\\%03o", s[i]); + i += 3; /* 4 printed, but i counts one */ + } else + return max - i; + } + return max - i; +} + +/* output an argv style NULL-terminated string list, converting unprintables + to octal as we go, separating items of the list by 'sep' and stopping after + processing max chars of output (accounting for expansion due to octal rep). +*/ +unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max) { + int i, n, seplen = strlen(sep); + for (n=0; *strs && n < max; strs++) { + for (i=0; strs[0][i] && n+i < max; i++) + if (isprint(strs[0][i]) || strs[0][i] == ' ') + fputc(strs[0][i], file); + else { + if (max-(n+i) > 3) { + fprintf(file, "\\%03o", strs[0][i]); + n += 3; /* 4 printed, but i counts one */ + } else + return max - n; + } + n += i; + if (n + seplen < max) { + fputs(sep, file); + n += seplen; + } else + return max - n; + } + return max - n; +} diff --git a/proc/procps.h b/proc/procps.h new file mode 100644 index 00000000..44497503 --- /dev/null +++ b/proc/procps.h @@ -0,0 +1,27 @@ +/* The shadow of the original with only common prototypes now. */ +#include +#include + +/* The HZ constant from is replaced by the Hertz variable + * available from "proc/sysinfo.h". + */ + +/* get page info */ +#include + +void *xrealloc(void *oldp, unsigned int size); +void *xmalloc(unsigned int size); +void *xcalloc(void *pointer, int size); + +int mult_lvl_cmp(void* a, void* b); +int node_mult_lvl_cmp(void* a, void* b); + +char *user_from_uid(uid_t uid); +char *group_from_gid(gid_t gid); + +const char * wchan(unsigned long address); +int open_psdb(const char *override); +int open_psdb_message(const char *override, void (*message)(const char *, ...)); + +unsigned print_str (FILE* file, char *s, unsigned max); +unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max); diff --git a/proc/pwcache.c b/proc/pwcache.c new file mode 100644 index 00000000..8fff60b3 --- /dev/null +++ b/proc/pwcache.c @@ -0,0 +1,74 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* 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 "proc/procps.h" +#include + +#define HASHSIZE 16 /* power of 2 */ +#define HASH(x) ((x) & (HASHSIZE - 1)) + +#define NAMESIZE 16 +#define NAMELENGTH "15" + +static struct pwbuf { + uid_t uid; + char name[NAMESIZE]; + struct pwbuf *next; +} *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; + if ((pw = getpwuid(uid)) == NULL) + sprintf((*p)->name, "#%d", uid); + else + sprintf((*p)->name, "%-." NAMELENGTH "s", pw->pw_name); + (*p)->next = NULL; + return((*p)->name); +} + +static struct grpbuf { + gid_t gid; + char name[NAMESIZE]; + struct grpbuf *next; +} *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; + if ((gr = getgrgid(gid)) == NULL) + sprintf((*g)->name, "#%d", gid); + else + sprintf((*g)->name, "%-." NAMELENGTH "s", gr->gr_name); + (*g)->next = NULL; + return((*g)->name); +} diff --git a/proc/readproc.c b/proc/readproc.c new file mode 100644 index 00000000..21a3996e --- /dev/null +++ b/proc/readproc.c @@ -0,0 +1,543 @@ +/* + * New Interface to Process Table -- PROCTAB Stream (a la Directory streams) + * Copyright (C) 1996 Charles L. Blake. + * Copyright (C) 1998 Michael K. Johnson + * May be distributed under the conditions of the + * GNU Library General Public License; a copy is in COPYING + */ +#include "proc/version.h" +#include "proc/readproc.h" +#include "proc/devname.h" +#include "proc/procps.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define Do(x) (flags & PROC_ ## x) /* convenient shorthand */ + +/* initiate a process table scan + */ +PROCTAB* openproc(int flags, ...) { + va_list ap; + PROCTAB* PT = xmalloc(sizeof(PROCTAB)); + + if (Do(PID)) + PT->procfs = NULL; + else if (!(PT->procfs = opendir("/proc"))) + return NULL; + PT->flags = flags; + va_start(ap, flags); /* Init args list */ + if (Do(PID)) + PT->pids = va_arg(ap, pid_t*); + else if (Do(TTY)) + PT->ttys = va_arg(ap, dev_t*); + else if (Do(UID)) { + PT->uids = va_arg(ap, uid_t*); + PT->nuid = va_arg(ap, int); + } else if (Do(STAT)) + PT->stats = va_arg(ap, char*); + va_end(ap); /* Clean up args list */ + if (Do(ANYTTY) && Do(TTY)) + PT->flags = PT->flags & ~PROC_TTY; /* turn off TTY flag */ + return PT; +} + +/* terminate a process table scan + */ +void closeproc(PROCTAB* PT) { + if (PT){ + if (PT->procfs) closedir(PT->procfs); + 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); +} + + + +static void status2proc (char* S, proc_t* P, int fill) { + char* tmp; + if (fill == 1) { + memset(P->cmd, 0, sizeof P->cmd); + sscanf (S, "Name:\t%15c", P->cmd); + tmp = strchr(P->cmd,'\n'); + *tmp='\0'; + tmp = strstr (S,"State"); + sscanf (tmp, "State:\t%c", &P->state); + } + + tmp = strstr (S,"Pid:"); + if(tmp) sscanf (tmp, + "Pid:\t%d\n" + "PPid:\t%d\n", + &P->pid, + &P->ppid + ); + else fprintf(stderr, "Internal error!\n"); + + tmp = strstr (S,"Uid:"); + if(tmp) sscanf (tmp, + "Uid:\t%d\t%d\t%d\t%d", + &P->ruid, &P->euid, &P->suid, &P->fuid + ); + else fprintf(stderr, "Internal error!\n"); + + tmp = strstr (S,"Gid:"); + if(tmp) sscanf (tmp, + "Gid:\t%d\t%d\t%d\t%d", + &P->rgid, &P->egid, &P->sgid, &P->fgid + ); + else fprintf(stderr, "Internal error!\n"); + + tmp = strstr (S,"VmSize:"); + if(tmp) sscanf (tmp, + "VmSize: %lu kB\n" + "VmLck: %lu kB\n" + "VmRSS: %lu kB\n" + "VmData: %lu kB\n" + "VmStk: %lu kB\n" + "VmExe: %lu kB\n" + "VmLib: %lu kB\n", + &P->vm_size, &P->vm_lock, &P->vm_rss, &P->vm_data, + &P->vm_stack, &P->vm_exe, &P->vm_lib + ); + else /* looks like an annoying kernel thread */ + { + 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; + } + + tmp = strstr (S,"SigPnd:"); + if(tmp) sscanf (tmp, +#ifdef SIGNAL_STRING + "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s", + P->signal, P->blocked, P->sigignore, P->sigcatch +#else + "SigPnd: %Lx SigBlk: %Lx SigIgn: %Lx %*s %Lx", + &P->signal, &P->blocked, &P->sigignore, &P->sigcatch +#endif + ); + else fprintf(stderr, "Internal error!\n"); +} + + + +/* stat2proc() makes sure it can handle arbitrary executable file basenames + * for `cmd', i.e. those with embedded whitespace or embedded ')'s. + * Such names confuse %s (see scanf(3)), so the string is split and %39c + * is used instead. (except for embedded ')' "(%[^)]c)" would work. + */ +static void stat2proc(char* S, proc_t* P) { + int num; + char* tmp = strrchr(S, ')'); /* split into "PID (cmd" and "" */ + *tmp = '\0'; /* replace trailing ')' with NUL */ + /* fill in default values for older kernels */ + P->exit_signal = SIGCHLD; + P->processor = 0; + /* parse these two strings separately, skipping the leading "(". */ + memset(P->cmd, 0, sizeof P->cmd); /* clear even though *P xcalloc'd ?! */ + sscanf(S, "%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 " + "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ + "%lu %lu %lu " + "%d %d", + &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->timeout, &P->it_real_value, + &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, +/* -- Linux 2.0.35 ends here -- */ + &P->exit_signal, &P->processor /* 2.2.1 ends with "exit_signal" */ +/* -- Linux 2.2.8 and 2.3.47 end here -- */ + ); + + /* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + if (P->tty == 0) + P->tty = -1; /* the old notty val, update elsewhere bef. moving to 0 */ +} + +static void statm2proc(char* s, proc_t* 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(char *directory, char *what, char *ret, int cap) { + static char filename[80]; + int fd, num_read; + + sprintf(filename, "%s/%s", directory, what); + if ( (fd = open(filename, O_RDONLY, 0)) == -1 ) return -1; + if ( (num_read = read(fd, ret, cap - 1)) <= 0 ) num_read = -1; + else ret[num_read] = 0; + close(fd); + return num_read; +} + +static char** file2strvec(char* directory, 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); + if ( (fd = open(buf, O_RDONLY, 0) ) == -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 < 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; +} + + +/* 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; \ + } ) + +/* 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. + * Unfortunately, the reverse filtering option in which any PID *except* the + * ones listed is pursued. + */ +proc_t* readproc(PROCTAB* PT, proc_t* rbuf) { + static struct direct *ent; /* dirent handle */ + static struct stat sb; /* stat buffer */ + static char path[32], sbuf[512]; /* bufs for stat,statm */ + int allocated = 0, matched = 0; /* flags */ + proc_t *p = NULL; + + /* loop until a proc matching restrictions is found or no more processes */ + /* I know this could be a while loop -- this way is easier to indent ;-) */ +next_proc: /* get next PID for consideration */ + +/*printf("PT->flags is 0x%08x\n", PT->flags);*/ +#define flags (PT->flags) + + if (Do(PID)) { + if (!*PT->pids) /* set to next item in pids */ + return NULL; + sprintf(path, "/proc/%d", *(PT->pids)++); + matched = 1; + } else { /* get next numeric /proc ent */ + while ((ent = readdir(PT->procfs)) && + (*ent->d_name < '0' || *ent->d_name > '9')) + ; + if (!ent || !ent->d_name) + return NULL; + sprintf(path, "/proc/%s", ent->d_name); + } + if (stat(path, &sb) == -1) /* no such dirent (anymore) */ + goto next_proc; + if (Do(UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) + goto next_proc; /* not one of the requested uids */ + + if (!allocated) { /* assign mem for return buf */ + p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */ + allocated = 1; /* remember space is set up */ + } + p->euid = sb.st_uid; /* need a way to get real uid */ + + if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1) + goto next_proc; /* error reading /proc/#/stat */ + stat2proc(sbuf, p); /* parse /proc/#/stat */ + + if (!matched && Do(TTY) && !XinL(dev_t, p->tty, PT->ttys)) + goto next_proc; /* not one of the requested ttys */ + + if (!matched && Do(ANYTTY) && p->tty == -1) + goto next_proc; /* no controlling terminal */ + + if (!matched && Do(STAT) && !strchr(PT->stats,p->state)) + goto next_proc; /* not one of the requested states */ + + if (Do(FILLMEM)) { /* read, parse /proc/#/statm */ + if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 ) + statm2proc(sbuf, p); /* ignore statm errors here */ + } /* statm fields just zero */ + + if (Do(FILLSTATUS)) { /* read, parse /proc/#/status */ + if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){ + status2proc(sbuf, p, 0 /*FIXME*/); + } + } + + /* some number->text resolving which is time consuming */ + if (Do(FILLUSR)){ + strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser); + strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup); + if(Do(FILLSTATUS)) { + strncpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser); + strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup); + strncpy(p->suser, user_from_uid(p->suid), sizeof p->suser); + strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup); + strncpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser); + strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup); + } + } + + if (Do(FILLCMD)) /* read+parse /proc/#/cmdline */ + p->cmdline = file2strvec(path, "cmdline"); + if (Do(FILLENV)) /* read+parse /proc/#/environ */ + p->environ = file2strvec(path, "environ"); + + if (p->state == 'Z') /* fixup cmd for zombies */ + strncat(p->cmd," ", sizeof p->cmd); + + return p; +} +#undef flags + +/* ps_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. + * Unfortunately, the reverse filtering option in which any PID *except* the + * ones listed is pursued. + */ +proc_t* ps_readproc(PROCTAB* PT, proc_t* rbuf) { + static struct direct *ent; /* dirent handle */ + static struct stat sb; /* stat buffer */ + static char path[32], sbuf[512]; /* bufs for stat,statm */ + int allocated = 0 /* , matched = 0 */ ; /* flags */ + proc_t *p = NULL; + + /* loop until a proc matching restrictions is found or no more processes */ + /* I know this could be a while loop -- this way is easier to indent ;-) */ +next_proc: /* get next PID for consideration */ + +/*printf("PT->flags is 0x%08x\n", PT->flags);*/ +#define flags (PT->flags) + + while ((ent = readdir(PT->procfs)) && + (*ent->d_name < '0' || *ent->d_name > '9')) + ; + if (!ent || !ent->d_name) + return NULL; + sprintf(path, "/proc/%s", ent->d_name); + + if (stat(path, &sb) == -1) /* no such dirent (anymore) */ + goto next_proc; + + if (!allocated) { /* assign mem for return buf */ + p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */ + allocated = 1; /* remember space is set up */ + } + p->euid = sb.st_uid; /* need a way to get real uid */ + + if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1) + goto next_proc; /* error reading /proc/#/stat */ + stat2proc(sbuf, p); /* parse /proc/#/stat */ + +/* if (Do(FILLMEM)) {*/ /* read, parse /proc/#/statm */ + if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 ) + statm2proc(sbuf, p); /* ignore statm errors here */ +/* } */ /* statm fields just zero */ + + /* if (Do(FILLSTATUS)) { */ /* read, parse /proc/#/status */ + if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){ + status2proc(sbuf, p, 0 /*FIXME*/); + } +/* }*/ + + /* some number->text resolving which is time consuming */ + /* if (Do(FILLUSR)){ */ + strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser); + strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup); +/* if(Do(FILLSTATUS)) { */ + strncpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser); + strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup); + strncpy(p->suser, user_from_uid(p->suid), sizeof p->suser); + strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup); + strncpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser); + strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup); +/* }*/ +/* }*/ + +/* if (Do(FILLCMD)) */ /* read+parse /proc/#/cmdline */ + p->cmdline = file2strvec(path, "cmdline"); +/* if (Do(FILLENV)) */ /* read+parse /proc/#/environ */ + p->environ = file2strvec(path, "environ"); + + if (p->state == 'Z') /* fixup cmd for zombies */ + strncat(p->cmd," ", sizeof p->cmd); + + return p; +} +#undef flags + + +void look_up_our_self(proc_t *p) { + static char path[32], sbuf[512]; /* bufs for stat,statm */ + sprintf(path, "/proc/%d", getpid()); + file2str(path, "stat", sbuf, sizeof sbuf); + stat2proc(sbuf, p); /* parse /proc/#/stat */ + file2str(path, "statm", sbuf, sizeof sbuf); + statm2proc(sbuf, p); /* ignore statm errors here */ + file2str(path, "status", sbuf, sizeof sbuf); + status2proc(sbuf, p, 0 /*FIXME*/); +} + + +/* Convenient wrapper around openproc and readproc to slurp in the whole process + * tree subset satisfying the constraints of flags and the optional PID list. + * Free allocated memory with freeproctree(). The tree structure is a classic + * left-list children + right-list siblings. The algorithm is a two-pass of the + * process table. Since most process trees will have children with strictly + * increasing PIDs, most of the structure will be picked up in the first pass. + * The second loop then cleans up any nodes which turn out to have preceeded + * their parent in /proc order. + */ + +/* Traverse tree 't' breadth-first looking for a process with pid p */ +static proc_t* LookupPID(proc_t* t, pid_t p) { + proc_t* tmp = NULL; + if (!t) + return NULL; + if (t->pid == p) /* look here/terminate recursion */ + return t; + if ((tmp = LookupPID(t->l, p))) /* recurse over children */ + return tmp; + for (; t; t=t->r) /* recurse over siblings */ + if ((tmp = LookupPID(tmp, p))) + return tmp; + return NULL; +} + +/* 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 freeproctab(). 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 (Do(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 (Do(PID) || Do(TTY) || Do(STAT)) + 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(PT, NULL); /* final null to terminate */ + } while (tab[n++]); /* stop when NULL reached */ + closeproc(PT); + return tab; +} diff --git a/proc/readproc.h b/proc/readproc.h new file mode 100644 index 00000000..81195944 --- /dev/null +++ b/proc/readproc.h @@ -0,0 +1,200 @@ +#ifndef PROCPS_PROC_READPROC_H +#define PROCPS_PROC_READPROC_H +/* + * New Interface to Process Table -- PROCTAB Stream (a la Directory streams) + * Copyright (C) 1996 Charles L. Blake. + * Copyright (C) 1998 Michael K. Johnson + * May be distributed under the terms of the + * GNU Library General Public License, a copy of which is provided + * in the file COPYING + */ + + +#define SIGNAL_STRING + + +/* + ld cutime, cstime, priority, nice, timeout, it_real_value, 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, nswap, cnswap, +*/ + +/* 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_s { +#ifdef SIGNAL_STRING + char + /* Linux 2.1.7x and up have more signals. This handles 88. */ + signal[24], /* mask of pending signals */ + blocked[24], /* mask of blocked signals */ + sigignore[24], /* mask of ignored signals */ + sigcatch[24]; /* mask of caught signals */ +#else + long long + /* Linux 2.1.7x and up have more signals. This handles 64. */ + signal, /* mask of pending signals */ + blocked, /* mask of blocked signals */ + sigignore, /* mask of ignored signals */ + sigcatch; /* mask of caught signals */ +#endif + long + cutime, /* cumulative utime of process and reaped children */ + cstime, /* cumulative stime of process and reaped children */ + priority, /* kernel scheduling priority */ + timeout, /* ? */ + nice, /* standard unix nice level of process */ + rss, /* resident set size from /proc/#/stat (pages) */ + it_real_value, /* ? */ + /* the next 7 members come from /proc/#/statm */ + size, /* total # of pages of memory */ + resident, /* number of resident set (non-swapped) pages (4k) */ + share, /* number of pages of shared (mmap'd) memory */ + trs, /* text resident set size */ + lrs, /* shared-lib resident set size */ + drs, /* data resident set size */ + dt; /* dirty pages */ + unsigned long + /* FIXME: are these longs? Maybe when the alpha does PCI bounce buffers */ + vm_size, /* same as vsize in kb */ + vm_lock, /* locked pages in kb */ + vm_rss, /* same as rss in kb */ + vm_data, /* data size */ + vm_stack, /* stack size */ + vm_exe, /* executable size */ + vm_lib, /* library size (all pages, not just used ones) */ + vsize, /* number of pages of virtual memory ... */ + rss_rlim, /* resident set size limit? */ + flags, /* kernel flags for the process */ + min_flt, /* number of minor page faults since process start */ + maj_flt, /* number of major page faults since process start */ + cmin_flt, /* cumulative min_flt of process and child processes */ + cmaj_flt, /* cumulative maj_flt of process and child processes */ + nswap, /* ? */ + cnswap, /* cumulative nswap ? */ + utime, /* user-mode CPU time accumulated by process */ + stime, /* kernel-mode CPU time accumulated by process */ + start_code, /* address of beginning of code segment */ + end_code, /* address of end of code segment */ + start_stack, /* address of the bottom of stack for the process */ + kstk_esp, /* kernel stack pointer */ + kstk_eip, /* kernel instruction pointer */ + start_time, /* start time of process -- seconds since 1-1-70 */ + wchan; /* address of kernel wait channel proc is sleeping in */ + struct proc_s *l, /* ptrs for building arbitrary linked structs */ + *r; /* (i.e. singly/doubly-linked lists and trees */ + char + **environ, /* environment string vector (/proc/#/environ) */ + **cmdline; /* command line string vector (/proc/#/cmdline) */ + char + /* Be compatible: Digital allows 16 and NT allows 14 ??? */ + ruser[16], /* real user name */ + euser[16], /* effective user name */ + suser[16], /* saved user name */ + fuser[16], /* filesystem user name */ + rgroup[16], /* real group name */ + egroup[16], /* effective group name */ + sgroup[16], /* saved group name */ + fgroup[16], /* filesystem group name */ + cmd[16]; /* basename of executable file in call to exec(2) */ + int + ruid, rgid, /* real */ + euid, egid, /* effective */ + suid, sgid, /* saved */ + fuid, fgid, /* fs (used for file access only) */ + pid, /* process id */ + ppid, /* pid of parent process */ + pgrp, /* process group id */ + session, /* session id */ + tty, /* full device number of controlling terminal */ + tpgid, /* terminal process group id */ + exit_signal, /* might not be SIGCHLD */ + processor; /* current (or most recent?) CPU */ + unsigned + pcpu; /* %CPU usage (is not filled in by readproc!!!) */ + char + state; /* single-char code for process state (S=sleeping) */ +} 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 +typedef struct { + DIR* procfs; + int flags; + pid_t* pids; /* pids of the procs */ + dev_t* ttys; /* devnos of the cttys */ + uid_t* uids; /* uids of procs */ + int nuid; /* cannot really sentinel-terminate unsigned short[] */ + char* stats; /* status chars (actually output into /proc//stat) */ +} PROCTAB; + +/* initialize a PROCTAB structure holding needed call-to-call persistent data + */ +PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ ); + + +/* 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 freeproctab(). Access via tab[N]->member. The + * pointer list is NULL terminated. + */ +proc_t** readproctab(int flags, ... /* same as openproc */ ); + +/* clean-up open files, etc from the openproc() + */ +void closeproc(PROCTAB* PT); + +/* retrieve the next process matching the criteria set by the openproc() + */ +proc_t* readproc(PROCTAB* PT, proc_t* return_buf); +proc_t* ps_readproc(PROCTAB* PT, proc_t* return_buf); + +void look_up_our_self(proc_t *p); + +/* deallocate space allocated by readproc + */ +void freeproc(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, and tty device numbers. The third + * argument is the length of the list (currently only used for lists of user + * id's since unsigned short[] supports no convenient termination sentinel.) + */ +#define PROC_FILLANY 0x00 /* either stat or status will do */ +#define PROC_FILLMEM 0x01 /* read statm into the appropriate proc_t entries */ +#define PROC_FILLCMD 0x02 /* alloc and fill in `cmdline' part of proc_t */ +#define PROC_FILLENV 0x04 /* alloc and fill in `environ' part of proc_t */ +#define PROC_FILLUSR 0x08 /* resolve user id number -> user name via passwd */ +#define PROC_FILLSTATUS 0x10 +#define PROC_FILLSTAT 0x20 +#define PROC_FILLBUG 0x3f /* No idea what we need */ + + +/* Obsolete, consider only processes with one of the passed: */ +#define PROC_PID 0x0100 /* process id numbers ( 0 terminated) */ +#define PROC_TTY 0x0200 /* ctty device nos. ( 0 terminated) */ +#define PROC_UID 0x0400 /* user id numbers ( length needed ) */ +#define PROC_STAT 0x0800 /* status fields ('\0' terminated) */ +#define PROC_ANYTTY 0x1000 /* proc must have a controlling terminal */ + +#endif diff --git a/proc/sig.c b/proc/sig.c new file mode 100644 index 00000000..46dd8470 --- /dev/null +++ b/proc/sig.c @@ -0,0 +1,234 @@ +/* + * Copyright 1998 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 + +/* 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 { + char *name; + int num; +} mapstruct; + + +static 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( ((mapstruct*)a)->name, ((mapstruct*)b)->name ); +} + +/* return -1 on failure */ +int signal_name_to_number(char *name){ + const mapstruct *ptr; + mapstruct ms; + long val; + char *endp; + 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 */ + ms.name = name; + 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 */ + val = strtol(name,&endp,10); + if(*endp) return -1; /* not valid */ + if(val+SIGRTMIN>127) return -1; /* not valid */ + return val+offset; +} + +static 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, char *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]; + char *txt; /* convenient alias */ + 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 00000000..3f232c57 --- /dev/null +++ b/proc/sig.h @@ -0,0 +1,19 @@ +/* + * Copyright 1998 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. + */ + +/* return -1 on failure */ +extern int signal_name_to_number(char *name); + +extern int print_given_signals(int argc, char *argv[], int max_line); + +extern void pretty_print_signals(void); + +extern void unix_print_signals(void); diff --git a/proc/status.c b/proc/status.c new file mode 100644 index 00000000..712743aa --- /dev/null +++ b/proc/status.c @@ -0,0 +1,29 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* 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 "proc/procps.h" +#include "proc/readproc.h" + +char * status(proc_t* task) { + static char buf[4] = " "; + + buf[0] = task->state; + if (task->rss == 0 && task->state != 'Z') + buf[1] = 'W'; + else + buf[1] = ' '; + if (task->nice < 0) + buf[2] = '<'; + else if (task->nice > 0) + buf[2] = 'N'; + else + buf[2] = ' '; + + return(buf); +} diff --git a/proc/status.h b/proc/status.h new file mode 100644 index 00000000..562efae4 --- /dev/null +++ b/proc/status.h @@ -0,0 +1,4 @@ +#ifndef __PROC_STATUS_H +#define __PROC_STATUS_H +extern char *status(proc_t* task); +#endif diff --git a/proc/sysinfo.c b/proc/sysinfo.c new file mode 100644 index 00000000..fbb14daf --- /dev/null +++ b/proc/sysinfo.c @@ -0,0 +1,325 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* 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. */ + +#include +#include +#include +#include + +#include +#include +#include "proc/version.h" +#include "proc/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, 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; + +static char buf[1024]; + +/* 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) { \ + fprintf(stderr, BAD_OPEN_MESSAGE); \ + 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 *uptime_secs, double *idle_secs) { + double up=0, idle=0; + + FILE_TO_BUF(UPTIME_FILE,uptime_fd); + if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { + fprintf(stderr, "bad data in " UPTIME_FILE "\n"); + return 0; + } + 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. + * + */ +unsigned long Hertz; +static void init_Hertz_value(void) __attribute__((constructor)); +static void init_Hertz_value(void){ + unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */ + double up_1, up_2, seconds; + unsigned long jiffies, h; + smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF); + if(smp_num_cpus==-1) smp_num_cpus=1; + 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)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */ + jiffies = user_j + nice_j + sys_j + other_j; + seconds = (up_1 + up_2) / 2; + h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus ); + /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ + switch(h){ + case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */ + case 48 ... 52 : Hertz = 50; break; + case 58 ... 62 : Hertz = 60; break; + case 63 ... 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 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)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! (%ld) Assume %ld.\n", h, Hertz); + } +} + +/*********************************************************************** + * The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we + * recover jiffies by adding up the 4 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 +void four_cpu_numbers(double *uret, double *nret, double *sret, double *iret){ + double tmp_u, tmp_n, tmp_s, tmp_i; + double scale; /* scale values to % */ + static JT old_u, old_n, old_s, old_i; + JT new_u, new_n, new_s, new_i; + JT ticks_past; /* avoid div-by-0 by not calling too often :-( */ + + FILE_TO_BUF(STAT_FILE,stat_fd); + sscanf(buf, "cpu %lu %lu %lu %lu", &new_u, &new_n, &new_s, &new_i); + ticks_past = (new_u+new_n+new_s+new_i)-(old_u+old_n+old_s+old_i); + 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; + }else{ + tmp_u = NAN; + tmp_n = NAN; + tmp_s = NAN; + tmp_i = 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); + old_u=new_u; + old_n=new_n; + old_s=new_s; + old_i=new_i; +} +#undef JT + +/***********************************************************************/ +void loadavg(double *av1, double *av5, double *av15) { + double avg_1=0, avg_5=0, avg_15=0; + + FILE_TO_BUF(LOADAVG_FILE,loadavg_fd); + if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) { + fprintf(stderr, "bad data in " LOADAVG_FILE "\n"); + exit(1); + } + SET_IF_DESIRED(av1, avg_1); + SET_IF_DESIRED(av5, avg_5); + SET_IF_DESIRED(av15, avg_15); +} + +/***********************************************************************/ +/* + * 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 */ + const unsigned *slot; /* slot in return struct */ +} mem_table_struct; + +static int compare_mem_table_structs(const void *a, const void *b){ + return strcmp(((mem_table_struct*)a)->name,((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 + * HighTotal: 0 kB + * HighFree: 0 kB + * LowTotal: 61768 kB + * LowFree: 1436 kB + * SwapTotal: 122580 kB old + * SwapFree: 60352 kB old + */ + +/* obsolete */ +unsigned kb_main_shared; +/* old but still kicking -- the important stuff */ +unsigned kb_main_buffers; +unsigned kb_main_cached; +unsigned kb_main_free; +unsigned kb_main_total; +unsigned kb_swap_free; +unsigned kb_swap_total; +/* recently introduced */ +unsigned kb_high_free; +unsigned kb_high_total; +unsigned kb_low_free; +unsigned kb_low_total; +/* 2.4.xx era */ +unsigned kb_active; +unsigned kb_inact_dirty; +unsigned kb_inact_clean; +unsigned kb_inact_target; +/* derived values */ +unsigned kb_swap_used; +unsigned kb_main_used; + +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}, + {"Buffers", &kb_main_buffers}, + {"Cached", &kb_main_cached}, + {"HighFree", &kb_high_free}, + {"HighTotal", &kb_high_total}, + {"Inact_clean", &kb_inact_clean}, + {"Inact_dirty", &kb_inact_dirty}, + {"Inact_target", &kb_inact_target}, + {"LowFree", &kb_low_free}, + {"LowTotal", &kb_low_total}, + {"MemFree", &kb_main_free}, + {"MemShared", &kb_main_shared}, + {"MemTotal", &kb_main_total}, + {"SwapFree", &kb_swap_free}, + {"SwapTotal", &kb_swap_total} + }; + const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct); + + FILE_TO_BUF(MEMINFO_FILE,meminfo_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, 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; + } + kb_swap_used = kb_swap_total - kb_swap_free; + kb_main_used = kb_main_total - kb_main_free; +} diff --git a/proc/sysinfo.h b/proc/sysinfo.h new file mode 100644 index 00000000..50064da1 --- /dev/null +++ b/proc/sysinfo.h @@ -0,0 +1,40 @@ +#ifndef SYSINFO_H +#define SYSINFO_H + +extern unsigned long Hertz; /* clock tick frequency */ +extern long smp_num_cpus; /* number of CPUs */ + +#define JT double +extern void four_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret); +#undef JT + +extern int uptime (double *uptime_secs, double *idle_secs); +extern void loadavg(double *av1, double *av5, double *av15); + + +/* obsolete */ +extern unsigned kb_main_shared; +/* old but still kicking -- the important stuff */ +extern unsigned kb_main_buffers; +extern unsigned kb_main_cached; +extern unsigned kb_main_free; +extern unsigned kb_main_total; +extern unsigned kb_swap_free; +extern unsigned kb_swap_total; +/* recently introduced */ +extern unsigned kb_high_free; +extern unsigned kb_high_total; +extern unsigned kb_low_free; +extern unsigned kb_low_total; +/* 2.4.xx era */ +extern unsigned kb_active; +extern unsigned kb_inact_dirty; +extern unsigned kb_inact_clean; +extern unsigned kb_inact_target; +/* derived values */ +extern unsigned kb_swap_used; +extern unsigned kb_main_used; + +extern void meminfo(void); + +#endif /* SYSINFO_H */ diff --git a/proc/tree.h b/proc/tree.h new file mode 100644 index 00000000..f834fa28 --- /dev/null +++ b/proc/tree.h @@ -0,0 +1,15 @@ +/* for oldps.c and proc/compare.c */ +struct tree_node { + proc_t *proc; + pid_t pid; + pid_t ppid; + char *line; + char *cmd; + char **cmdline; + char **environ; + int children; + int maxchildren; + int *child; + int have_parent; +}; + diff --git a/proc/version.c b/proc/version.c new file mode 100644 index 00000000..aee0bc32 --- /dev/null +++ b/proc/version.c @@ -0,0 +1,43 @@ +/* Suite version information for procps utilities + * Copyright (c) 1995 Martin Schulze + * Ammended by cblake to only export the function symbol. + * Redistributable under the terms of the + * GNU Library General Public License; see COPYING + */ +#include +#include + +#ifdef MINORVERSION +char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION; +#else +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 = 0; + +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 (linux_version_code) return; + 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 00000000..7b58cdbc --- /dev/null +++ b/proc/version.h @@ -0,0 +1,23 @@ +#ifndef PROC_VERSION_H +#define PROC_VERSION_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 + */ + +extern void display_version(void); /* display suite version */ +extern 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) + +#endif /* PROC_VERSION_H */ diff --git a/proc/whattime.c b/proc/whattime.c new file mode 100644 index 00000000..61e26013 --- /dev/null +++ b/proc/whattime.c @@ -0,0 +1,89 @@ +/* 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 "proc/whattime.h" +#include "proc/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, " %2d:%02d%s ", + realtime->tm_hour%12 ? realtime->tm_hour%12 : 12, + realtime->tm_min, realtime->tm_hour > 11 ? "pm" : "am"); + +/* 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 00000000..3d37bfa2 --- /dev/null +++ b/proc/whattime.h @@ -0,0 +1,9 @@ +/* whattime.h --- see whattime.c for explanation */ + +#ifndef __WHATTIME_H +#define __WHATTIME_H + +extern void print_uptime(void); +extern char *sprint_uptime(void); + +#endif diff --git a/procps.lsm b/procps.lsm new file mode 100644 index 00000000..4e76e738 --- /dev/null +++ b/procps.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: procps +Version: 010114 +Entered-date: 14JAN01 +Description: Procps is a library which parses the textual /proc filesystem + and a suite of utilites which use the library. +Keywords: procps /proc libproc + ps uptime tload free w top vmstat watch skill snice kill pgrep pkill +Author: Michael K. Johnson, Charles Blake, Albert Cahalan, many others. +Maintained-by: various +Primary-site: http://www.cs.uml.edu/~acahalan/procps/ + 185kB procps-010114.tar.gz +Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html + 185kB procps-010114.tar.gz +Copying-policy: mixed +End diff --git a/procps.spec b/procps.spec new file mode 100644 index 00000000..a3b212d3 --- /dev/null +++ b/procps.spec @@ -0,0 +1,105 @@ +Summary: Utilities for monitoring your system and processes on your system. +Name: procps +%define major_version 2 +%define minor_version 0 +%define revision 7 +%define version %{major_version}.%{minor_version}.%{revision} +Version: %{version} +Release: 1 +Copyright: GPL +Group: Applications/System +Source: ftp://people.redhat.com/johnsonm/procps/procps-%{version}.tar.gz +BuildRoot: /var/tmp/procps-root + +%package X11 +Group: Applications/System +Summary: X based process monitoring utilities. + +%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. 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. +Sysctl is a simple program for managing system configuration entries. +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're 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. + +%description X11 +The procps-X11 package contains the XConsole shell script, a backwards +compatibility wrapper for the xconsole program. + +%prep +%setup + +%build +PATH=/usr/X11R6/bin:$PATH + +make CC="gcc $RPM_OPT_FLAGS" LDFLAGS=-s + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/bin $RPM_BUILD_ROOT/usr/bin $RPM_BUILD_ROOT/sbin +mkdir -p $RPM_BUILD_ROOT/usr/man/man1 $RPM_BUILD_ROOT/usr/man/man8 +mkdir -p $RPM_BUILD_ROOT/lib $RPM_BUILD_ROOT/usr/X11R6/bin + +make DESTDIR=$RPM_BUILD_ROOT OWNERGROUP= MANDIR=/usr/share/man install + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +# add libproc to the cache +/sbin/ldconfig +# remove obsolete files +rm -f /etc/psdevtab /etc/psdatabase + +%files +%defattr(0644,root,root,755) +%attr(0644,root,root) %config(missingok) /etc/X11/applnk/Utilities/top.desktop +%doc NEWS BUGS TODO +%attr(755,root,root) /lib/libproc.so.2.0.7 +%attr(555,root,root) /bin/ps +%attr(555,root,root) /sbin/sysctl +%attr(555,root,root) /usr/bin/oldps +%attr(555,root,root) /usr/bin/uptime +%attr(555,root,root) /usr/bin/tload +%attr(555,root,root) /usr/bin/free +%attr(555,root,root) /usr/bin/w +%attr(555,root,root) /usr/bin/top +%attr(555,root,root) /usr/bin/vmstat +%attr(555,root,root) /usr/bin/watch +%attr(555,root,root) /usr/bin/skill +%attr(555,root,root) /usr/bin/snice +%attr(555,root,root) /usr/bin/pgrep +%attr(555,root,root) /usr/bin/pkill + +%attr(0644,root,root) /usr/man/man1/free.1 +%attr(0644,root,root) /usr/man/man1/ps.1 +%attr(0644,root,root) /usr/man/man1/oldps.1 +%attr(0644,root,root) /usr/man/man1/sessreg.1 +%attr(0644,root,root) /usr/man/man1/skill.1 +%attr(0644,root,root) /usr/man/man1/snice.1 +%attr(0644,root,root) /usr/man/man1/tload.1 +%attr(0644,root,root) /usr/man/man1/top.1 +%attr(0644,root,root) /usr/man/man1/uptime.1 +%attr(0644,root,root) /usr/man/man1/w.1 +%attr(0644,root,root) /usr/man/man1/watch.1 +%attr(0644,root,root) /usr/man/man1/pgrep.1 +%attr(0644,root,root) /usr/man/man1/pkill.1 +%attr(0644,root,root) /usr/man/man8/vmstat.8 +%attr(0644,root,root) /usr/man/man8/sysctl.8 + +%files X11 +%attr(0755,root,root) /usr/X11R6/bin/XConsole diff --git a/ps/.cvsignore.patch b/ps/.cvsignore.patch new file mode 100644 index 00000000..7c386a25 --- /dev/null +++ b/ps/.cvsignore.patch @@ -0,0 +1,5 @@ +diff -Naur procps-2.0.6/ps/.cvsignore procps-2.0.7/ps/.cvsignore +--- procps-2.0.6/ps/.cvsignore Wed Dec 31 19:00:00 1969 ++++ procps-2.0.7/ps/.cvsignore Fri Jul 14 16:45:01 2000 +@@ -0,0 +1 @@ ++ps diff --git a/ps/COPYING b/ps/COPYING new file mode 100644 index 00000000..92b8903f --- /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 00000000..ffae92c5 --- /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/Makefile b/ps/Makefile new file mode 100755 index 00000000..ec04f2f3 --- /dev/null +++ b/ps/Makefile @@ -0,0 +1,35 @@ +all: ps + +ps: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o + $(CC) -o ps escape.o global.o help.o select.o sortformat.o output.o parser.o display.o -L../proc -lproc + +# This just adds the stacktrace code +debug: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o + $(CC) -o ps escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o -L../proc -lproc -lefence + +sortformat.o: sortformat.c common.h + +global.o: global.c common.h + +escape.o: escape.c + +help.o: help.c + +select.o: select.c common.h + +output.o: output.c common.h + +parser.o: parser.c common.h + +display.o: display.c common.h + +stacktrace.o: stacktrace.c + + +install: ps + install $(OWNERGROUP) --mode a=rx --strip ps $(BINDIR)/ps + install $(OWNERGROUP) --mode a=r ps.1 $(MAN1DIR)/ps.1 + -rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz + +clean: + rm -f *.o DEADJOE *~ core ps gmon.out diff --git a/ps/TRANSLATION b/ps/TRANSLATION new file mode 100644 index 00000000..788aa6fe --- /dev/null +++ b/ps/TRANSLATION @@ -0,0 +1,28 @@ +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 heirarchy) +c show true command name diff --git a/ps/common.h b/ps/common.h new file mode 100644 index 00000000..d2bf76f5 --- /dev/null +++ b/ps/common.h @@ -0,0 +1,324 @@ +/* + * Copyright 1998 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/readproc.h" +#include /* looks safe for glibc, we need PAGE_SIZE */ + +#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 + +/* 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 */ + +/* + * Must not overflow the output buffer: + * 32 pages for env+cmd + * 8 kB pages on the Alpha + * 5 chars for "\001 " + * plus some slack for other stuff + * That is about 1.3 MB on the Alpha + * + * This isn't good enough for setuid. If anyone cares, mmap() over the + * last page with something unwriteable. + */ + +/* maximum escape expansion is 6, for " */ +#define ESC_STRETCH 6 +/* output buffer size */ +#define OUTBUF_SIZE (32*PAGE_SIZE*ESC_STRETCH + 8*PAGE_SIZE) +/* spaces used to right-justify things */ +#define SPACE_AMOUNT (int)(PAGE_SIZE) + +/******************* PS DEFINE *******************/ + +/* personality control flags */ +#define PER_BROKEN_o 0x0001 +#define PER_BSD_h 0x0002 +#define PER_BSD_m 0x0004 +#define PER_CUMUL_MARKED 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_IRIX_l 0x0400 + +/* 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 */ + +/* 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; + uid_t uid; + gid_t gid; + dev_t tty; + char cmd[8]; /* 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; +} sort_node; + +typedef struct format_node { + struct format_node *next; + char *name; /* user can override default name */ + int (*pr)(void); /* print function */ +/* int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */ + int width; + int pad; + 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)(void); /* print function */ + int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */ + const int width; + const int pad; /* could be second width */ + 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; + + +/*********************** GENERAL GLOBALS *************************/ + +/* escape.c */ +extern int escape_strlist(char *dst, const char **src, size_t n); +extern int escape_str(char *dst, const char *src, size_t n); +extern int octal_escape_str(char *dst, const char *src, size_t n); +extern int simple_escape_str(char *dst, const char *src, size_t n); + +/********************* UNDECIDED GLOBALS **************/ + +/* + * fputs(3) should (as in "good behavior") return the number of + * characters written as it does on Digital Unix, AIX, Irix, and SunOS. + * I'll assume glibc 2.1 has this extremely useful feature. + * + * Note: code ported from other systems will keep breaking until + * the library is updated. You should patch the library itself if + * at all possible. (for example, distributers who build libc from + * source with automatic patching as part of the build process) + */ + +#if defined __GLIBC__ && ((__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) || __GLIBC__ > 2) +#warning Hopefully fputs(3) has been modernized... +#else +#define EMULATE_FPUTS +#define fputs something_to_avoid_libc_troubles +static inline int fputs(const char *s, FILE *fp){ + return fwrite(s,1,strlen(s),fp); + /* return fprintf(fp, "%s", s); */ +} +#endif + +/* output.c */ +extern void show_one_proc(proc_t* p); +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); + +/* global.c */ +extern void reset_global(void); + +/* global.c */ +extern int all_processes; +extern char *bsd_j_format; +extern char *bsd_l_format; +extern char *bsd_s_format; +extern char *bsd_u_format; +extern 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 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 char *sysv_f_format; +extern char *sysv_fl_format; +extern char *sysv_j_format; +extern char *sysv_l_format; +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 00000000..6824b41f --- /dev/null +++ b/ps/display.c @@ -0,0 +1,398 @@ +/* + * Copyright 1998 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 + +/* username lookups */ +#include +#include +#include + +/* major/minor number */ +#include + +#include /* catch signals */ + +#include "common.h" +#include "../proc/procps.h" +#include "../proc/version.h" +#include "../proc/readproc.h" +#include "../proc/sysinfo.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 caught by ps (%s).\n" + "Please send bug reports to \n", + 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_pid(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d,", data[n].pid); + } + printf("%d\n", data[0].pid); +} + +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_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 */ +} + +/***** fill in %CPU; not in libproc because of include_dead_children */ +static void fill_pcpu(proc_t *buf){ + unsigned long total_time; + unsigned long pcpu = 0; + unsigned long seconds; + + total_time = buf->utime + buf->stime; + if(include_dead_children) total_time += (buf->cutime + buf->cstime); + seconds = (seconds_since_boot - ((unsigned long)buf->start_time) / Hertz); + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + buf->pcpu = (pcpu > 999) ? 999 : pcpu; +} + +/***** just display */ +static void simple_spew(void){ + proc_t buf; + PROCTAB* ptp; + ptp = openproc(PROC_FILLBUG); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + memset(&buf, '#', sizeof(proc_t)); + /* use "ps_" prefix to catch library mismatch */ + while(ps_readproc(ptp,&buf)){ + fill_pcpu(&buf); + if(want_this_proc(&buf)) show_one_proc(&buf); + /* if(buf.cmdline) free(buf.cmdline); */ /* these crash */ + /* if(buf.environ) free(buf.environ); */ + memset(&buf, '#', sizeof(proc_t)); + } + 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->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->next = sort_list; + sort_list = tmp_list; +} + +/* we rely on the POSIX requirement for zeroed memory */ +static proc_t *processes[32*1024]; + +/***** 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 **)a, *(const proc_t **)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(int n){ + proc_t **p = processes; + while(n--){ + show_one_proc(*p); + /* if(p->cmdline) free(p->cmdline); */ /* this crashes */ + /* if(p->environ) free(p->environ); */ /* this crashes */ + /* 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]); /* first show self */ + /* if(p->cmdline) free(p->cmdline); */ /* this crashes */ + /* if(p->environ) free(p->environ); */ /* this crashes */ + /* memset(*p, '%', sizeof(proc_t)); */ /* debug */ + for(;;){ /* look for children */ + if(i >= n) return; /* no children */ + if(processes[i]->ppid == processes[self]->pid) 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]->pid; + 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'; +} + +/***** 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]->pid == 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 */ +} + +/***** sorted or forest */ +static void fancy_spew(void){ + proc_t *retbuf; + PROCTAB* ptp; + int n = 0; /* number of processes & index into array */ + ptp = openproc(PROC_FILLBUG); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + while((retbuf = ps_readproc(ptp,NULL))){ + fill_pcpu(retbuf); + if(want_this_proc(retbuf)) processes[n++] = retbuf; + else free(retbuf); + } + closeproc(ptp); + 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(n); +} + + +/***** no comment */ +int main(int argc, char *argv[]){ +#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 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 */ + check_headers(); + if (open_psdb(namelist_file)) wchan_is_number = 1; + if(forest_type || sort_list) fancy_spew(); /* sort or forest */ + else simple_spew(); /* no sort, no forest */ + show_one_proc((proc_t *)-1); /* no output yet? */ + return 0; +} diff --git a/ps/escape.c b/ps/escape.c new file mode 100644 index 00000000..0c613cc3 --- /dev/null +++ b/ps/escape.c @@ -0,0 +1,106 @@ +/* + * Copyright 1998 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 + +/* sanitize a string, without the nice BSD library function: */ +/* strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH) */ +int octal_escape_str(char *dst, const char *src, size_t n){ + unsigned char c; + char d; + size_t i; + const char *codes = + "Z------abtnvfr-------------e----" + " *******************************" /* better: do not print any space */ + "****************************\\***" + "*******************************-" + "--------------------------------" + "********************************" + "********************************" + "********************************"; + for(i=0; i n) goto leave; + i += 4; + *(dst++) = '\\'; + *(dst++) = "01234567"[c>>6]; + *(dst++) = "01234567"[(c>>3)&07]; + *(dst++) = "01234567"[c&07]; + break; + default: + if(i+2 > n) goto leave; + i += 2; + *(dst++) = '\\'; + *(dst++) = d; + break; + } + } +leave: + *(dst++) = '\0'; + return i; +} + +/* sanitize a string via one-way mangle */ +int simple_escape_str(char *dst, const char *src, size_t n){ + unsigned char c; + size_t i; + const char *codes = + "Z-------------------------------" + "********************************" + "********************************" + "*******************************-" + "--------------------------------" + "********************************" + "********************************" + "********************************"; + for(i=0; i 1) && (*(src+1))) dst[i++] = ' '; + src++; + } + return i; +} diff --git a/ps/global.c b/ps/global.c new file mode 100644 index 00000000..640978f0 --- /dev/null +++ b/ps/global.c @@ -0,0 +1,422 @@ +/* + * Copyright 1998 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 + +/*#undef __GLIBC_MINOR__ +#define __GLIBC_MINOR__ 1 */ +#include "common.h" +/*#undef __GLIBC_MINOR__ +#define __GLIBC_MINOR__ 0 */ + +#include +#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 char *saved_personality_text = "You found a bug!"; + +int all_processes = -1; +char *bsd_j_format = (char *)0xdeadbeef; +char *bsd_l_format = (char *)0xdeadbeef; +char *bsd_s_format = (char *)0xdeadbeef; +char *bsd_u_format = (char *)0xdeadbeef; +char *bsd_v_format = (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]; +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; +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 */ +char *sysv_f_format = (char *)0xdeadbeef; +char *sysv_fl_format = (char *)0xdeadbeef; +char *sysv_j_format = (char *)0xdeadbeef; +char *sysv_l_format = (char *)0xdeadbeef; +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. + */ +static void set_screen_size(void){ + struct winsize ws; + char *columns; /* Unix98 environment variable */ + char *lines; /* Unix98 environment variable */ + if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0){ + screen_cols = ws.ws_col; + screen_rows = ws.ws_row; + }else{ /* TODO: ought to do tgetnum("co") and tgetnum("li") now */ + screen_cols = 80; + screen_rows = 24; + } + 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(((personality_table_struct*)a)->name,((personality_table_struct*)b)->name); +} + +static const char *set_personality(void){ + 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}, + {"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_CUMUL_MARKED | PER_OLD_m; + prefer_bsd_defaults = 1; + sysv_f_format = "RD_f"; + /* sysv_fl_format = "RD_fl"; */ /* Debian 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: + 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: + case_posix: + case_sco: + case_solaris2: + case_sysv: + 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; + running_only = 0; + seconds_since_boot = uptime(0,0); + selection_list = NULL; + simple_select = 0; + sort_list = NULL; + unix_f_option = 0; + user_is_number = 0; + wchan_is_number = 0; +} + +/*********** spew variables ***********/ +void self_info(void){ +#ifndef EMULATE_FPUTS + int count; +#endif + 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: libc %d, internal version %d.%d\n\n", + __GNU_LIBRARY__, __GLIBC__, __GLIBC_MINOR__ + ); + +#ifdef EMULATE_FPUTS + fprintf(stderr, "libc assumed lame, using fprintf to emulate fputs.\n\n"); +#else + fprintf(stderr, "fputs(\""); + count = fputs("123456789", stderr); + fprintf(stderr, "\", stderr) gives %d, which is %s.\n", + count, count==9?"good":"BAD!\nAdjust ps/common.h or libc, then recompile" + ); + if(count!=9){ + fprintf(stderr, "(procps includes a libc patch called glibc.patch)\n"); + } + fprintf(stderr, "\n"); +#endif + + 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 + ); + +/* open_psdb(namelist_file); */ + fprintf(stderr, + "personality=0x%08x (from \"%s\")\n" + "EUID=%d TTY=%d,%d Hertz=%ld\n" +/* "namelist_file=\"%s\"\n" */ + , + personality, saved_personality_text, + cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz /* , + namelist_file?namelist_file:"" */ + ); +} diff --git a/ps/help.c b/ps/help.c new file mode 100644 index 00000000..8e6bc0d5 --- /dev/null +++ b/ps/help.c @@ -0,0 +1,49 @@ +/* + * Copyright 1998 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 leader OR by 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 all, even group leaders! -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\n" +"-j,j job control s signal --group --user --sid --rows\n" +"-O,O preloaded -o v virtual memory --cumulative --format --deselect\n" +"-l,l long u user-oriented --sort --tty --forest --version\n" +" X registers --heading --no-heading\n" +" ********* misc options *********\n" +"-V,V show version L list format codes f ASCII art forest\n" +"-m,m show threads S children in sum -y change -l format\n" +"-n,N set namelist file c true command name n numeric WCHAN,UID\n" +"-w,w wide output e show environment -H process heirarchy\n" +; + + + +/* Missing: + * + * -c -L -P -M --info + * + */ diff --git a/ps/it b/ps/it new file mode 100644 index 00000000..07fd6dce --- /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/output.c b/ps/output.c new file mode 100644 index 00000000..152ad641 --- /dev/null +++ b/ps/output.c @@ -0,0 +1,1608 @@ +/* + * Copyright 1999 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. + */ + +/* + * 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. + */ + + +/* proc_t offset macro */ +#define PO(q) ((unsigned long)(&(((proc_t*)0)->q))) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "../proc/status.h" +#include "../proc/procps.h" +#include "../proc/devname.h" +#include "common.h" + + +/* TODO: + * Stop assuming system time is local time. + */ + +#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */ + +static char whitespace_and_outbuf[OUTBUF_SIZE + SPACE_AMOUNT + PAGE_SIZE*2]; +static char *outbuf = whitespace_and_outbuf+SPACE_AMOUNT; +static char *whitespace = whitespace_and_outbuf; +static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */ +static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */ + +/* Justification control for flags field. */ +#define JUST_MASK 0x0f + /* AIXHACK 0 */ +#define USER 1 /* left if text, right if numeric */ +#define LEFT 2 +#define RIGHT 3 +#define UNLIMITED 4 +#define WCHAN 5 /* left if text, right if numeric */ +#define SIGNAL 6 /* right in 9, or 16 if screen_cols>107 */ + +#define CUMUL 16 /* mark cumulative (Summed) headers with 'C' */ + +static int wide_signals; /* true if we have room */ + +static proc_t *pp; /* the process being printed */ + +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; \ +} + +CMP_INT(cutime) +CMP_INT(cstime) +CMP_INT(priority) /* nice */ +CMP_INT(timeout) +CMP_INT(nice) /* priority */ +CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */ +CMP_INT(it_real_value) +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_INT(flags) +CMP_INT(min_flt) +CMP_INT(maj_flt) +CMP_INT(cmin_flt) +CMP_INT(cmaj_flt) +CMP_INT(nswap) +CMP_INT(cnswap) +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_INT(pid) +CMP_INT(ppid) +CMP_INT(pgrp) +CMP_INT(session) +CMP_INT(tty) +CMP_INT(tpgid) + +CMP_INT(pcpu) + +CMP_INT(state) + +/***************************************************************************/ +/************ Lots of format functions, starting with the NOP **************/ + +static int pr_nop(void){ + 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(void){ + char *p = forest_prefix; + char *q = outbuf; + 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 */ + } + q += 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 */ + } + q += 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 + */ + +/* "command" is the same thing: long unless c */ +static int pr_args(void){ + char *endp; + endp = outbuf + forest_helper(); + if(bsd_c_option){ + endp += escape_str(endp, pp->cmd, PAGE_SIZE); /* short version */ + }else{ + const char **lc = (const char**)pp->cmdline; /* long version */ + if(lc && *lc) { + endp += escape_strlist(endp, lc, OUTBUF_SIZE); + } else { + char buf[ESC_STRETCH*PAGE_SIZE]; /* TODO: avoid copy */ + escape_str(buf, pp->cmd, ESC_STRETCH*PAGE_SIZE); + endp += snprintf(endp, COLWID, "[%s]", buf); + } + } + if(bsd_e_option){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + endp += escape_strlist(endp, env, OUTBUF_SIZE); + } + } + return endp - outbuf; +} + +/* "ucomm" is the same thing: short unless -f */ +static int pr_comm(void){ + char *endp; + endp = outbuf + forest_helper(); + if(!unix_f_option){ /* does -f matter? */ + endp += escape_str(endp, pp->cmd, PAGE_SIZE); /* short version */ + }else{ + const char **lc = (const char**)pp->cmdline; /* long version */ + if(lc && *lc) { + endp += escape_strlist(endp, lc, OUTBUF_SIZE); + } else { + char buf[ESC_STRETCH*PAGE_SIZE]; /* TODO: avoid copy */ + escape_str(buf, pp->cmd, ESC_STRETCH*PAGE_SIZE); + endp += snprintf(endp, COLWID, "[%s]", buf); + } + } + if(bsd_e_option){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + endp += escape_strlist(endp, env, OUTBUF_SIZE); + } + } + return endp - outbuf; +} +/* Non-standard, from SunOS 5 */ +static int pr_fname(void){ + char *endp; + endp = outbuf + forest_helper(); + endp += escape_str(endp, pp->cmd, 8); + return endp - outbuf; +} + +/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */ +static int pr_etime(void){ + unsigned t, dd,hh,mm,ss; + char *cp = outbuf; + t = ( + ((unsigned long)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); +} +static int pr_nice(void){ + return snprintf(outbuf, COLWID, "%ld", pp->nice); +} + +/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */ +static int pr_c(void){ + unsigned long total_time; /* jiffies used by this process */ + unsigned long pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned 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 - ((unsigned long)pp->start_time) / Hertz + ; + /* Use 100ULL (not 100) to avoid 32-bit overflow. */ + if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds; + if (pcpu > 99) pcpu = 99; + return snprintf(outbuf, COLWID, "%2u", (unsigned)pcpu); +} +/* normal %CPU in ##.# format. */ +static int pr_pcpu(void){ + unsigned long total_time; /* jiffies used by this process */ + unsigned long pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned 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 - ((unsigned long)pp->start_time) / Hertz + ; + /* Use 1000ULL (not 1000) to avoid 32-bit overflow. */ + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999) pcpu = 999; + return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pcpu/10), (unsigned)(pcpu%10)); +} +/* this is a "per-mill" format, like %cpu with no decimal point */ +static int pr_cp(void){ + unsigned long total_time; /* jiffies used by this process */ + unsigned long pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned 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 - ((unsigned long)pp->start_time) / Hertz + ; + /* Use 1000ULL (not 1000) to avoid 32-bit overflow. */ + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999) pcpu = 999; + return snprintf(outbuf, COLWID, "%3u", (unsigned)pcpu); +} + +static int pr_pgid(void){ + return snprintf(outbuf, COLWID, "%u", pp->pgrp); +} +static int pr_pid(void){ + return snprintf(outbuf, COLWID, "%u", pp->pid); +} +static int pr_ppid(void){ + return snprintf(outbuf, COLWID, "%u", pp->ppid); +} + + +/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */ +static int pr_time(void){ + unsigned t, dd,hh,mm,ss; + int c; + t = (unsigned)( + ( + (unsigned long)(pp->utime) + + + (unsigned long)(pp->stime) + ) + / + (unsigned long)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(void){ + return snprintf(outbuf, COLWID, "%lu", pp->vm_size); +} + +/* + * internal terms: ruid euid suid fuid + * kernel vars: uid euid suid fsuid + * command args: ruid uid svuid n/a + */ + +static int pr_ruser(void){ + if(user_is_number || (strlen(pp->ruser)>max_rightward)) + return snprintf(outbuf, COLWID, "%d", pp->ruid); + return snprintf(outbuf, COLWID, "%s", pp->ruser); +} +static int pr_egroup(void){ + if(strlen(pp->egroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->egid); + return snprintf(outbuf, COLWID, "%s", pp->egroup); +} +static int pr_rgroup(void){ + if(strlen(pp->rgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->rgid); + return snprintf(outbuf, COLWID, "%s", pp->rgroup); +} +static int pr_euser(void){ + if(user_is_number || (strlen(pp->euser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->euid); + return snprintf(outbuf, COLWID, "%s", pp->euser); +} + +/********* maybe standard (Unix98 only defines the header) **********/ + + +/* + * "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. + */ +static int pr_priority(void){ /* -20..20 */ + return snprintf(outbuf, COLWID, "%ld", pp->priority); +} +static int pr_pri(void){ /* 20..60 */ + return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority); +} +static int pr_opri(void){ /* 39..79 */ + return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority); +} + +static int pr_wchan(void){ +/* + * 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. + */ + if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "%s", "-"); + if(wchan_is_number) return snprintf(outbuf, COLWID, "%lx", pp->wchan & 0xffffff); + return snprintf(outbuf, COLWID, "%s", wchan(pp->wchan)); +} + +/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */ +/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */ +static int pr_tty4(void){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, 4, pp->tty, pp->pid, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS); +} + +/* Unix98: format is unspecified, but must match that used by who(1). */ +static int pr_tty8(void){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, PAGE_SIZE-1, pp->tty, pp->pid, ABBREV_DEV); +} + +#if 0 +/* This BSD state display may contain spaces, which is illegal. */ +static int pr_oldstate(void){ + 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(void){ + int end = 0; + outbuf[end++] = pp->state; + if(pp->rss == 0 && pp->state != 'Z') outbuf[end++] = 'W'; + if(pp->nice < 0) outbuf[end++] = '<'; + if(pp->nice > 0) outbuf[end++] = 'N'; + if(pp->vm_lock) outbuf[end++] = 'L'; + outbuf[end] = '\0'; + return end; +} + +/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */ +static int pr_s(void){ + outbuf[0] = pp->state; + outbuf[1] = '\0'; + return 1; +} + +static int pr_flag(void){ + /* Unix98 requires octal -- good thing Linux hex looks octal! */ + return snprintf(outbuf, COLWID, "%03lx", (pp->flags)&0x777); +} + +static int pr_euid(void){ + return snprintf(outbuf, COLWID, "%d", pp->euid); +} + +/*********** 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(void){ + return snprintf(outbuf, COLWID, "%08lx", pp->start_stack); +} + +static int pr_esp(void){ + return snprintf(outbuf, COLWID, "%08lx", pp->kstk_esp); +} + +static int pr_eip(void){ + return snprintf(outbuf, COLWID, "%08lx", pp->kstk_eip); +} + +/* This function helps print old-style time formats */ +static int old_time_helper(char *dst, unsigned long t, unsigned long rel) { + if(!t) return snprintf(dst, COLWID, " -"); + if((long)t == -1) return snprintf(dst, COLWID, " xx"); + if((long)(t-=rel) < 0) t=0; + if(t>9999) return snprintf(dst, COLWID, "%5lu", t/100); + else return snprintf(dst, COLWID, "%2lu.%02lu", t/100, t%100); +} + +static int pr_bsdtime(void){ + unsigned long t; + t = pp->utime + pp->stime; + if(include_dead_children) t += (pp->cutime + pp->cstime); + t /= Hertz; + return snprintf(outbuf, COLWID, "%3ld:%02d", t/60, (int)(t%60)); +} + +static int pr_bsdstart(void){ + 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_timeout(void){ + return old_time_helper(outbuf, pp->timeout, seconds_since_boot*Hertz); +} + +static int pr_alarm(void){ + return old_time_helper(outbuf, pp->it_real_value, 0); +} + +/* HP-UX puts this in pages and uses "vsz" for kB */ +static int pr_sz(void){ + 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(void){ + 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(void){ + 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(void){ + 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(void){ + long trs = 0; + if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", trs); +} + + + +static int pr_minflt(void){ + long flt = pp->min_flt; + if(include_dead_children) flt += pp->cmin_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_majflt(void){ + long flt = pp->maj_flt; + if(include_dead_children) flt += pp->cmaj_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_lim(void){ + if(pp->rss_rlim == RLIM_INFINITY) return snprintf(outbuf, COLWID, "%s", "xx"); + return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10); +} + +/* should print leading tilde ('~') if process is bound to the CPU */ +static int pr_psr(void){ + return snprintf(outbuf, COLWID, "%d", pp->processor); +} + +static int pr_wname(void){ +/* 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. + */ + if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "%s", "-"); + return snprintf(outbuf, COLWID, "%s", wchan(pp->wchan)); +} + +static int pr_nwchan(void){ + if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%lx", pp->wchan & 0xffffff); +} + +static int pr_rss(void){ + 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(void){ + 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(void){ + time_t t; + t = ( + ((unsigned long)time_of_boot) + + ((unsigned long)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(void){ + struct tm *proc_time; + struct tm *our_time; + time_t t; + 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_t)( + ((unsigned long)time_of_boot) + + ((unsigned long)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(void){ + time_t t; + char *str; + t = ( + ((unsigned long)time_of_boot) + + ((unsigned long)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(const char *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 + +static int pr_sig(void){ + return help_pr_sig(pp->signal); +} +static int pr_sigmask(void){ + return help_pr_sig(pp->blocked); +} +static int pr_sigignore(void){ + return help_pr_sig(pp->sigignore); +} +static int pr_sigcatch(void){ + return help_pr_sig(pp->sigcatch); +} + + +static int pr_egid(void){ + return snprintf(outbuf, COLWID, "%d", pp->egid); +} +static int pr_rgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->rgid); +} +static int pr_sgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->sgid); +} +static int pr_fgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->fgid); +} +static int pr_ruid(void){ + return snprintf(outbuf, COLWID, "%d", pp->ruid); +} +static int pr_suid(void){ + return snprintf(outbuf, COLWID, "%d", pp->suid); +} +static int pr_fuid(void){ + return snprintf(outbuf, COLWID, "%d", pp->fuid); +} + + +static int pr_fgroup(void){ + if(strlen(pp->fgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->fgid); + return snprintf(outbuf, COLWID, "%s", pp->fgroup); +} +static int pr_sgroup(void){ + if(strlen(pp->sgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->sgid); + return snprintf(outbuf, COLWID, "%s", pp->sgroup); +} +static int pr_fuser(void){ + if(user_is_number || (strlen(pp->fuser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->fuid); + return snprintf(outbuf, COLWID, "%s", pp->fuser); +} +static int pr_suser(void){ + if(user_is_number || (strlen(pp->suser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->suid); + return snprintf(outbuf, COLWID, "%s", pp->suser); +} + + +static int pr_thread(void){ /* TID tid LWP lwp SPID spid */ + return snprintf(outbuf, COLWID, "%u", pp->pid); /* for now... FIXME */ +} +static int pr_nlwp(void){ /* THCNT thcount NLWP nlwp */ + return snprintf(outbuf, COLWID, "-"); /* for now... FIXME */ +} + +static int pr_sess(void){ + return snprintf(outbuf, COLWID, "%u", pp->session); +} +static int pr_tpgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->tpgid); +} + + +/* SGI uses "cpu" to print the processor ID with header "P" */ +static int pr_sgi_p(void){ /* FIXME */ + if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor); + return snprintf(outbuf, COLWID, "*"); +} + + + +/***************************************************************************/ +/*************************** 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) + */ + +/* short names to save space */ +#define MEM PROC_FILLMEM /* read statm */ +#define CMD PROC_FILLCMD /* read cmdline */ +#define ENV PROC_FILLENV /* read environ */ +#define USR PROC_FILLUSR /* uid_t and gid_t -> user and group names */ +#define BUG PROC_FILLBUG /* what does this need? */ + +/* 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 195 listed */ + +/* Many of these are placeholders for unsupported options. */ +static const format_struct format_array[] = { +/* code header print() sort() width ? vendor flags */ +{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, RIGHT}, /*pcpu*/ +{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, RIGHT}, /*pmem*/ +{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, /*acflg*/ +{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*acflag*/ +{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, RIGHT}, +{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, LEFT}, +{"alarm", "ALARM", pr_alarm, sr_it_real_value, 5, 0, LNX, RIGHT}, +{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, RIGHT}, +{"args", "COMMAND", pr_args, sr_nop, 16, 0, U98, UNLIMITED}, /*command*/ +{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, CUMUL|RIGHT}, /*cputime*/ /* was 6 wide */ +{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, SIGNAL}, /*sigmask*/ +{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, RIGHT}, +{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, RIGHT}, +{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, RIGHT}, +{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, RIGHT}, +{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, SIGNAL}, /*sigcatch*/ +{"class", "CLS", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, +{"cls", "-", pr_nop, sr_nop, 1, 0, HPU, RIGHT}, +{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, RIGHT}, +{"cmd", "CMD", pr_args, sr_cmd, 16, 0, DEC, UNLIMITED}, /*ucomm*/ +{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, RIGHT}, +{"cnswap", "-", pr_nop, sr_cnswap, 1, 0, LNX, RIGHT}, +{"comm", "COMMAND", pr_comm, sr_nop, 16, 0, U98, UNLIMITED}, /*ucomm*/ +{"command", "COMMAND", pr_args, sr_nop, 16, 0, XXX, UNLIMITED}, /*args*/ +{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, RIGHT}, /*cpu*/ +{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */ +{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, RIGHT}, /*time*/ +{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, RIGHT}, +{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, RIGHT}, +{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, LEFT}, +{"drs", "DRS", pr_drs, sr_drs, 4, 0, LNX, RIGHT}, +{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, RIGHT}, +{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, RIGHT}, +{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, 0, LNX, USER}, +{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, RIGHT}, +{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, RIGHT}, +{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, 0, LNx, UNLIMITED}, +{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, RIGHT}, +{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, RIGHT}, /* was 7 wide */ +{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, RIGHT}, +{"euser", "EUSER", pr_euser, sr_euser, 8, 0, LNX, USER}, +{"f", "F", pr_flag, sr_nop, 3, 0, XXX, RIGHT}, /*flags*/ +{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, RIGHT}, +{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, 0, LNX, USER}, +{"flag", "F", pr_flag, sr_flags, 3, 0, DEC, RIGHT}, +{"flags", "F", pr_flag, sr_flags, 3, 0, BSD, RIGHT}, /*f*/ /* was FLAGS, 8 wide */ +{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, LEFT}, +{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, RIGHT}, +{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, 0, LNX, USER}, +{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, RIGHT}, +{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, 0, LNX, USER}, +{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, RIGHT}, +{"fuser", "FUSER", pr_fuser, sr_fuser, 8, 0, LNX, USER}, +{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, RIGHT}, +{"group", "GROUP", pr_egroup, sr_egroup, 5, 0, U98, USER}, /* was 8 wide */ +{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, SIGNAL}, /*sigignore*/ +{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*inblock*/ +{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, /*inblk*/ +{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, RIGHT}, +{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, RIGHT}, +{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, RIGHT}, +{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, RIGHT}, +{"label", "LABEL", pr_nop, sr_nop, 25, 0, SGI, LEFT}, +{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, RIGHT}, +{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, LEFT}, /*logname*/ /* double check */ +{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, LEFT}, /*login*/ +{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, LEFT}, +{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, RIGHT}, +{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, RIGHT}, /* login ID */ +{"luser", "LUSER", pr_nop, sr_nop, 8, 0, LNX, USER}, /* login USER */ +{"lwp", "LWP", pr_thread, sr_nop, 5, 0, SUN, RIGHT}, +{"m_drs", "DRS", pr_drs, sr_drs, 5, 0, LNx, RIGHT}, +{"m_dt", "DT", pr_nop, sr_dt, 4, 0, LNx, RIGHT}, +{"m_lrs", "LRS", pr_nop, sr_lrs, 5, 0, LNx, RIGHT}, +{"m_resident", "RES", pr_nop, sr_resident, 5, 0, LNx, RIGHT}, +{"m_share", "SHRD", pr_nop, sr_share, 5, 0, LNx, RIGHT}, +{"m_size", "SIZE", pr_nop, sr_size, 5, 0, LNx, RIGHT}, +{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, RIGHT}, +{"m_trs", "TRS", pr_trs, sr_trs, 5, 0, LNx, RIGHT}, +{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, CUMUL|RIGHT}, +{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, RIGHT}, +{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, CUMUL|RIGHT}, +{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, RIGHT}, +{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, RIGHT}, +{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, RIGHT}, +{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, RIGHT}, /*nice*/ +{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, RIGHT}, /*ni*/ +{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, +{"nlwp", "NLWP", pr_nlwp, sr_nop, 4, 0, SUN, RIGHT}, +{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, /*nsigs*/ +{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*nsignals*/ +{"nswap", "NSWAP", pr_nop, sr_nswap, 5, 0, XXX, RIGHT}, +{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, +{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, RIGHT}, +{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, RIGHT}, +{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, RIGHT}, +{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*oublock*/ +{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, /*oublk*/ +{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, RIGHT}, +{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, RIGHT}, +{"pagein", "PAGEIN", pr_majflt, sr_nop, 6, 0, XXX, RIGHT}, +{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, RIGHT}, /*%cpu*/ +{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, SIGNAL}, /*sig*/ +{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, RIGHT}, +{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, RIGHT}, +{"pid", "PID", pr_pid, sr_pid, 5, 0, U98, RIGHT}, +{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, RIGHT}, /*%mem*/ +{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, RIGHT}, +{"policy", "POL", pr_nop, sr_nop, 3, 0, DEC, RIGHT}, +{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, RIGHT}, +{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, RIGHT}, +{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, RIGHT}, /*ni,nice*/ /* from Linux sorting names */ +{"prmgrp", "-", pr_nop, sr_nop, 1, 0, HPU, RIGHT}, +{"prmid", "-", pr_nop, sr_nop, 1, 0, HPU, RIGHT}, +{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, RIGHT}, +{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, RIGHT}, +{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, RIGHT}, +{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, RIGHT}, +{"resident", "RES", pr_nop, sr_resident, 5, 0, LNX, RIGHT}, +{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, RIGHT}, +{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 6, 0, U98, USER}, /* was 8 wide */ +{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, RIGHT}, +{"rss", "RSS", pr_rss, sr_rss, 4, 0, XXX, RIGHT}, /* was 5 wide */ +{"rssize", "RSS", pr_rss, sr_vm_rss, 4, 0, DEC, RIGHT}, /*rsz*/ +{"rsz", "RSZ", pr_rss, sr_vm_rss, 4, 0, BSD, RIGHT}, /*rssize*/ +{"rtprio", "RTPRIO", pr_nop, sr_nop, 7, 0, BSD, RIGHT}, +{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, RIGHT}, +{"ruser", "RUSER", pr_ruser, sr_ruser, 8, 0, U98, USER}, +{"s", "S", pr_s, sr_state, 1, 0, SUN, LEFT}, /*stat,state*/ +{"sched", "SCH", pr_nop, sr_nop, 1, 0, AIX, RIGHT}, +{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, RIGHT}, /* man page misspelling of scount? */ +{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, RIGHT}, /* scnt==scount, DEC claims both */ +{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, RIGHT}, +{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, RIGHT}, +{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, RIGHT}, /* "cpu" number */ +{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, LEFT}, /* SZ:RSS */ +{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, RIGHT}, +{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, 0, LNX, USER}, +{"share", "-", pr_nop, sr_share, 1, 0, LNX, RIGHT}, +{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, RIGHT}, /* Sun & HP */ +{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, SIGNAL}, /*pending*/ +{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, SIGNAL}, +{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, SIGNAL}, +{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, SIGNAL}, +{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, SIGNAL}, +{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, SIGNAL}, /*caught*/ +{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, SIGNAL}, /*ignored*/ +{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, SIGNAL}, /*blocked*/ +{"size", "-", pr_nop, sr_size, 1, 0, SCO, RIGHT}, +{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, RIGHT}, +{"spid", "SPID", pr_thread, sr_nop, 5, 0, SGI, RIGHT}, +{"stackp", "STACKP", pr_stackp, sr_nop, 8, 0, LNX, RIGHT}, /*start_stack*/ +{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, RIGHT}, +{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, RIGHT}, +{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, RIGHT}, /*stackp*/ +{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, RIGHT}, +{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, LEFT}, /*state,s*/ +{"state", "S", pr_s, sr_state, 1, 0, XXX, LEFT}, /*stat,s*/ /* was STAT */ +{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, /* CUMUL| */RIGHT}, /* was 6 wide */ +{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, RIGHT}, +{"suser", "SUSER", pr_suser, sr_suser, 8, 0, LNx, USER}, +{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, RIGHT}, +{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, 0, LNX, USER}, +{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, RIGHT}, +{"svuser", "SVUSER", pr_suser, sr_suser, 8, 0, LNX, USER}, +{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, RIGHT}, +{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, RIGHT}, +{"thcount", "THCNT", pr_nlwp, sr_nop, 5, 0, AIX, RIGHT}, +{"tid", "TID", pr_thread, sr_nop, 5, 0, AIX, RIGHT}, +{"time", "TIME", pr_time, sr_nop, 8, 0, U98, CUMUL|RIGHT}, /*cputime*/ /* was 6 wide */ +{"timeout", "TMOUT", pr_timeout, sr_timeout, 5, 0, LNX, RIGHT}, +{"tmout", "TMOUT", pr_timeout, sr_timeout, 5, 0, LNX, RIGHT}, +{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, LEFT}, +{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, RIGHT}, +{"trs", "TRS", pr_trs, sr_trs, 4, 0, AIX, RIGHT}, +{"trss", "TRSS", pr_trs, sr_trs, 4, 0, BSD, RIGHT}, /* 4.3BSD NET/2 */ +{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, +{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, +{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, RIGHT}, +{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, LEFT}, +{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */ +{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, LEFT}, +{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, LEFT}, +{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"ucmd", "CMD", pr_comm, sr_cmd, 16, 0, DEC, UNLIMITED}, /*ucomm*/ +{"ucomm", "COMMAND", pr_comm, sr_nop, 16, 0, XXX, UNLIMITED}, /*comm*/ +{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, RIGHT}, +{"uid_hack", "UID", pr_euser, sr_nop, 8, 0, XXX, USER}, +{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, +{"uname", "USER", pr_euser, sr_euser, 8, 0, DEC, USER}, /* man page misspelling of user? */ +{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, RIGHT}, /*usrpri*/ +{"uprocp", "-", pr_nop, sr_nop, 1, 0, BSD, RIGHT}, +{"user", "USER", pr_euser, sr_euser, 8, 0, U98, USER}, /* BSD n forces this to UID */ +{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, RIGHT}, +{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, RIGHT}, /*upr*/ +{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, CUMUL|RIGHT}, +{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, RIGHT}, +{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, RIGHT}, +{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, RIGHT}, +{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, RIGHT}, +{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, RIGHT}, +{"vsize", "VSZ", pr_vsz, sr_vsize, 5, 0, DEC, RIGHT}, /*vsz*/ +{"vsz", "VSZ", pr_vsz, sr_vm_size, 5, 0, U98, RIGHT}, /*vsize*/ +{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, 0, XXX, WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */ +{"wname", "WCHAN", pr_wname, sr_nop, 6, 0, SGI, WCHAN}, /* opposite of nwchan */ +{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, +{"~", "-", pr_nop, sr_nop, 1, 0, LNX, RIGHT} /* NULL would ruin alphabetical order */ +}; + +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 */ + +{"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(((format_struct*)a)->spec,((format_struct*)b)->spec); +} + +static int compare_macro_structs(const void *a, const void *b){ + return strcmp(((macro_struct*)a)->spec,((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) & JUST_MASK){ + default: + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case SIGNAL: + sigs++; + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case 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); + +#if 0 + printf("123456789-123456789-123456789-123456789-" + "123456789-123456789-123456789-123456789\n"); + printf("need %d, using %d\n", total, active_cols); +#endif +} + + +/********** show one process (NULL proc prints header) **********/ +void show_one_proc(proc_t* p){ + /* 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 */ + format_node *fmt = format_list; + static int did_stuff = 0; /* have we ever printed anything? */ + + if(-1==(long)p){ /* true only once, at the end */ + check_header_width(); /* temporary test code */ + 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); + } + /* fprintf(stderr, "No processes available.\n"); */ /* legal? */ + exit(1); + } + if(p){ /* not header, maybe we should call ourselves for it */ + if(!--lines_to_next_header){ + lines_to_next_header = header_gap; + show_one_proc(NULL); + } + } + did_stuff = 1; + pp = p; /* global, the proc_t struct */ + if(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(fmt->next) max_rightward = fmt->width; + else max_rightward = active_cols-((correct>actual) ? correct : actual); + max_leftward = fmt->width + actual - correct; /* TODO check this */ + /* prepare data and calculate leftpad */ + if(p && fmt->pr) amount = (*fmt->pr)(); + else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */ + switch((fmt->flags) & JUST_MASK){ + case 0: /* for AIX, assigned outside this file */ + leftpad = 0; + break; + case LEFT: /* bad */ + leftpad = 0; + break; + case RIGHT: /* OK */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + case 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 USER: /* bad */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + if(!user_is_number) leftpad = 0; + break; + case WCHAN: /* bad */ + if(wchan_is_number){ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + }else{ + if(fmt->next){ + outbuf[fmt->width] = '\0'; /* Must chop, more columns! */ + }else{ + int chopspot; /* place to chop */ + int tmpspace; /* need "space" before it is calculated below */ + tmpspace = correct - actual; + if(tmpspace<1) tmpspace = dospace; + chopspot = active_cols-actual-tmpspace; + if(chopspot<1) chopspot=1; /* oops, we (mostly) lose this column... */ + outbuf[chopspot] = '\0'; /* chop at screen/buffer limit */ + } + leftpad = 0; + break; + } + case UNLIMITED: + if(fmt->next){ + outbuf[fmt->width] = '\0'; /* Must chop, more columns! */ + }else{ + int chopspot; /* place to chop */ + int tmpspace; /* need "space" before it is calculated below */ + tmpspace = correct - actual; + if(tmpspace<1) tmpspace = dospace; + chopspot = active_cols-actual-tmpspace; + if(chopspot<1) chopspot=1; /* oops, we (mostly) lose this column... */ + outbuf[chopspot] = '\0'; /* chop at screen/buffer limit */ + } + leftpad = 0; + break; + default: + fputs("bad alignment code\n", stderr); + 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(space>SPACE_AMOUNT) space=SPACE_AMOUNT; + + /* print data, set x position stuff */ + amount = strlen(outbuf); /* post-chop data width */ + if(!fmt->next){ + /* Last column. Write padding + data + newline all together. */ + outbuf[amount] = '\n'; + fwrite(outbuf-space, space+amount+1, 1, stdout); + break; + } + /* Not the last column. Write padding + data together. */ + fwrite(outbuf-space, space+amount, 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){ + memset(whitespace, ' ', PAGE_SIZE); +#if 0 + mprotect(whitespace, PAGE_SIZE, PROT_READ); /* FIXME may fail if unaligned */ + mprotect( + (void *)((unsigned long)(whitespace_and_outbuf-PAGE_SIZE) &~ (PAGE_SIZE-1)), + PAGE_SIZE, PROT_NONE + ); +#endif + seconds_since_1970 = time(NULL); + time_of_boot = seconds_since_1970 - seconds_since_boot; + meminfo(); + switch(getpagesize()){ + 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; + } + check_header_width(); +} diff --git a/ps/p b/ps/p new file mode 100755 index 00000000..880df4a6 --- /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_PRELOAD=../proc/libproc.so exec ./ps "$@" diff --git a/ps/parser.c b/ps/parser.c new file mode 100644 index 00000000..4ecf0e8b --- /dev/null +++ b/ps/parser.c @@ -0,0 +1,1123 @@ +/* + * Copyright 1998 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; + int num; + static const char *pidrange = "Process ID out of range."; + static const char *pidsyntax = "Process ID list syntax error."; + num = strtol(str, &endp, 0); + if(*endp != '\0') return pidsyntax; + if(num>0x7fff) return pidrange; /* Linux PID limit */ + if(num<1) 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, 8); /* 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 = -1; /* processes w/o tty */ + return 0; + } + if(!strcmp(str,"?")){ /* "?" means no tty, which bash eats (Reno BSD?) */ + ret->tty = -1; /* processes w/o tty */ + return 0; + } + if(!*(str+1) && (stat(str,&sbuf)>=0)){ /* Kludge! Assume bash ate '?'. */ + ret->tty = -1; /* 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. */ + if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; + 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 heirarchy (like ASCII art forest option)\n"); + forest_type = 'u'; + break; + 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"); + format_modifiers |= FM_L; + break; + case 'M': /* someday, maybe, we will have MAC like SGI's Irix */ + trace("-M Print security label for Mandatory Access Control.\n"); + format_modifiers |= FM_M; + return "Sorry, no Mandatory Access Control support."; + 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) or PSR column (Sun)\n"); + format_modifiers |= FM_P; + break; +#ifdef WE_UNDERSTAND_THIS + case 'R': /* unknown HP/UX feature */ + trace("-R selects PRM groups\n"); + return "Don't understand PRM on Linux."; + break; +#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"); + 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); + case 'Z': /* full Mandatory Access Control level info */ + trace("-Z shows full MAC info\n"); + return "Don't understand MAC on Linux."; + 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"); + /* Debian uses 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 */ + /* not implemented -- silently ignore the option */ + 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 */ +#ifdef KNOW_WHAT_TO_DO_WITH_THIS + 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; +#ifdef NOBODY_HAS_BSD_HABITS_ANYMORE + case '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_x; + break; +#endif + 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; + case 'z': /* alias of Mandatory Access Control level info */ + trace("-z shows aliased MAC info\n"); + return "Don't understand MAC on Linux."; + break; + 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 'L': /* single */ + trace("L List all format specifiers\n"); + exclusive("L"); + print_format_specifiers(); + exit(0); +#if 0 + case 'M': + trace("M junk (use alternate core)\n"); + return "Option M is unsupported, try N or -n instead."; + break; +#endif + 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 '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; +#if 0 + case 'k': + trace("k N/A Use /vmcore as c-dumpfile\n"); + return "Obsolete k option not supported."; + break; +#endif + 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; + } + /* not implemented -- silently ignore the option */ + 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(((gnu_table_struct*)a)->name,((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}, + {"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}, + {"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"); + fputs(help_message, stderr); + 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_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; +} + +/*************** 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 */ +} + +int arg_parse(int argc, char *argv[]){ + const char *err = NULL; + const char *err2 = NULL; + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + +#if 0 + {int debugloop = 0; while(debugloop are dead processes (so-called "zombies") that +remain because their parent has not destroyed them properly. These processes +will be destroyed by init(8) if the parent process exits. + + +PROCESS FLAGS + +ALIGNWARN 001 print alignment warning msgs +STARTING 002 being created +EXITING 004 getting shut down +PTRACED 010 set if ptrace (0) has been called +TRACESYS 020 tracing system calls +FORKNOEXEC 040 forked but didn't exec +SUPERPRIV 100 used super-user privileges +DUMPCORE 200 dumped core +SIGNALED 400 killed by a signal + + +PROCESS STATE CODES + +D uninterruptible sleep (usually IO) +R runnable (on run queue) +S sleeping +T traced or stopped +Z a defunct ("zombie") process + +For BSD formats and when the "stat" keyword is used, additional +letters may be displayed: + +W has no resident pages +< high-priority process +N low-priority task +L has pages locked into memory (for real-time and custom IO) + + +SORT KEYS + +Note that the values used in sorting are the internal values ps uses and not +the `cooked' values used in some of the output format fields. Pipe ps +output into the sort(1) command if you want to sort the cooked values. + +KEY LONG DESCRIPTION +c cmd simple name of executable +C cmdline full command line +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 +K stime system 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 minor device number of 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 + + +AIX FORMAT DESCRIPTORS + +This ps supports AIX format descriptors, which work somewhat like the +formatting codes of printf(1) and printf(3). For example, the normal +default output can be produced with this: ps -eo "%p %y %x %c" + +CODE NORMAL HEADER +%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 + + +STANDARD FORMAT SPECIFIERS + +These may be used to control both output format and sorting. +For example: ps -eo pid,user,args --sort user + +CODE HEADER +%cpu %CPU +%mem %MEM +alarm ALARM +args COMMAND +blocked BLOCKED +bsdstart START +bsdtime TIME +c C +caught CAUGHT +cmd CMD +comm COMMAND +command COMMAND +cputime TIME +drs DRS +dsiz DSIZ +egid EGID +egroup EGROUP +eip EIP +esp ESP +etime ELAPSED +euid EUID +euser EUSER +f F +fgid FGID +fgroup FGROUP +flag F +flags F +fname COMMAND +fsgid FSGID +fsgroup FSGROUP +fsuid FSUID +fsuser FSUSER +fuid FUID +fuser FUSER +gid GID +group GROUP +ignored IGNORED +intpri PRI +lim LIM +longtname TTY +lstart STARTED +m_drs DRS +m_trs TRS +maj_flt MAJFL +majflt MAJFLT +min_flt MINFL +minflt MINFLT +ni NI +nice NI +nwchan WCHAN +opri PRI +pagein PAGEIN +pcpu %CPU +pending PENDING +pgid PGID +pgrp PGRP +pid PID +pmem %MEM +ppid PPID +pri PRI +priority PRI +rgid RGID +rgroup RGROUP +rss RSS +rssize RSS +rsz RSZ +ruid RUID +ruser RUSER +s S +sess SESS +session SESS +sgi_p P +sgi_rss RSS +sgid SGID +sgroup SGROUP +sid SID +sig PENDING +sig_block BLOCKED +sig_catch CATCHED +sig_ignore IGNORED +sig_pend SIGNAL +sigcatch CAUGHT +sigignore IGNORED +sigmask BLOCKED +stackp STACKP +start STARTED +start_stack STACKP +start_time START +stat STAT +state S +stime STIME +suid SUID +suser SUSER +svgid SVGID +svgroup SVGROUP +svuid SVUID +svuser SVUSER +sz SZ +time TIME +timeout TMOUT +tmout TMOUT +tname TTY +tpgid TPGID +trs TRS +trss TRSS +tsiz TSIZ +tt TT +tty TT +tty4 TTY +tty8 TTY +ucmd CMD +ucomm COMMAND +uid UID +uid_hack UID +uname USER +user USER +vsize VSZ +vsz VSZ +wchan WCHAN + + + + +ENVIRONMENT VARIABLES +The following environment variables could affect ps: + COLUMNS Override default display width. + LINES Override default display height. + PS_PERSONALITY Set to one of posix,old,linux,bsd,sun,digital... + CMD_ENV Set to one of posix,old,linux,bsd,sun,digital... + I_WANT_A_BROKEN_PS Force obsolete command line interpretation. + LC_TIME Date format. + PS_COLORS Not currently supported. + PS_FORMAT Default output format override. + PS_SYSMAP Default namelist (System.map) location. + PS_SYSTEM_MAP Default namelist (System.map) location. + POSIXLY_CORRECT Don't find excuses to ignore bad "features". + UNIX95 Don't find excuses to ignore bad "features". + _XPG Cancel CMD_ENV=irix non-standard behavior. + +In general, it is a bad idea to set these variables. The one exception +is CMD_ENV or PS_PERSONALITY, which could be set to Linux for normal +systems. Without that setting, ps follows the useless and bad parts +of the Unix98 standard. + + +PERSONALITY + 390 like the S/390 OpenEdition ps + aix like AIX ps + bsd like FreeBSD ps (totally non-standard) + compaq like Digital Unix ps + debian like the old Debian ps + digital like Digital Unix ps + gnu like the old Debian ps + hp like HP-UX ps + hpux like HP-UX ps + irix like Irix ps + linux ***** RECOMMENDED ***** + old like the original Linux ps (totally non-standard) + posix standard + sco like SCO ps + sgi like Irix ps + sun like SunOS 4 ps (totally non-standard) + sunos like SunOS 4 ps (totally non-standard) + sysv standard + unix standard + unix95 standard + unix98 standard + + +EXAMPLES +To see every process on the system using standard syntax: + ps -e +To see every process on the system using BSD syntax: + ps ax +To see every process except those running as root (real & effective ID) + ps -U root -u root -N +To see every process with a user-defined format: + ps -eo pid,tt,user,fname,tmout,f,wchan +Odd display with AIX field descriptors: + ps -o "%u : %U : %p : %a" +Print only the process IDs of syslogd: + ps -C syslogd -o pid= + +SEE ALSO +top(1) pstree(1) proc(5) + +STANDARDS +This ps conforms to version 2 of the Single Unix Specification. + +AUTHOR +ps 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 diff --git a/ps/regression b/ps/regression new file mode 100644 index 00000000..c71c826a --- /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 00000000..e6bf8c7f --- /dev/null +++ b/ps/select.c @@ -0,0 +1,146 @@ +/* + * Copyright 1998 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 session_leader(p) ((p)->session == (p)->pid) +#define process_group_leader(p) ((p)->pgid == (p)->pid) +#define without_a_tty(p) ((unsigned short)((p)->tty) == (unsigned short)-1) +#define some_other_user(p) ((p)->euid != cached_euid) +#define running(p) (((p)->state=='R')||((p)->state=='D')) +#define has_our_euid(p) ((unsigned short)((p)->euid) == (unsigned short)cached_euid) +#define on_our_tty(p) ((unsigned short)((p)->tty) == (unsigned short)cached_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 short )(buf->foo) == (unsigned short)(*(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(pid,pid); + break; case SEL_TTY : return_if_match(tty,tty); + break; case SEL_SESS: return_if_match(session,pid); + + /* TODO Should use a long long cast for performance */ + break; case SEL_COMM: i=sn->n; while(i--) + if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 8 )) 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 00000000..9c08d9a1 --- /dev/null +++ b/ps/sortformat.c @@ -0,0 +1,875 @@ +/* + * Copyright 1998 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 "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; + + +#define parse_sort_opt <-- arrgh! do not use this --> +#define gnusort_parse <-- arrgh! do not use this --> + + +/**************** 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)); + 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->pad = fs->pad; + 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; + 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->pad = 0; + fnode->vendor = AIX; + fnode->flags = 0; + 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; + 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++; + } + 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; + } + 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(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++; + } + fs = search_format_array(spec); + if(fs){ + sort_node *thisnode; + thisnode = malloc(sizeof(format_node)); + thisnode->sr = fs->sr; + 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; + const char *err; /* error code that could or did happen */ + 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; + err = "Improper sort specifier list."; + 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->pad = 0; + fn->vendor = AIX; /* yes, for SGI weirdness */ + fn->flags = 0; + 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"); PUSH("opri"); + } + if((format_modifiers & FM_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(format_modifiers & FM_L) PUSH("lwp"); + if(format_modifiers & FM_j){ + PUSH("sid"); + PUSH("pgid"); + } + if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid"); + if(format_modifiers & FM_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 */ + } + format_modifiers = 0; + 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){ + const char *err; + sf_node *sf_walk; + int option_source; /* true if user-defined */ + if(personality & PER_BROKEN_o) localbroken = 1; + if(personality & PER_GOOD_o) localbroken = 0; + broken = localbroken; + if(sf_list){ + 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; + } + + if(format_list){ + if(format_flags) return "Conflicting format options."; + option_source = 1; + }else{ + format_node *fmt_walk; + format_node *fn; + 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; + + } /* end switch(format_flags) */ + + option_source = 0; + if(!format_flags && !format_modifiers){ /* was default */ + char *tmp; + tmp = getenv("PS_FORMAT"); /* user override kills default */ + if(tmp && *tmp){ + spec = tmp; + option_source = 2; + } + } + + if(spec){ + fn = do_one_spec(spec, NULL); /* use override "" for no headers */ + fmt_walk = fn; + 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; + } + }else{ + err = generate_sysv_list(); + if(err) return err; + option_source = 3; + } + } + if(format_modifiers){ /* generate_sysv_list() may have cleared some bits */ + format_node *fn; + if(option_source) return "Can't use output modifiers with user-defined output"; + 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(!option_source){ /* OK to really muck with stuff */ + format_node *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"); + } + } + + /* Could scan for duplicates (format and sort) here. Digital does. */ + return NULL; +} + diff --git a/ps/stacktrace.c b/ps/stacktrace.c new file mode 100644 index 00000000..79814bb3 --- /dev/null +++ b/ps/stacktrace.c @@ -0,0 +1,165 @@ +/* + * Gnu debugger stack trace code provided by Peter Mattis + * on Thu, 2 Nov 1995 + * + * Modified for easy use by Albert Cahalan. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTERACTIVE 0 +#define STACK_TRACE 1 + +char *stored_prog_name = "You forgot to set \"program\""; +static int stack_trace_done; + +/***********/ +static void debug_stop(char **args){ + execvp (args[0], args); + perror ("exec failed"); + _exit (0); +} + +/***********/ +static void stack_trace_sigchld(int signum){ + stack_trace_done = 1; +} + +/************/ +static void stack_trace(char **args){ + pid_t pid; + int in_fd[2]; + int out_fd[2]; + fd_set fdset; + fd_set readset; + struct timeval tv; + int sel, index, state; + char buffer[256]; + char c; + + stack_trace_done = 0; + signal(SIGCHLD, stack_trace_sigchld); + + if((pipe (in_fd) == -1) || (pipe (out_fd) == -1)){ + perror ("could open pipe"); + _exit (0); + } + + pid = fork (); + if (pid == 0){ + close (0); dup (in_fd[0]); /* set the stdin to the in pipe */ + close (1); dup (out_fd[1]); /* set the stdout to the out pipe */ + close (2); dup (out_fd[1]); /* set the stderr to the out pipe */ + execvp (args[0], args); /* exec gdb */ + perror ("exec failed"); + _exit (0); + } else { + if(pid == (pid_t) -1){ + perror ("could not fork"); + _exit (0); + } + } + + FD_ZERO (&fdset); + FD_SET (out_fd[0], &fdset); + + write (in_fd[1], "backtrace\n", 10); + write (in_fd[1], "p x = 0\n", 8); + write (in_fd[1], "quit\n", 5); + + index = 0; + state = 0; + + for(;;){ + readset = fdset; + tv.tv_sec = 1; + tv.tv_usec = 0; + + sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv); + if (sel == -1) break; + + if((sel > 0) && (FD_ISSET (out_fd[0], &readset))){ + if(read (out_fd[0], &c, 1)){ + switch(state){ + case 0: + if(c == '#'){ + state = 1; + index = 0; + buffer[index++] = c; + } + break; + case 1: + buffer[index++] = c; + if((c == '\n') || (c == '\r')){ + buffer[index] = 0; + fprintf (stderr, "%s", buffer); + state = 0; + index = 0; + } + break; + default: + break; + } + } + } + else if(stack_trace_done) break; + } + + close (in_fd[0]); + close (in_fd[1]); + close (out_fd[0]); + close (out_fd[1]); + _exit (0); +} + +/************/ +void debug(int method, char *prog_name){ + pid_t pid; + char buf[16]; + char *args[4] = { "gdb", NULL, NULL, NULL }; + int x; + + snprintf (buf, 99, "%d", getpid ()); + + args[1] = prog_name; + args[2] = buf; + + pid = fork (); + if(pid == 0){ + switch (method){ + case INTERACTIVE: + fprintf (stderr, "debug_stop\n"); + debug_stop(args); + break; + case STACK_TRACE: + fprintf (stderr, "stack_trace\n"); + stack_trace(args); + break; + } + _exit(0); + } else if(pid == (pid_t) -1){ + perror ("could not fork"); + return; + } + + x = 1; + while(x); /* wait for debugger? */ +} + +/************/ +static void stack_trace_sigsegv(int signum){ + debug(STACK_TRACE, stored_prog_name); +} + +/************/ +void init_stack_trace(char *prog_name){ + stored_prog_name = prog_name; + signal(SIGSEGV, stack_trace_sigsegv); +} diff --git a/skill.1 b/skill.1 new file mode 100644 index 00000000..643cffeb --- /dev/null +++ b/skill.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 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 \- report process status + +.SH SYNOPSIS +.nf +skill [signal to send] [options] process selection criteria +snice [new priority] [options] process selection criteria +.fi + +.SH DESCRIPTION +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. +.TE + +.SH "PROCESS SELECTION OPTIONS" +Selection criteria can be: terminal, user, pid, command. +The options below may be used to ensure correct interpretation. +.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 +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 netscape crack +7 Slow down netscape and crack +skill -KILL -v /dev/pts/* Kill users on new-style PTY devices +skill -STOP torvalds davem tytso Stop 3 users +snice -17 root bash Give priority to root's shell +.TE + +.SH "SEE ALSO" +top(1) kill(1) renice(1) nice(1) + +.SH STANDARDS +No standards apply. + +.SH AUTHOR +Albert Cahalan wrote skill and snice in 1999 as a +replacement for a non-free version. Michael K. Johnson +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 00000000..7d784692 --- /dev/null +++ b/skill.c @@ -0,0 +1,557 @@ +/* + * Copyright 1998 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 +#include +#include /* char *user_from_uid(uid_t uid) */ + +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 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 char **saved_argv; + +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 + + +/********************************************************************/ + + +/***** kill or nice a process */ +static void hurt_proc(int tty, int uid, int pid, char *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, "%-8.8s %-8.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, "%-8.8s %-8.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("%-8.8s %-8.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){ + 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, char *argv[]){ + char *sigptr; + int signo = SIGTERM; + int exitvalue = 0; + if(argc<2) kill_usage(); + 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 */ + if(argv[1][1]=='s' && argv[1][2]=='\0'){ + 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; + 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){ + 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, char *argv[]){ + int signo = -1; + int prino = NO_PRI_VAL; + int force = 0; + int num_found = 0; + char *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); + } + 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,-1); + 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(); + } + if(n_flag && signo>=0){ + fprintf(stderr,"ERROR: -n makes no sense with a signal.\n"); + skillsnice_usage(); + } + if(n_flag && prino!=NO_PRI_VAL){ + fprintf(stderr,"ERROR: -n makes no sense with a priority value.\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, char *argv[]){ + char *tmpstr; + my_pid = getpid(); + saved_argv = argv; + 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/snice.1 b/snice.1 new file mode 100644 index 00000000..1595a808 --- /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 00000000..1491252a --- /dev/null +++ b/sysctl.8 @@ -0,0 +1,74 @@ +.\" 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] variable ..." +.br +.B "sysctl [-n] -w variable=value ..." +.br +.B "sysctl [-n] -p (default /etc/sysctl.conf)" +.br +.B "sysctl [-n] -a" +.br +.B "sysctl [-n] -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 "-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. +.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 00000000..e1af4bd7 --- /dev/null +++ b/sysctl.c @@ -0,0 +1,437 @@ + +/* + * 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 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Additional types we might need. + */ +typedef int bool; + +static bool true = 1; +static bool false = 0; + + +/* + * Function Prototypes + */ +int Usage(const char *name); +void Preload(const char *filename); +int WriteSetting(const char *setting); +int ReadSetting(const char *setting); +int DisplayAll(const char *path, bool ShowTableUtil); + + +/* + * Globals... + */ + +const char *PROC_PATH = "/proc/sys/"; +const char *DEFAULT_PRELOAD = "/etc/sysctl.conf"; +static bool PrintName; +static bool PrintNewline; + +/* error messages */ +const char *ERR_UNKNOWN_PARAMETER = "error: Unknown parameter '%s'\n"; +const char *ERR_MALFORMED_SETTING = "error: Malformed setting '%s'\n"; +const char *ERR_NO_EQUALS = "error: '%s' must be of the form name=value\n"; +const char *ERR_INVALID_KEY = "error: '%s' is an unknown key\n"; +const char *ERR_UNKNOWN_WRITING = "error: unknown error %d setting key '%s'\n"; +const char *ERR_UNKNOWN_READING = "error: unknown error %d reading key '%s'\n"; +const char *ERR_PERMISSION_DENIED = "error: permission denied on key '%s'\n"; +const char *ERR_OPENING_DIR = "error: unable to open directory '%s'\n"; +const char *ERR_PRELOAD_FILE = "error: unable to open preload file '%s'\n"; +const char *WARN_BAD_LINE = "warning: %s(%d): invalid syntax, continuing...\n"; + + +#define DOTSLASH(x) do{ \ + char *p_ = (x); \ + for(;;){ \ + p_ = strchr(p_, '.'); \ + if(!p_) break; \ + *p_ = '/'; \ + } \ +}while(0) + +#define SLASHDOT(x) do{ \ + char *p_ = (x); \ + for(;;){ \ + p_ = strchr(p_, '.'); \ + if(!p_) break; \ + *p_ = '/'; \ + } \ +}while(0) + + +/* + * 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; + + if (argc < 2) { + return Usage(me); + } /* endif */ + + argv++; + + for (; argv && *argv && **argv; argv++) { + if (SwitchesAllowed && **argv == '-') { /* we have a switch */ + switch((*argv)[1]) { + case 'b': + /* This is "binary" format, which means more for BSD. */ + PrintNewline = false; + /* FALL THROUGH */ + case 'n': + PrintName = false; + break; + case 'w': + SwitchesAllowed = false; + WriteMode = true; + break; + case 'p': + argv++; + if (argv && *argv && **argv) { + preloadfile = *argv; + } /* endif */ + + Preload(preloadfile); + return(0); + break; + case 'a': /* string and integer values (for Linux, all of them) */ + case 'A': /* the above, including "opaques" (would be unprintable) */ + case 'X': /* the above, with opaques completly printed in hex */ + SwitchesAllowed = false; + return DisplayAll(PROC_PATH, ((*argv)[1] == 'a') ? false : true); + case 'h': + case '?': + return Usage(me); + default: + fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv); + return Usage(me); + } /* end switch */ + } else { + SwitchesAllowed = false; + if (WriteMode) + ReturnCode = WriteSetting(*argv); + else ReadSetting(*argv); + } /* end if */ + } /* end for */ + +return ReturnCode; +} /* end main */ + + + + + +/* + * Display the usage format + * + */ +int Usage(const char *name) { + printf("usage: %s [-n] variable ... \n" + " %s [-n] -w variable=value ... \n" + " %s [-n] -a \n" + " %s [-n] -p (default /etc/sysctl.conf) \n" + " %s [-n] -A\n", name, name, name, name, name); +return -1; +} /* end Usage() */ + + +/* + * Strip the leading and trailing spaces from a string + * + */ +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; +} /* end StripLeadingAndTrailingSpaces() */ + + + +/* + * Preload the sysctl's from the conf file + * - we parse the file and then reform it (strip out whitespace) + * + */ +void Preload(const char *filename) { +FILE *fp; +char oneline[257]; +char buffer[257]; +char *t; +int n = 0; +char *name, *value; + + if (!filename || ((fp = fopen(filename, "r")) == NULL)) { + fprintf(stderr, ERR_PRELOAD_FILE, filename); + return; + } /* endif */ + + while (fgets(oneline, 256, fp)) { + oneline[256] = 0; + 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; + } /* endif */ + + StripLeadingAndTrailingSpaces(name); + + value = strtok(NULL, "\n\r"); + if (!value || !*value) { + fprintf(stderr, WARN_BAD_LINE, filename, n); + continue; + } /* endif */ + + while ((*value == ' ' || *value == '\t') && *value != 0) + value++; + + sprintf(buffer, "%s=%s", name, value); + WriteSetting(buffer); + } /* endwhile */ + + fclose(fp); +} /* end Preload() */ + + + +/* + * Write a sysctl setting + * + */ +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 dont' want to display this err */ + return 0; + } /* end if */ + + equals = index(setting, '='); + + if (!equals) { + fprintf(stderr, ERR_NO_EQUALS, setting); + return -1; + } /* end if */ + + value = equals + sizeof(char); /* point to the value in name=value */ + + if (!*name || !*value || name == equals) { + fprintf(stderr, ERR_MALFORMED_SETTING, setting); + return -2; + } /* end if */ + + tmpname = (char *)malloc((equals-name+1+strlen(PROC_PATH))*sizeof(char)); + outname = (char *)malloc((equals-name+1)*sizeof(char)); + + strcpy(tmpname, PROC_PATH); + strncat(tmpname, name, (int)(equals-name)); + tmpname[equals-name+strlen(PROC_PATH)] = 0; + strncpy(outname, name, (int)(equals-name)); + outname[equals-name] = 0; + + DOTSLASH(tmpname); /* change . to / */ + SLASHDOT(outname); /* change / to . */ + + fp = fopen(tmpname, "w"); + + if (!fp) { + switch(errno) { + case ENOENT: + fprintf(stderr, ERR_INVALID_KEY, outname); + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + break; + default: + fprintf(stderr, ERR_UNKNOWN_WRITING, errno, outname); + break; + } /* end switch */ + rc = -1; + } else { + fprintf(fp, "%s\n", value); + fclose(fp); + if (PrintName) { + fprintf(stdout, "%s = %s\n", outname, value); + } else { + if (PrintNewline) + fprintf(stdout, "%s\n", value); + else + fprintf(stdout, "%s", value); + } + } /* endif */ + + free(tmpname); + free(outname); +return rc; +} /* end WriteSetting() */ + + + +/* + * Read a sysctl setting + * + */ +int ReadSetting(const char *setting) { +int rc = 0; +char *tmpname, *outname; +char inbuf[1025]; +const char *name = setting; +FILE *fp; + + if (!setting || !*setting) { + fprintf(stderr, ERR_INVALID_KEY, setting); + } /* endif */ + + tmpname = (char *)malloc((strlen(name)+strlen(PROC_PATH)+1)*sizeof(char)); + outname = (char *)malloc((strlen(name)+1)*sizeof(char)); + + strcpy(tmpname, PROC_PATH); + strcat(tmpname, name); + strcpy(outname, name); + + + DOTSLASH(tmpname); /* change . to / */ + SLASHDOT(outname); /* change / to . */ + + fp = fopen(tmpname, "r"); + + if (!fp) { + switch(errno) { + case ENOENT: + fprintf(stderr, ERR_INVALID_KEY, outname); + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + break; + default: + fprintf(stderr, ERR_UNKNOWN_READING, errno, outname); + break; + } /* end switch */ + rc = -1; + } else { + while(fgets(inbuf, 1024, fp)) { + /* 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); + } + } /* endwhile */ + fclose(fp); + } /* endif */ + + free(tmpname); + free(outname); +return rc; +} /* end ReadSetting() */ + + + +/* + * Display all the sysctl settings + * + */ +int DisplayAll(const char *path, bool ShowTableUtil) { +int rc = 0; +int rc2; +DIR *dp; +struct dirent *de; +char *tmpdir; +struct stat ts; + + dp = opendir(path); + + if (!dp) { + fprintf(stderr, ERR_OPENING_DIR, path); + rc = -1; + } else { + readdir(dp); readdir(dp); /* skip . and .. */ + while (( de = readdir(dp) )) { + tmpdir = (char *)malloc(strlen(path)+strlen(de->d_name)+2); + sprintf(tmpdir, "%s%s", path, de->d_name); + rc2 = stat(tmpdir, &ts); /* should check this return code */ + if (rc2 != 0) { + perror(tmpdir); + } else { + if (S_ISDIR(ts.st_mode)) { + strcat(tmpdir, "/"); + DisplayAll(tmpdir, ShowTableUtil); + } else { + rc |= ReadSetting(tmpdir+strlen(PROC_PATH)); + } /* endif */ + } /* endif */ + free(tmpdir); + } /* end while */ + closedir(dp); + } /* endif */ + +return rc; +} /* end DisplayAll() */ + diff --git a/sysctl.conf.5 b/sysctl.conf.5 new file mode 100644 index 00000000..0d8b0731 --- /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 00000000..c7b3c06f --- /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_PRELOAD=proc/libproc.so exec ./top "$@" diff --git a/tload.1 b/tload.1 new file mode 100644 index 00000000..aec34dd4 --- /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 00000000..1d346bbe --- /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/tmp-junk.c b/tmp-junk.c new file mode 100644 index 00000000..db011b17 --- /dev/null +++ b/tmp-junk.c @@ -0,0 +1,730 @@ +/* + * w.c v1.4 + * + * An alternative "w" program for Linux. + * Shows users and their processes. + * + * Copyright (c) Dec 1993, Oct 1994 Steve "Mr. Bassman" Bryant + * bassman@hpbbi30.bbn.hp.com (Old address) + * bassman@muttley.soc.staffs.ac.uk + * + * Info: + * I starting writing as an improvement of the w program included + * with linux. The idea was to add in some extra functionality to the + * program, and see if I could fix a couple of bugs which seemed to + * occur. + * Mr. Bassman, 10/94 + * + * Acknowledgments: + * + * The original version of w: + * Copyright (c) 1993 Larry Greenfield (greenfie@gauss.rutgers.edu) + * + * Uptime routine and w mods: + * Michael K. Johnson (johnsonm@stolaf.edu) + * + * + * Distribution: + * This program is freely distributable under the terms of copyleft. + * No warranty, no support, use at your own risk etc. + * + * Compilation: + * gcc -O -o w sysinfo.c whattime.c w.c + * + * Usage: + * w [-hfusd] [user] + * + * + * $Log: tmp-junk.c,v $ + * Revision 1.1 2002/02/01 22:46:37 csmall + * Initial revision + * + * Revision 1.5 1994/10/26 17:57:35 bassman + * Loads of stuff - see comments. + * + * Revision 1.4 1994/01/01 12:57:21 johnsonm + * Added RCS, and some other fixes. + * + * Revision history: + * Jan 01, 1994 (mkj): Eliminated GCC warnings, took out unnecessary + * dead variables in fscanf, replacing them with + * *'d format qualifiers. Also added RCS stuff. + * Oct 26, 1994 (bass): Tidied up the code, fixed bug involving corrupt + * utmp records. Added switch for From field; + * default is compile-time set. Added -d option + * as a remnant from BSD 'w'. Fixed bug so it now + * behaves if the first process on a tty isn't owned + * by the person first logged in on that tty, and + * also detects su'd users. Changed the tty format + * to the short one. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/whattime.h" + + +#define TRUE 1 +#define FALSE 0 +/* + * Default setting for whether to have a From field. The -f switch + * toggles this - if the default is to have it, using -f will turn + * it off; if the default is not to have it, the -f switch will put + * it in. Possible values are TRUE (to have the field by default), + * and FALSE. + */ +#define DEFAULT_FROM TRUE +#define ZOMBIE "" + + +void put_syntax(); +char *idletime(); +char *logintime(); + +static char rcsid[]="$Id: tmp-junk.c,v 1.1 2002/02/01 22:46:37 csmall Exp $"; + + +void main (argc, argv) + +int argc; +char *argv[]; + +{ + int header=TRUE, long_format=TRUE, ignore_user=TRUE, + from_switch=DEFAULT_FROM, show_pid=FALSE, line_length; + int i, j; + struct utmp *utmp_rec; + struct stat stat_rec; + struct passwd *passwd_entry; + uid_t uid; + char username[9], tty[13], rhost[17], login_time[27]; + char idle_time[7], what[1024], pid[10]; + char out_line[1024], file_name[256]; + char search_name[9]; + int jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime; + char /*ch,*/ state, comm[1024], *columns_ptr; + FILE *fp; + + + search_name[0] = '\0'; + + + /* + * Process the command line + */ + if (argc > 1) + { + /* + * Args that start with '-' + */ + for (i = 1; ((i < argc) && (argv[i][0] == '-')); i ++) + { + for (j = 1; argv[i][j] != '\0'; j++) + { + switch (argv[i][j]) + { + case 'h': + header = FALSE; + break; + case 's': + long_format = FALSE; + break; + case 'u': + ignore_user = FALSE; + break; + case 'd': + show_pid = TRUE; + break; + case 'f': + if (DEFAULT_FROM == TRUE) + from_switch = FALSE; + else + from_switch = TRUE; + break; + default: + fprintf (stderr, "w: unknown option: '%c'\n", + argv[i][j]); + put_syntax (); + break; + } + } + } + + + /* + * Check for arg not starting with '-' (ie: username) + */ + if (argc > i) + { + strncpy (search_name, argv[i], 8); + search_name[8] = '\0'; + i ++; + + if (argc > i) + { + fprintf (stderr, "w: syntax error\n"); + put_syntax (); + } + } + } + + + + /* + * Check that /proc is actually there, or else we can't + * get all the information. + */ + if (chdir ("/proc")) + { + fprintf (stderr, "w: fatal error: cannot access /proc\n"); + perror (strerror(errno)); + exit (-1); + } + + + + /* + * Find out our screen width from $COLUMNS + */ + columns_ptr = getenv ("COLUMNS"); + if (columns_ptr == NULL) + { + struct winsize window; + + /* + * Try getting it directly + */ + if ((ioctl (1, TIOCGWINSZ, &window) != 1) && (window.ws_col > 0)) + line_length = window.ws_col; + else + line_length = 80; /* Default length assumed */ + } + else + line_length = atoi (columns_ptr); + + /* + * Maybe we should check whether there is enough space on + * the lines for the options selected... + */ + if (line_length < 60) + long_format = FALSE; + + line_length --; + + + /* + * Print whatever headers + */ + if (header == TRUE) + { + /* + * uptime: from MKJ's uptime routine, + * found in whattime.c + */ + print_uptime(); + + + /* + * Print relevant header bits + */ + printf ("User tty "); + + if (long_format == TRUE) + { + if (from_switch == TRUE) + printf ("From "); + + printf (" login@ idle JCPU PCPU "); + + if (show_pid == TRUE) + printf (" PID "); + + printf ("what\n"); + } + else + { + printf (" idle "); + + if (show_pid == TRUE) + printf (" PID "); + + printf ("what\n"); + } + } + + + + + /* + * Process user information. + */ + while ((utmp_rec = getutent())) + { + /* + * Check we actually want to see this record. + * It must be a valid active user process, + * and match a specified search name. + */ + if ( (utmp_rec->ut_type == USER_PROCESS) + && (strcmp(utmp_rec->ut_user, "")) + && ( (search_name[0] == '\0') + || ( (search_name[0] != '\0') + && !strncmp(search_name, utmp_rec->ut_user, 8) ) ) ) + { + /* + * Get the username + */ + strncpy (username, utmp_rec->ut_user, 8); + username[8] = '\0'; /* Set end terminator */ + + + /* + * Find out the uid of that user (from their + * passwd entry) + */ + uid = -1; + if ((passwd_entry = getpwnam (username)) != NULL) + { + uid = passwd_entry->pw_uid; + } + + /* + * Get (and clean up) the tty line + */ + for (i = 0; (utmp_rec->ut_line[i] > 32) && (i < 6); i ++) + tty[i] = utmp_rec->ut_line[i]; + + utmp_rec->ut_line[i] = '\0'; + tty[i] = '\0'; + + + /* + * Don't bother getting info if it's not asked for + */ + if (long_format == TRUE) + { + + /* + * Get the remote hostname; this can be up to 16 chars, + * but if any chars are invalid (ie: [^a-zA-Z0-9\.]) + * then the char is changed to a string terminator. + */ + if (from_switch == TRUE) + { + strncpy (rhost, utmp_rec->ut_host, 16); + rhost[16] = '\0'; + + } + + + /* + * Get the login time + * (Calculated by LG's routine, below) + */ + strcpy (login_time, logintime(utmp_rec->ut_time)); + } + + + + /* + * Get the idle time. + * (Calculated by LG's routine, below) + */ + strcpy (idle_time, idletime (tty)); + + + + /* + * That's all the info out of /etc/utmp. + * The rest is more difficult. We use the pid from + * utmp_rec->ut_pid to look in /proc for the info. + * NOTE: This is not necessarily the active pid, so we chase + * down the path of parent -> child pids until we find it, + * according to the information given in /proc//stat. + */ + + sprintf (pid, "%d", utmp_rec->ut_pid); + + what[0] = '\0'; + strcpy (file_name, pid); + strcat (file_name, "/stat"); + jcpu = 0; + pcpu = 0; + + if ((fp = fopen(file_name, "r"))) + { + while (what[0] == '\0') + { + /* + * Check /proc//stat to see if the process + * controlling the tty is the current one + */ + fscanf (fp, "%d %s %c %*d %*d %*d %*d %d " + "%*u %*u %*u %*u %*u %d %d %d %d", + &curr_pid, comm, &state, &tpgid, + &utime, &stime, &cutime, &cstime); + + fclose (fp); + + if (comm[0] == '\0') + strcpy (comm, "-"); + + /* + * Calculate jcpu and pcpu. + * JCPU is the time used by all processes and their + * children, attached to the tty. + * PCPU is the time used by the current process + * (calculated once after the loop, using last + * obtained values). + */ + if (!jcpu) + jcpu = cutime + cstime; + + /* + * Check for a zombie first... + */ + if (state == 'Z') + strcpy (what, ZOMBIE); + else if (curr_pid == tpgid) + { + /* + * If it is the current process, read cmdline + * If that's empty, then the process is swapped out, + * or is a zombie, so we use the command given in stat + * which is in normal round brackets, ie: "()". + */ + strcpy (file_name, pid); + strcat (file_name, "/cmdline"); + if ((fp = fopen(file_name, "r"))) + { + i = 0; + j = fgetc (fp); + while ((j != EOF) && (i < 256)) + { + if (j == '\0') + j = ' '; + + what[i] = j; + i++; + j = fgetc (fp); + } + what[i] = '\0'; + fclose (fp); + } + + if (what[0] == '\0') + strcpy (what, comm); + } + else + { + /* + * Check out the next process + * If we can't open it, use info from this process, + * so we have to check out cmdline first. + * + * If we're not using "-u" then should we just + * say "-" (or "-su") instead of a command line ? + * If so, we should strpcy(what, "-"); when we + * fclose() in the if after the stat() below. + */ + strcpy (file_name, pid); + strcat (file_name, "/cmdline"); + + if ((fp = fopen (file_name, "r"))) + { + i = 0; + j = fgetc (fp); + while ((j != EOF) && (i < 256)) + { + if (j == '\0') + j = ' '; + + what[i] = j; + i++; + j = fgetc (fp); + } + what[i] = '\0'; + fclose (fp); + } + + if (what[0] == '\0') + strcpy (what, comm); + + /* + * Now we have something in the what variable, + * in case we can't open the next process. + */ + sprintf (pid, "%d", tpgid); + strcpy (file_name, pid); + strcat (file_name, "/stat"); + + fp = fopen (file_name, "r"); + + if (fp && (ignore_user == FALSE)) + { + /* + * We don't necessarily go onto the next process, + * unless we are either ignoring who the effective + * user is, or it's the same uid + */ + stat (file_name, &stat_rec); + + /* + * If the next process is not owned by this + * user finish the loop. + */ + if (stat_rec.st_uid != uid) + { + fclose (fp); + + strcpy (what, "-su"); + /* + * See comment above somewhere; I've used + * "-su" here, as the next process is owned + * by someone else; this is generally + * because the user has done an "su" which + * then exec'd something else. + */ + } + else + what[0] = '\0'; + } + else if (fp) /* else we are ignoring uid's */ + what[0] = '\0'; + } + } + } + else /* Could not open first process for user */ + strcpy (what, "?"); + + + /* + * There is a bug somewhere in my version of linux + * which means that utmp records are not cleaned + * up properly when users log out. However, we + * can detect this, by the users first process + * not being there when we look in /proc. + */ + + + /* + * Don't output a line for "dead" users. + * This gets round a bug which doesn't update utmp/wtmp + * when users log out. + */ + if (what[0] != '?') + { +#ifdef 0 +/* This makes unix98 pty's not line up, so has been disabled - JEH. */ + /* + * Remove the letters 'tty' from the tty id + */ + if (!strncmp (tty, "tty", 3)) + { + for (i = 3; tty[i - 1] != '\0'; i ++) + tty[i - 3] = tty[i]; + } +#endif + + /* + * Common fields + */ + sprintf (out_line, "%-9.8s%-6.7s ", username, tty); + + + /* + * Format the line for output + */ + if (long_format == TRUE) + { + /* + * Calculate CPU usage + */ + pcpu = utime + stime; + jcpu /= 100; + pcpu /= 100; + + if (from_switch == TRUE) + sprintf (out_line, "%s %-16.15s", out_line, rhost); + + sprintf (out_line, "%s%8.8s ", out_line, login_time); + + } + + sprintf (out_line, "%s%6s", out_line, idle_time); + + + if (long_format == TRUE) + { + if (!jcpu) + strcat (out_line, " "); + else if (jcpu/60) + sprintf (out_line, "%s%3d:%02d", out_line, + jcpu/60, jcpu%60); + else + sprintf (out_line, "%s %2d", out_line, jcpu); + + if (!pcpu) + strcat (out_line, " "); + else if (pcpu/60) + sprintf (out_line, "%s%3d:%02d", out_line, + pcpu/60, pcpu%60); + else + sprintf (out_line, "%s %2d", out_line, pcpu); + } + + if (show_pid == TRUE) + sprintf (out_line, "%s %5.5s", out_line, pid); + + + strcat (out_line, " "); + strcat (out_line, what); + + + /* + * Try not to exceed the line length + */ + out_line[line_length] = '\0'; + + printf ("%s\n", out_line); + } + } + } +} + + + +/* + * put_syntax() + * + * Routine to print the correct syntax to call this program, + * and then exit out appropriately + */ +void put_syntax () +{ + fprintf (stderr, "usage: w [-hfsud] [user]\n"); + exit (-1); +} + + + +/* + * idletime() + * + * Routine which returns a string containing + * the idle time of a given user. + * + * This routine was lifted from the original w program + * by Larry Greenfield (greenfie@gauss.rutgers.edu) + * Copyright (c) 1993 Larry Greenfield + * + */ +char *idletime (tty) + +char *tty; + +{ + struct stat terminfo; + unsigned long idle; + char ttytmp[40]; + static char give[20]; + time_t curtime; + + curtime = time (NULL); + + sprintf (ttytmp, "/dev/%s", tty); + stat (ttytmp, &terminfo); + idle = (unsigned long) curtime - (unsigned long) terminfo.st_atime; + + if (idle >= (60 * 60)) /* more than an hour */ + { + if (idle >= (60 * 60 * 48)) /* more than two days */ + sprintf (give, "%2ludays", idle / (60 * 60 * 24)); + else + sprintf (give, " %2lu:%02u", idle / (60 * 60), + (unsigned) ((idle / 60) % 60)); + } + else + { + if (idle / 60) + sprintf (give, "%6lu", idle / 60); + else + give[0]=0; + } + + return give; +} + + + +/* + * logintime() + * + * Returns the time given in a suitable format + * + * This routine was lifted from the original w program + * by Larry Greenfield (greenfie@gauss.rutgers.edu) + * Copyright (c) 1993 Larry Greenfield + * + */ + +#undef ut_time + +char *logintime(ut_time) + +time_t ut_time; + +{ + time_t curtime; + struct tm *logintime, *curtm; + int hour, am, curday, logday; + static char give[20]; + static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", + "Sat" }; + static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" }; + + curtime = time(NULL); + curtm = localtime(&curtime); + curday = curtm->tm_yday; + logintime = localtime(&ut_time); + hour = logintime->tm_hour; + logday = logintime->tm_yday; + am = (hour < 12); + + if (!am) + hour -= 12; + + if (hour == 0) + hour = 12; + + /* + * This is a newer behavior: it waits 12 hours and the next day, and then + * goes to the 2nd time format. This should reduce confusion. + * It then waits only 6 days (not till the last moment) to go the last + * time format. + */ + if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday)) + { + if (curtime > (ut_time + (60 * 60 * 24 * 6))) + sprintf(give, "%2d%3s%2d", logintime->tm_mday, + month[logintime->tm_mon], (logintime->tm_year % 100)); + else + sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday], + hour, am ? "am" : "pm"); + } + else + sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm"); + + return give; +} + diff --git a/top.1 b/top.1 new file mode 100644 index 00000000..7be16674 --- /dev/null +++ b/top.1 @@ -0,0 +1,456 @@ +.\" This file Copyright 1992 Robert J. Nation +.\" (nation@rocket.sanders.lockheed.com) +.\" It may be distributed under the GNU Public License, version 2, or +.\" any higher version. See section COPYING of the GNU Public license +.\" for conditions under which this file may be redistributed. +.\" +.\" Modified 1994/04/25 Michael Shields +.\" Cleaned up, and my changes documented. New `.It' macro. Edited. +.\" modified 1996/01/27 Helmut Geyer to match my changes. +. +.de It +.TP 0.5i +.B "\\$1 " +.. +.TH TOP 1 "Feb 1 1993" "Linux" "Linux User's Manual" +.SH NAME +top \- display top CPU processes +.SH SYNOPSIS +.B top +.RB [ \- ] +.RB [ d +.IR delay ] +.RB [ p +.IR pid ] +.RB [ q ] +.RB [ c ] +.RB [ C ] +.RB [ S ] +.RB [ s ] +.RB [ i ] +.RB [ n +.IR iter ] +.RB [ b ] +.SH DESCRIPTION +.B top +provides an ongoing look at processor activity in real time. It +displays a listing of the most CPU-intensive tasks on the system, and +can provide an interactive interface for manipulating processes. +It can sort the tasks by CPU usage, memory usage and runtime. +.N top +can be better configured than the standard top from the procps suite. +Most features can either be selected by an interactive command or by +specifying the feature in the personal or system-wide configuration +file. See below for more information. +.PP +.SH "COMMAND\-LINE OPTIONS" +.It d +Specifies the delay between screen updates. You can change this with +the +.B s +interactive command. +.It p +Monitor only processes with given process id. +This flag can be given up to twenty times. This option is neither +available interactively nor can it be put into the configuration file. +.It q +This causes +.B top +to refresh without any delay. If the caller has superuser priviledges, +top runs with the highest possible priority. +.It S +Specifies cumulative mode, where each process is listed with the CPU +time that it +.I as well as its dead children +has spent. This is like the +.B -S +flag to +.BR ps (1). +See the discussion below of the +.B S +interactive command. +.It s +Tells +.B top +to run in secure mode. This disables the potentially dangerous of the +interactive commands (see below). A secure +.B top +is a nifty thing to leave running on a spare terminal. +.It i +Start +.B top +ignoring any idle or zombie processes. See the interactive command +.B i +below. +.It C +display total CPU states in addition to individual CPUs. This option +only affects SMP systems. +.It c +display command line instead of the command name only. The default +behaviour has been changed as this seems to be more useful. +.It n +Number of iterations. Update the display this number of times and then exit. +.It b +Batch mode. Useful for sending output from top to other programs or to a file. +In this mode, +.B top +will not accept command line input. It runs until it produces the number of +iterations requested with the +.B n +option or until killed. Output is plain text suitable for display on a dumb +terminal. +. +.SH "FIELD DESCRIPTIONS" +.B top +displays a variety of information about the processor state. The +display is updated every 5 seconds by default, but you can change that +with the +.B d +command-line option or the +.B s +interactive command. +.It "uptime" +This line displays the time the system has been up, and the three load +averages for the system. The load averages are the average number of +process ready to run during the last 1, 5 and 15 minutes. This line is +just like the output of +.BR uptime (1). +The uptime display may be toggled by the interactive +.B l +command. +.It processes +The total number of processes running at the time of the last update. +This is also broken down into the number of tasks which are running, +sleeping, stopped, or undead. The processes and states display may be +toggled by the +.B t +interactive command. +.It "CPU states" +Shows the percentage of CPU time in user mode, system mode, niced tasks, +and idle. (Niced tasks are only those whose nice value is negative.) +Time spent in niced tasks will also be counted in system and user time, +so the total will be more than 100%. The processes and states display +may be +toggled by the +.B t +interactive command. +.It Mem +Statistics on memory usage, including total available memory, free +memory, used memory, shared memory, and memory used for buffers. The +display of memory information may be toggled by the +.B m +interactive command. +.It Swap +Statistics on swap space, including total swap space, available swap +space, and used swap space. This and +.B Mem +are just like the output of +.BR free (1). +.It PID +The process ID of each task. +.It PPID +The parent process ID each task. +.It UID +The user ID of the task's owner. +.It USER +The user name of the task's owner. +.It PRI +The priority of the task. +.It NI +The nice value of the task. Negative nice values are higher priority. +.It SIZE +The size of the task's code plus data plus stack space, in kilobytes, +is shown here. +.It TSIZE +The code size of the task. This gives strange values for kernel +processes and is broken for ELF processes. +.It DSIZE +Data + Stack size. This is broken for ELF processes. +.It TRS +Text resident size. +.It SWAP +Size of the swapped out part of the task. +.It D +Size of pages marked dirty. +.It LC +Last used processor. (That this changes from time to time is not +a bug; Linux intentionally uses weak affinity. Also notice that +the very act of running top may break weak affinity and cause more +processes to change current CPU more often because of the extra +demand for CPU time.) +.It RSS +The total amount of physical memory used by the task, in kilobytes, is +shown here. For ELF processes used library pages are counted here, for +a.out processes not. +.It SHARE +The amount of shared memory used by the task is shown in this column. +.It STAT +The state of the task is shown here. The state is either +.B S +for sleeping, +.B D +for uninterruptible sleep, +.B R +for running, +.B Z +for zombies, or +.B T +for stopped or traced. These states are modified by trailing +.B < +for a process with negative nice value, +.B N +for a process with positive nice value, +.B W +for a swapped out process (this does not work correctly for kernel +processes). +.It WCHAN +depending on the availablity of either +.I /boot/psdatabase +or the kernel link map +.I /boot/System.map +this shows the address or the name of the kernel function the task +currently is sleeping in. +.It TIME +Total CPU time the task has used since it started. If cumulative mode +is on, this also includes the CPU time used by the process's children +which have died. You can set cumulative mode with the +.B S +command line option or toggle it with the interactive command +.BR S . +The header line will then be changed to +.BR CTIME . +.It %CPU +The task's share of the CPU time since the last screen update, expressed +as a percentage of total CPU time per processor. +.It %MEM +The task's share of the physical memory. +.It COMMAND +The task's command name, which will be truncated if it is too long to be +displayed on one line. Tasks in memory will have a full command line, +but swapped-out tasks will only have the name of the program in +parentheses (for example, "(getty)"). +.It "A , WP" +these fields from the kmem top are not supported. +. +.SH "INTERACTIVE COMMANDS" +Several single-key commands are recognized while +.B top +is running. Some are disabled if the +.B s +option has been given on the command line. +.It space +Immediately updates the display. +.It ^L +Erases and redraws the screen. +.It "h\fR or \fB?" +Displays a help screen giving a brief summary of commands, and the +status of secure and cumulative modes. +.It k +Kill a process. You will be prompted for the PID of the task, and the +signal to send to it. For a normal kill, send signal 15. For a sure, +but rather abrupt, kill, send signal 9. The default signal, as with +.BR kill (1), +is 15, +.BR SIGTERM . +This command is not available in secure mode. +.It i +Ignore idle and zombie processes. This is a toggle switch. +.It I +Toggle between Solaris (CPU percentage divided by total number of CPUs) +and Irix (CPU percentage calculated solely by amount of time) views. +This is a toggle switch that affects only SMP systems. +.It "n\fR or \fB#" +Change the number of processes to show. You will be prompted to enter +the number. This overrides automatic determination of the number of +processes to show, which is based on window size measurement. If 0 is +specified, then top will show as many processes as will fit on the +screen; this is the default. +.It q +Quit. +.It r +Re-nice a process. You will be prompted for the PID of the task, and +the value to nice it to. Entering a positve value will cause a process +to be niced to negative values, and lose priority. If root is running +.BR top , +a negative value can be entered, causing a process to get a higher than +normal priority. The default renice value is 10. This command is not +available in secure mode. +.It S +This toggles cumulative mode, the equivalent of +.BR "ps -S" , +i.e., that CPU times will include a process's defunct children. For +some programs, such as compilers, which work by forking into many +seperate tasks, normal mode will make them appear less demanding than +they actually are. For others, however, such as shells and +.BR init , +this behavior is correct. In any case, try cumulative mode for an +alternative view of CPU use. +.It s +Change the delay between updates. You will be prompted to enter the +delay time, in seconds, between updates. Fractional values are +recognized down to microseconds. Entering 0 causes continuous updates. +The default value is 5 seconds. Note that low values cause nearly +unreadably fast displays, and greatly raise the load. This command is +not available in secure mode. +.It "f\fR or \fBF" +Add fields to display or remove fields from the display. See below for +more information. +.It "o\fR or \fBO" +Change order of displayed fields. See below for more information. +.It l +toggle display of load average and uptime information. +.It m +toggle display of memory information. +.It t +toggle display of processes and CPU states information. +.It c +toggle display of command name or full command line. +.It N +sort tasks by pid (\fIn\fPumerically). +.It A +sort tasks by age (newest first). +.It P +sort tasks by CPU usage (default). +.It M +sort tasks by resident memory usage. +.It T +sort tasks by time / cumulative time. +.It W +Write current setup to +.IR ~/.toprc . +This is the recommended way to write a top configuration file. +. +.SH The Field and Order Screens +After pressing +.BR f , +.BR F , +.B o +or +.B O +you will be shown a screen specifying the field order on the top line +and short descriptions of the field contents. The field order string +uses the following syntax: If the letter in the filed string +corresponding to a field is upper case, the field will be displayed. +This is furthermore indicated by an asterisk in front of the field +description. +The order of the fields corresponds to the order of the letters in the +string. + From the field select screen you can toggle the display of a field by +pressing the corresponding letter. + From the order screen you may move a field to the left by pressing +the corresponding upper case letter resp. to the right by pressing the +lower case one. +. +.SH Configuration Files +Top reads it's default configuration from two files, +.I /etc/toprc +and +.IR ~/.toprc . +The global configuration file may be used to restrict the usage of top +to the secure mode for non-priviledged users. If this is desired, the +file should contain a 's' to specify secure mode and a digit d (2<=d<=9) +for the default delay (in seconds) on a single line. +. +The personal configuration file contains two lines. The first line +contains lower and upper letters to specify which fields in what +order are to be displayed. The letters correspond to the letters in the +Fields or Order screens from top. As this is not very instructive, it is +recommended to select fields and order in a running top process and to +save this using the +.I W +interactive command. +. +The second line is more interesting (and important). It contains +information on the other options. Most important, if you have saved a +configuration in secure mode, you will not get an insecure top without +removing the lower 's' from the second line of your +.IR ~/.toprc . +A digit specifies the delay time between updates, a capital 'S' +cumulative mode, a lower 'i' no-idle mode, a capital 'I' Irix view. As +in interactive mode, a lower 'm', 'l', and 't' suppresses the display +of memory, uptime resp. process and CPU state information. +Currently changing the default sorting order (by CPU usage) is not +supported. +. +.SH NOTES +This +.BR proc -based +.B top +works by reading the files in the +.B proc +filesystem, +mounted on +.IR /proc . +If +.I /proc +is not mounted, +.B top +will not work. +.PP +.B %CPU +shows the cputime/realtime percentage in the period of time between +updates. For the first update, a short delay is used, and +.B top +itself dominates the CPU usage. After that, +.B top +will drop back, and a more reliable estimate of CPU usage is available. +.PP +The +.B SIZE +and +.B RSS fields don't count the page tables and the +.B task_struct +of a process; this is at least 12K of memory that is always resident. +.B SIZE +is the virtual size of the process (code+data+stack). +.PP +Keep in mind that a process must die for its time to be recorded on its +parent by cumulative mode. Perhaps more useful behavior would be to +follow each process upwards, adding time, but that would be more +expensive, possibly prohibitively so. In any case, that would make +.BR top 's +behavior incompatible with +.BR ps . +. +.SH FILES +.I /etc/toprc +The global configuration file. +.I ~/.toprc +The personal configuration file. +. +.SH "SEE ALSO" +.BR ps (1), +.BR free (1), +.BR uptime (1), +.BR kill (1), +.BR renice (1). +. +.SH +BUGS +If the window is less than about 70x7, +.B top +will not format information correctly. + Many fields still have problems with ELF processes. + the help screens are not yet optimized for windows with less than +25 lines +. +.SH AUTHOR +.B top +was originally written by Roger Binns, based on Branko Lankester's + ps program. +Robert Nation re-wrote it +significantly to use the proc filesystem, based on Michael K. Johnson's + proc-based ps program. +Michael Shields made many changes, including +secure and cumulative modes and a general cleanup. +Tim Janik added age sorting and the ability to monitor +specific processes through their ids. + +Helmut Geyer +Heavily changed it to include support for configurable fields and other +new options, and did further cleanup and use of the new readproc interface. + +The "b" and "n" options contributed by George Bonser +for CapTech IT Services. + +Please send bug reports to diff --git a/top.c b/top.c new file mode 100644 index 00000000..848bf9b1 --- /dev/null +++ b/top.c @@ -0,0 +1,1746 @@ +/* + * top.c - show top CPU processes + * + * Copyright (c) 1992 Branko Lankester + * Copyright (c) 1992 Roger Binns + * Copyright (c) 1997 Michael K. Johnson + * + * Snarfed and HEAVILY modified in december 1992 for procps + * by Michael K. Johnson, johnsonm@sunsite.unc.edu. + * + * Modified Michael K. Johnson's ps to make it a top program. + * Also borrowed elements of Roger Binns kmem based top program. + * Changes made by Robert J. Nation (nation@rocket.sanders.lockheed.com) + * 1/93 + * + * Modified by Michael K. Johnson to be more efficient in cpu use + * 2/21/93 + * + * Changed top line to use uptime for the load average. Also + * added SIGTSTP handling. J. Cowley, 19 Mar 1993. + * + * Modified quite a bit by Michael Shields (mjshield@nyx.cs.du.edu) + * 1994/04/02. Secure mode added. "d" option added. Argument parsing + * improved. Switched order of tick display to user, system, nice, idle, + * because it makes more sense that way. Style regularized (to K&R, + * more or less). Cleaned up much throughout. Added cumulative mode. + * Help screen improved. + * + * Fixed kill buglet brought to my attention by Rob Hooft. + * Problem was mixing of stdio and read()/write(). Added + * getnum() to solve problem. + * 12/30/93 Michael K. Johnson + * + * Added toggling output of idle processes via 'i' key. + * 3/29/94 Gregory K. Nickonov + * + * Fixed buglet where rawmode wasn't getting restored. + * Added defaults for signal to send and nice value to use. + * 5/4/94 Jon Tombs. + * + * Modified 1994/04/25 Michael Shields + * Merged previous changes to 0.8 into 0.95. + * Allowed the use of symbolic names (e.g., "HUP") for signal input. + * Rewrote getnum() into getstr(), getint(), getsig(), etc. + * + * Modified 1995 Helmut Geyer + * added kmem top functionality (configurable fields) + * configurable order of process display + * Added options for dis/enabling uptime, statistics, and memory info. + * fixed minor bugs for ELF systems (e.g. SIZE, RSS fields) + * + * Modified 1996/05/18 Helmut Geyer + * Use of new interface and general cleanup. The code should be far more + * readable than before. + * + * Modified 1996/06/25 Zygo Blaxell + * Added field scaling code for programs that run more than two hours or + * take up more than 100 megs. We have lots of both on our production line. + * + * Modified 1998/02/21 Kirk Bauer + * Added the 'u' option to display only a selected user... plus it will + * take into account that not all 20 top processes are actually shown, + * so it can fit more onto the screen. I think this may help the + * 'don't show idle' mode, but I'm not sure. + * + * Modified 1997/07/27 & 1999/01/27 Tim Janik + * added `-p' option to display specific process ids. + * process sorting is by default disabled in this case. + * added `N' and `A' keys to sort the tasks Numerically by pid or + * sort them by Age (newest first). + * + * Modified 1999/10/22 Tim Janik + * miscellaneous minor fixes, including "usage: ..." output for + * unrecognized options. + * + * Modified 2000/02/07 Jakub Jelinek + * Only load System.map when we are going to display WCHAN. + * Show possible error messages from that load using SHOWMESSAGE. + * + * Modified 2000/07/10 Michael K. Johnson + * Integrated a patch to display SMP information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/sysinfo.h" +#include "proc/procps.h" +#include "proc/whattime.h" +#include "proc/sig.h" +#include "proc/version.h" +#include "proc/readproc.h" +#include "proc/status.h" +#include "proc/devname.h" +#include "proc/compare.h" + +#define PUTP(x) (tputs(x,1,putchar)) +#define BAD_INPUT -30 + +#include "top.h" /* new header for top specific things */ + +static int *cpu_mapping; +static int nr_cpu; + +/*####################################################################### + *#### Startup routines: parse_options, get_options, ############## + *#### setup_terminal and main ############## + *####################################################################### + */ + + /* + * parse the options string as read from the config file(s). + * if top is in secure mode, disallow changing of the delay time between + * screen updates. + */ +static void parse_options(char *Options, int secure) +{ + int i; + for (i = 0; i < strlen(Options); i++) { + switch (Options[i]) { + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!secure) + Sleeptime = (float) Options[i] - '0'; + break; + case 'S': + Cumulative = 1; + headers[22][1] = 'C'; + break; + case 's': + Secure = 1; + break; + case 'i': + Noidle = 1; + break; + case 'm': + show_memory = 0; + header_lines -= 2; + break; + case 'M': + sort_type = S_MEM; + reset_sort_options(); + register_sort_function( -1, (cmp_t)mem_sort); + break; + case 'l': + show_loadav = 0; + header_lines -= 1; + break; + case 'P': + sort_type = S_PCPU; + reset_sort_options(); + register_sort_function( -1, (cmp_t)pcpu_sort); + break; + case 'N': + sort_type = S_NONE; + reset_sort_options(); + break; + case 'A': + sort_type = S_AGE; + reset_sort_options(); + register_sort_function( -1, (cmp_t)age_sort); + break; + case 't': + show_stats = 0; + header_lines -= 2; + break; + case 'T': + sort_type = S_TIME; + reset_sort_options(); + register_sort_function( -1, (cmp_t)time_sort); + break; + case 'c': + show_cmd = 0; + break; + case '\n': + break; + case 'I': + Irixmode = 0; + break; + default: + fprintf(stderr, "Wrong configuration option %c\n", i); + exit(1); + break; + } + } +} + +/* + * Read the configuration file(s). There are two files, once SYS_TOPRC + * which should only contain the secure switch and a sleeptime + * value iff ordinary users are to use top in secure mode only. + * + * The other file is $HOME/RCFILE. + * The configuration file should contain two lines (any of which may be + * empty). The first line specifies the fields that are to be displayed + * in the order you want them to. Uppercase letters specify fields + * displayed by default, lowercase letters specify fields not shown by + * default. The order of the letters in this line corresponds to the + * order of the displayed fileds. + * + * all Options but 'q' can be read from this config file + * The delay time option syntax differs from the commandline syntax: + * only integer values between 2 and 9 seconds are recognized + * (this is for standard configuration, so I think this should do). + * + * usually this file is not edited by hand, but written from top using + * the 'W' command. + */ + +static void get_options(void) +{ + FILE *fp; + char *pt; + char rcfile[MAXNAMELEN]; + char Options[256] = ""; + int i; + + nr_cpu = sysconf (_SC_NPROCESSORS_ONLN); + cpu_mapping = (int *) xmalloc (sizeof (int) * nr_cpu); + /* read cpuname */ + for (i=0; i< nr_cpu; i++) cpu_mapping[i]=i; + header_lines = 6 + nr_cpu; + strcpy(rcfile, SYS_TOPRC); + fp = fopen(rcfile, "r"); + if (fp != NULL) { + fgets(Options, 254, fp); + fclose(fp); + } + parse_options(Options, 0); + strcpy(Options, ""); + if (getenv("HOME")) { + strcpy(rcfile, getenv("HOME")); + strcat(rcfile, "/"); + } + strcat(rcfile, RCFILE); + fp = fopen(rcfile, "r"); + if (fp == NULL) { + strcpy(Fields, DEFAULT_SHOW); + } else { + if (fgets(Fields, 254, fp) != NULL) { + pt = strchr(Fields, '\n'); + if (pt) *pt = 0; + } + fgets(Options, 254, fp); + fclose(fp); + } + parse_options(Options, getuid()? Secure : 0); +} + +/* + * Set up the terminal attributes. + */ +static void setup_terminal(void) +{ + char *termtype; + struct termios newtty; + if (!Batch) + termtype = getenv("TERM"); + else + termtype = "dumb"; + if (!termtype) { + /* In theory, $TERM should never not be set, but in practice, + some gettys don't. Fortunately, vt100 is nearly always + correct (or pretty close). */ + termtype = "VT100"; + /* fprintf(stderr, PROGNAME ": $TERM not set\n"); */ + /* exit(1); */ + } + + /* + * Get termcap entries and window size. + */ + if(tgetent(NULL, termtype) != 1) { + fprintf(stderr, PROGNAME ": Unknown terminal \"%s\" in $TERM\n", + termtype); + exit(1); + } + + cm = tgetstr("cm", 0); + top_clrtobot = tgetstr("cd", 0); + cl = tgetstr("cl", 0); + top_clrtoeol = tgetstr("ce", 0); + ho = tgetstr("ho", 0); + md = tgetstr("md", 0); + mr = tgetstr("mr", 0); + me = tgetstr("me", 0); + + + if (Batch) return; /* the rest doesn't apply to batch mode */ + if (tcgetattr(0, &Savetty) == -1) { + perror(PROGNAME ": tcgetattr() failed"); + error_end(errno); + } + newtty = Savetty; + newtty.c_lflag &= ~ICANON; + newtty.c_lflag &= ~ECHO; + newtty.c_cc[VMIN] = 1; + newtty.c_cc[VTIME] = 0; + if (tcsetattr(0, TCSAFLUSH, &newtty) == -1) { + printf("cannot put tty into raw mode\n"); + error_end(1); + } + tcgetattr(0, &Rawtty); +} + +static int parseint(const char *src, const char *err) +{ + char *endp; + int num; + int len; + num = strtol(src, &endp, 0); + if (*endp == '\0') return num; + /* also accept prefixes of: infinite, infinity, maximum, all */ + len = strlen(src); + if(len<1) goto fail; + if(len<9 && !strncmp(src,"infinite",len)) return INT_MAX; + if(len<9 && !strncmp(src,"infinity",len)) return INT_MAX; + if(len<8 && !strncmp(src,"maximum" ,len)) return INT_MAX; + if(len<4 && !strncmp(src,"all" ,len)) return INT_MAX; +fail: + fprintf(stderr, err, src); + exit(1); +} + +static double parseflt(const char *src, const char *err) +{ + char *endp; + double num; + int len; + num = strtod(src, &endp); + if (*endp == '\0') return num; + /* also accept prefixes of: infinite, infinity, maximum, all */ + len = strlen(src); + if(len<1) goto fail; + if(len<9 && !strncmp(src,"infinite",len)) return (double)INT_MAX; + if(len<9 && !strncmp(src,"infinity",len)) return (double)INT_MAX; + if(len<8 && !strncmp(src,"maximum" ,len)) return (double)INT_MAX; + if(len<4 && !strncmp(src,"all" ,len)) return (double)INT_MAX; +fail: + fprintf(stderr, err, src); + exit(1); +} + +int main(int argc, char **argv) +{ + /* For select(2). */ + struct timeval tv; + fd_set in; + /* For parsing arguments. */ + char *cp; + /* The key read in. */ + char c; + + struct sigaction sact; + + setlocale(LC_ALL, ""); + get_options(); + + /* set to PCPU sorting */ + register_sort_function( -1, (cmp_t)pcpu_sort); + + /* + * Parse arguments. + */ + (void)argc; + argv++; + while (*argv) { + cp = *argv++; + while (*cp) { + switch (*cp) { + case 'd': + if (cp[1]) { + Sleeptime = parseflt(++cp, PROGNAME ": Bad delay time %s'\n"); + goto breakargv; + } else if (*argv) { /* last char in an argv, use next as arg */ + Sleeptime = parseflt(cp = *argv++, PROGNAME ": Bad delay time %s'\n"); + goto breakargv; + } else { + fprintf(stderr, "-d requires an argument\n"); + exit(1); + } + break; + case 'n': + if (cp[1]) { + Loops = parseint(++cp, PROGNAME ": Bad value %s'\n"); + goto breakargv; + } else if (*argv) { /* last char in an argv, use next as arg */ + Loops = parseint(cp = *argv++, PROGNAME ": Bad value %s'\n"); + goto breakargv; + } + break; + + case 'q': + if (!getuid()) + /* set priority to -10 in order to stay above kswapd */ + if (setpriority(PRIO_PROCESS, getpid(), -10)) { + /* We check this just for paranoia. It's not + fatal, and shouldn't happen. */ + perror(PROGNAME ": setpriority() failed"); + } + Sleeptime = 0; + break; + case 'p': + if (monpids_index >= monpids_max) { + fprintf(stderr, PROGNAME ": More than %u process ids specified\n", + monpids_max); + exit(1); + } + if (cp[1]) { + if (sscanf(++cp, "%d", &monpids[monpids_index]) != 1 || + monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) { + fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp); + exit(1); + } + } else if (*argv) { /* last char in an argv, use next as arg */ + if (sscanf(cp = *argv++, "%d", &monpids[monpids_index]) != 1 || + monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) { + fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp); + exit(1); + } + } else { + fprintf(stderr, "-p requires an argument\n"); + exit(1); + } + if (!monpids[monpids_index]) + monpids[monpids_index] = getpid(); + /* default to no sorting when monitoring process ids */ + if (!monpids_index++) { + sort_type = S_NONE; + reset_sort_options(); + } + cp = "_"; + break; + case 'b': + Batch = 1; + break; + case 'c': + show_cmd = !show_cmd; + break; + case 'S': + Cumulative = 1; + break; + case 'i': + Noidle = 1; + break; + case 's': + Secure = 1; + break; + case 'C': + CPU_states = 1; + break; + case '-': + break; /* Just ignore it */ + case 'v': + case 'V': + fprintf(stdout, "top (%s)\n", procps_version); + exit(0); + case 'h': + fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n"); + exit(0); + default: + fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp); + fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n"); + exit(1); + } + cp++; + } + breakargv: + } + + if (nr_cpu > 1 && CPU_states) + header_lines++; + + meminfo(); /* need kb_main_total value filled in */ + + setup_terminal(); + window_size(0); + /* + * Set up signal handlers. + */ + sact.sa_handler = end; + sact.sa_flags = 0; + sigemptyset(&sact.sa_mask); + sigaction(SIGHUP, &sact, NULL); + sigaction(SIGINT, &sact, NULL); + sigaction(SIGQUIT, &sact, NULL); + sact.sa_handler = stop; + sact.sa_flags = SA_RESTART; + sigaction(SIGTSTP, &sact, NULL); + sact.sa_handler = window_size; + sigaction(SIGWINCH, &sact, NULL); + sigaction(SIGCONT, &sact, NULL); + + /* loop, collecting process info and sleeping */ + while (1) { + if (Loops > 0) + Loops--; + /* display the tasks */ + show_procs(); + /* sleep & wait for keyboard input */ + if (Loops == 0) + end(0); + if (!Batch) + { + tv.tv_sec = Sleeptime; + tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000; + FD_ZERO(&in); + FD_SET(0, &in); + if (select(1, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1) + do_key(c); + } else { + sleep(Sleeptime); + } + } +} + +/*####################################################################### + *#### Signal handled routines: error_end, end, stop, window_size ### + *#### Small utilities: make_header, getstr, getint, getfloat, getsig ### + *####################################################################### + */ + + + /* + * end when exiting with an error. + */ +static void error_end(int rno) +{ + if (!Batch) + tcsetattr(0, TCSAFLUSH, &Savetty); + PUTP(tgoto(cm, 0, Lines - 1)); + fputs("\r\n", stdout); + exit(rno); +} +/* + * Normal end of execution. + */ +static void end(int signo) +{ + (void)signo; + if (!Batch) + tcsetattr(0, TCSAFLUSH, &Savetty); + PUTP(tgoto(cm, 0, Lines - 1)); + fputs("\r\n", stdout); + exit(0); +} + +/* + * SIGTSTP catcher. + */ +static void stop(int signo) +{ + (void)signo; + /* Reset terminal. */ + if (!Batch) + tcsetattr(0, TCSAFLUSH, &Savetty); + PUTP(tgoto(cm, 0, Lines - 3)); + fflush(stdout); + raise(SIGSTOP); + /* Later... */ + if (!Batch) + tcsetattr (0, TCSAFLUSH, &Rawtty); +} + +/* + * Reads the window size and clear the window. This is called on setup, + * and also catches SIGWINCHs, and adjusts Maxlines. Basically, this is + * the central place for window size stuff. + */ +static void window_size(int signo) +{ + struct winsize ws; + (void)signo; + if((ioctl(1, TIOCGWINSZ, &ws) != -1) && (ws.ws_col>73) && (ws.ws_row>7)){ + Cols = ws.ws_col; + Lines = ws.ws_row; + }else{ + Cols = tgetnum("co"); + Lines = tgetnum("li"); + } + if (!Batch) + clear_screen(); + /* + * calculate header size, length of cmdline field ... + */ + Numfields = make_header(); +} + +/* + * this prints a possible message from open_psdb_message + */ +static void top_message(const char *format, ...) { + va_list arg; + int n; + char buffer[512]; + + va_start (arg, format); + n = vsnprintf (buffer, 512, format, arg); + va_end (arg); + if (n > -1 && n < 512) + SHOWMESSAGE(("%s", buffer)); +} + +/* + * this adjusts the lines needed for the header to the current value + */ +static int make_header(void) +{ + int i, j; + + j = 0; + for (i = 0; i < strlen(Fields); i++) { + if (Fields[i] < 'a') { + pflags[j++] = Fields[i] - 'A'; + if (Fields[i] == 'U' && CL_wchan_nout == -1) { + CL_wchan_nout = 0; + /* for correct handling of WCHAN fields, we have to do distingu + * between kernel versions */ + /* get kernel symbol table, if needed */ + if (open_psdb_message(NULL, top_message)) { + CL_wchan_nout = 1; + } else { + psdbsucc = 1; + } + } + } + } + strcpy(Header, ""); + for (i = 0; i < j; i++) + strcat(Header, headers[pflags[i]]); + /* readjust window size ... */ + Maxcmd = Cols - strlen(Header) + 7; + Maxlines = Display_procs ? Display_procs : Lines - header_lines; + if (Maxlines > Lines - header_lines) + Maxlines = Lines - header_lines; + return (j); +} + + + +/* + * Get a string from the user; the base of getint(), et al. This really + * ought to handle long input lines and errors better. NB: The pointer + * returned is a statically allocated buffer, so don't expect it to + * persist between calls. + */ +static char *getstr(void) +{ + static char line[BUFSIZ]; /* BUFSIZ from ; arbitrary */ + int i = 0; + + /* Must make sure that buffered IO doesn't kill us. */ + fflush(stdout); + fflush(stdin); /* Not POSIX but ok */ + + do { + read(STDIN_FILENO, &line[i], 1); + } while (line[i++] != '\n' && i < sizeof(line)); + line[--i] = 0; + + return (line); +} + + +/* + * Get an integer from the user. Display an error message and + * return BAD_INPUT if it's invalid; else return the number. + */ +static int getint(void) +{ + char *line; + int i; + int r; + + line = getstr(); + + for (i = 0; line[i]; i++) { + if (!isdigit(line[i]) && line[i] != '-') { + SHOWMESSAGE(("That's not a number!")); + return (BAD_INPUT); + } + } + + /* An empty line is a legal error (hah!). */ + if (!line[0]) + return (BAD_INPUT); + + sscanf(line, "%d", &r); + return (r); +} + + +/* + * Get a float from the user. Just like getint(). + */ +static float getfloat(void) +{ + char *line; + int i; + float r; + + line = getstr(); + + for (i = 0; line[i]; i++) { + if (!isdigit(line[i]) && line[i] != '.' && line[i] != '-') { + SHOWMESSAGE(("That's not a float!")); + return (BAD_INPUT); + } + } + + /* An empty line is a legal error (hah!). */ + if (!line[0]) + return (BAD_INPUT); + + sscanf(line, "%f", &r); + return (r); +} + + +/* + * Get a signal number or name from the user. Return the number, or -1 + * on error. + */ +static int getsig(void) +{ + char *line; + + /* This is easy. */ + line = getstr(); + return signal_name_to_number(line); +} + +/*####################################################################### + *#### Routine for sorting on used time, resident memory and %CPU ##### + *#### It would be easy to include full sorting capability as in ##### + *#### ps, but I think there is no real use for something that ##### + *#### complicated. Using register_sort_function or parse_sort_opt ##### + *#### you just have to do the natural thing and it will work. ##### + *####################################################################### + */ + +static int time_sort (proc_t **P, proc_t **Q) +{ + if (Cumulative) { + if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) < + ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return -1; + if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) > + ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return 1; + } else { + if( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime)) + return -1; + if( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime)) + return 1; + } + return 0; +} + +static int pcpu_sort (proc_t **P, proc_t **Q) +{ + if( (*P)->pcpu < (*Q)->pcpu ) return -1; + if( (*P)->pcpu > (*Q)->pcpu ) return 1; + return 0; +} + +static int mem_sort (proc_t **P, proc_t **Q) +{ + if( (*P)->vm_rss < (*Q)->vm_rss ) return -1; + if( (*P)->vm_rss > (*Q)->vm_rss ) return 1; + return 0; +} + +int age_sort (proc_t **P, proc_t **Q) +{ + if( (*P)->start_time < (*Q)->start_time ) return -1; + if( (*P)->start_time > (*Q)->start_time ) return 1; + return 0; +} + +/*####################################################################### + *#### Routines handling the field selection/ordering screens: ######## + *#### show_fields, change_order, change_fields ######## + *####################################################################### + */ + + /* + * Display the specification line of all fields. Upper case indicates + * a displayed field, display order is according to the order of the + * letters. A short description of each field is shown as well. + * The description of a displayed field is marked by a leading + * asterisk (*). + */ +static void show_fields(void) +{ + int i, row, col; + char *p; + + clear_screen(); + PUTP(tgoto(cm, 3, 0)); + printf("Current Field Order: %s\n", Fields); + for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) { + row = i % (Lines - 3) + 3; + col = i / (Lines - 3) * 40; + PUTP(tgoto(cm, col, row)); + for (p = headers[i]; *p == ' '; ++p); + printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A', + p, headers2[i]); + } +} + +/* + * change order of displayed fields + */ +static void change_order(void) +{ + char c, ch, *p; + int i; + + show_fields(); + for (;;) { + PUTP(tgoto(cm, 0, 0)); + PUTP(top_clrtoeol); + PUTP(tgoto(cm, 3, 0)); + PUTP(mr); + printf("Current Field Order: %s", Fields); + PUTP(me); + putchar('\n'); + PUTP(tgoto(cm, 0, 1)); + printf("Upper case characters move a field to the left, lower case to the right"); + fflush(stdout); + if (!Batch) { /* should always be true, but... */ + tcsetattr(0, TCSAFLUSH, &Rawtty); + read(0, &c, 1); + tcsetattr(0, TCSAFLUSH, &Savetty); + } + i = toupper(c) - 'A'; + if ((p = strchr(Fields, i + 'A')) != NULL) { + if (isupper(c)) + p--; + if ((p[1] != '\0') && (p >= Fields)) { + ch = p[0]; + p[0] = p[1]; + p[1] = ch; + } + } else if ((p = strchr(Fields, i + 'a')) != NULL) { + if (isupper(c)) + p--; + if ((p[1] != '\0') && (p >= Fields)) { + ch = p[0]; + p[0] = p[1]; + p[1] = ch; + } + } else { + break; + } + } + Numfields = make_header(); +} +/* + * toggle displayed fields + */ +static void change_fields(void) +{ + int i, changed = 0; + int row, col; + char c, *p; + char tmp[2] = " "; + + show_fields(); + for (;;) { + PUTP(tgoto(cm, 0, 0)); + PUTP(top_clrtoeol); + PUTP(tgoto(cm, 3, 0)); + PUTP(mr); + printf("Current Field Order: %s", Fields); + PUTP(me); + putchar('\n'); + PUTP(tgoto(cm, 0, 1)); + if (!Batch) { /* should always be true, but... */ + printf("Toggle fields with a-x, any other key to return: "); + fflush(stdout); + tcsetattr(0, TCSAFLUSH, &Rawtty); + read(0, &c, 1); + tcsetattr(0, TCSAFLUSH, &Savetty); + } + i = toupper(c) - 'A'; + if (i >= 0 && i < sizeof headers / sizeof headers[0]) { + row = i % (Lines - 3) + 3; + col = i / (Lines - 3) * 40; + PUTP(tgoto(cm, col, row)); + if ((p = strchr(Fields, i + 'A')) != NULL) { /* deselect Field */ + *p = i + 'a'; + putchar(' '); + } else if ((p = strchr(Fields, i + 'a')) != NULL) { /* select previously */ + *p = i + 'A'; /* deselected field */ + putchar('*'); + } else { /* select new field */ + tmp[0] = i + 'A'; + strcat(Fields, tmp); + putchar('*'); + } + changed = 1; + fflush(stdout); + } else + break; + } + if (changed) + Numfields = make_header(); +} + +/* Do the scaling stuff: interprets time in seconds, formats it to + * fit width, and returns pointer to static char*. + */ +static char *scale_time(int t,int width) +{ + static char buf[100]; + + /* Try successively higher units until it fits */ + + sprintf(buf,"%d:%02d",t/60,t%60); /* minutes:seconds */ + if (strlen(buf)<=width) + return buf; + + t/=60; /* minutes */ + sprintf(buf,"%dm",t); + if (strlen(buf)<=width) + return buf; + + t/=60; /* hours */ + sprintf(buf,"%dh",t); + if (strlen(buf)<=width) + return buf; + + t/=24; /* days */ + sprintf(buf,"%dd",t); + if (strlen(buf)<=width) + return buf; + + t/=7; /* weeks */ + sprintf(buf,"%dw",t); + return buf; /* this is our last try; + if it still doesn't fit, too bad. */ + + /* FIXME: if someone has a 16-way SMP running over a year... */ +} + +/* scale_k(k,width,unit) - interprets k as a count, formats to fit width. + if unit is 0, k is a byte count; 1 is a kilobyte + count; 2 for megabytes; 3 for gigabytes. +*/ + +static char *scale_k(int k,int width,int unit) +{ + /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */ + static double scale[]={1024,1024*1024,1024*1024*1024,0}; + /* kilo, mega, giga, tera */ + static char unitletters[]={'K','M','G','T',0}; + static char buf[100]; + char *up; + double *dp; + + /* Try successively higher units until it fits */ + + sprintf(buf,"%d",k); + if (strlen(buf)<=width) + return buf; + + for (up=unitletters+unit,dp=scale ; *dp ; ++dp,++up) { + sprintf(buf,"%.1f%c",k / *dp,*up); + if (strlen(buf)<=width) + return buf; + sprintf(buf,"%d%c",(int)(k / *dp),*up); + if (strlen(buf)<=width) + return buf; + } + + /* Give up; give them what we got on our shortest attempt */ + return buf; +} + +/* + *####################################################################### + *#### Routines handling the main top screen: ######## + *#### show_task_info, show_procs, show_memory, do_stats ######## + *####################################################################### + */ + /* + * Displays infos for a single task + */ +static void show_task_info(proc_t *task) +{ + int i,j; + unsigned int t; + char *cmdptr; + char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p; + + for (i = 0; i < Numfields; i++) { + tmp[0] = 0; + switch (pflags[i]) { + case P_PID: + sprintf(tmp, "%5d ", task->pid); + break; + case P_PPID: + sprintf(tmp, "%5d ", task->ppid); + break; + case P_EUID: + sprintf(tmp, "%4d ", task->euid); + break; + case P_EUSER: + sprintf(tmp, "%-8.8s ", task->euser); + break; + case P_PCPU: + sprintf(tmp, "%4.1f ", (float)task->pcpu / 10); + break; + case P_LCPU: + sprintf(tmp, "%2d ", task->processor); + break; + case P_PMEM: { + unsigned pmem; + pmem = task->vm_rss * 1000ULL / kb_main_total; + if (pmem > 999) pmem = 999; + sprintf(tmp, "%2u.%u ", pmem/10U, pmem%10U); + } + break; + case P_TTY: { + char outbuf[9]; + dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV); + sprintf(tmp, "%-8.8s ", outbuf); + } + break; + case P_PRI: + sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0)); + break; + case P_NICE: + sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0)); + break; + case P_PAGEIN: + sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0)); + break; + case P_TSIZ: + sprintf(tmp, "%5.5s ", + scale_k(((task->end_code - task->start_code) / 1024), 5, 1)); + break; + case P_DSIZ: + sprintf(tmp, "%5.5s ", + scale_k(((task->vsize - task->end_code) / 1024), 5, 1)); + break; + case P_SIZE: + sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1)); + break; + case P_TRS: + sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1)); + break; + case P_SWAP: + sprintf(tmp, "%4.4s ", + scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1)); + break; + case P_SHARE: + sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1)); + break; + case P_A: + sprintf(tmp, "%3.3s ", "NYI"); + break; + case P_WP: + sprintf(tmp, "%3.3s ", "NYI"); + break; + case P_DT: + sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0)); + break; + case P_RSS: /* rss, not resident (which includes IO memory) */ + sprintf(tmp, "%4.4s ", + scale_k((task->rss << CL_pg_shift), 4, 1)); + break; + case P_WCHAN: + if (!CL_wchan_nout) + sprintf(tmp, "%-9.9s ", wchan(task->wchan)); + else + sprintf(tmp, "%-9lx", task->wchan); + break; + case P_STAT: + sprintf(tmp, "%-4.4s ", status(task)); + break; + case P_TIME: + t = (task->utime + task->stime) / Hertz; + if (Cumulative) + t += (task->cutime + task->cstime) / Hertz; + sprintf(tmp, "%6.6s ", scale_time(t,6)); + break; + case P_COMMAND: + if (!show_cmd && task->cmdline && *(task->cmdline)) { + j=0; + while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){ +/* #if 0 */ /* This is useless? FIXME */ + if (j > 0) + strcat(tmp3, " "); +/* #endif */ + strncat(tmp3, (task->cmdline)[j], 1000); + j++; + } + cmdptr = tmp3; + } else { + cmdptr = task->cmd; + } + if (strlen(cmdptr) > Maxcmd) + cmdptr[Maxcmd - 1] = 0; + sprintf(tmp, "%s", cmdptr); + tmp3[0]=0; + break; + case P_FLAGS: + sprintf(tmp, "%8lx ", task->flags); + break; + } + strcat(tmp2, tmp); + } + if (strlen(tmp2) > Cols - 1) + tmp2[Cols - 1] = 0; + + /* take care of cases like: + perl -e 'foo + bar foo bar + foo + # end of perl script' + */ + for (p=tmp2;*p;++p) + if (!isgraph(*p)) + *p=' '; + + printf("\n%s", tmp2); + PUTP(top_clrtoeol); +} + +/* + * This is the real program! Read process info and display it. + * One could differentiate options of readproctable2, perhaps it + * would be useful to support the PROC_UID and PROC_TTY + * as command line options. + */ +static void show_procs(void) +{ + static proc_t **p_table=NULL; + static int proc_flags; + int count; + int ActualLines; + float elapsed_time; + static int first=0; + + if (first==0) { + proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR|PROC_FILLSTATUS|PROC_FILLSTAT; + if (monpids_index) + proc_flags |= PROC_PID; + p_table=readproctab2(proc_flags, p_table, monpids); + elapsed_time = get_elapsed_time(); + do_stats(p_table, elapsed_time, 0); + sleep(1); + first=1; + } + if (first && Batch) + fputs("\n\n",stdout); + /* Display the load averages. */ + PUTP(ho); + PUTP(md); + if (show_loadav) { + printf("%s", sprint_uptime()); + PUTP(top_clrtoeol); + putchar('\n'); + } + p_table=readproctab2(proc_flags, p_table, monpids); + /* Immediately find out the elapsed time for the frame. */ + elapsed_time = get_elapsed_time(); + /* Display the system stats, calculate percent CPU time + * and sort the list. */ + do_stats(p_table, elapsed_time,1); + /* Display the memory and swap space usage. */ + show_meminfo(); + if (strlen(Header) + 2 > Cols) + Header[Cols - 2] = 0; + PUTP(mr); + fputs(Header, stdout); + PUTP(top_clrtoeol); + PUTP(me); + + /* + * Finally! Loop through to find the top task, and display it. + * Lather, rinse, repeat. + */ + count = 0; + ActualLines = 0; + while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) { + char Stat; + + Stat = p_table[count]->state; + + if ( (!Noidle || (Stat != 'S' && Stat != 'Z')) && + ( (CurrUser[0] == '\0') || + (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) { + + /* + * Show task info. + */ + show_task_info(p_table[count]); + if (!Batch) + ActualLines++; + } + count++; + } + PUTP(top_clrtobot); + PUTP(tgoto(cm, 0, header_lines - 2)); + fflush(stdout); +} + + +/* + * Finds the current time (in microseconds) and calculates the time + * elapsed since the last update. This is essential for computing + * percent CPU usage. + */ +static float get_elapsed_time(void) +{ + struct timeval t; + static struct timeval oldtime; + struct timezone timez; + float elapsed_time; + + gettimeofday(&t, &timez); + elapsed_time = (t.tv_sec - oldtime.tv_sec) + + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0; + oldtime.tv_sec = t.tv_sec; + oldtime.tv_usec = t.tv_usec; + return (elapsed_time); +} + +/* + * Reads the memory info and displays it. Returns the total memory + * available, for use in percent memory usage calculations. + */ +static void show_meminfo(void) +{ + meminfo(); /* read+parse /proc/meminfo */ + if (show_memory) { + printf( + "Mem: %8dK total, %8dK used, %8dK free, %8dK buffers", + kb_main_total, + kb_main_used, + kb_main_free, + kb_main_buffers + ); + PUTP(top_clrtoeol); + putchar('\n'); + printf( + "Swap: %8dK total, %8dK used, %8dK free, %8dK cached", + kb_swap_total, + kb_swap_used, + kb_swap_free, + kb_main_cached + ); + PUTP(top_clrtoeol); + putchar('\n'); + } + PUTP(me); + PUTP(top_clrtoeol); + putchar('\n'); +} + + +/* + * Calculates the number of tasks in each state (running, sleeping, etc.). + * Calculates the CPU time in each state (system, user, nice, etc). + * Calculates percent cpu usage for each task. + */ +static void do_stats(proc_t** p, float elapsed_time, int pass) +{ + proc_t *this; + int arrindex, total_time, cpumap, i, n = 0; + int sleeping = 0, stopped = 0, zombie = 0, running = 0; + double system_ticks, user_ticks, nice_ticks, idle_ticks; + static int prev_count = 0; + int systime, usrtime; + + /* start with one page as a reasonable allocate size */ + static int save_history_size = + sizeof(long)*1024 / sizeof(struct save_hist); + static struct save_hist *save_history; + struct save_hist *New_save_hist; + + if (!save_history) + save_history = xcalloc(NULL, sizeof(struct save_hist)*save_history_size); + New_save_hist = xcalloc(NULL, sizeof(struct save_hist)*save_history_size); + + /* + * Make a pass through the data to get stats. + */ + arrindex = 0; + while (p[n]->pid != -1) { + this = p[n]; + switch (this->state) { + case 'S': + case 'D': + sleeping++; + break; + case 'T': + stopped++; + break; + case 'Z': + zombie++; + break; + case 'R': + running++; + break; + default: + /* Don't know how to handle this one. */ + break; + } + + /* + * Calculate time in this process. Time is sum of user time + * (usrtime) plus system time (systime). + */ + total_time = this->utime + this->stime; + if (arrindex >= save_history_size) { + save_history_size *= 2; + save_history = xrealloc(save_history, sizeof(struct save_hist)*save_history_size); + New_save_hist = xrealloc(New_save_hist, sizeof(struct save_hist)*save_history_size); + } + New_save_hist[arrindex].ticks = total_time; + New_save_hist[arrindex].pid = this->pid; + systime = this->stime; + usrtime = this->utime; + New_save_hist[arrindex].stime = systime; + New_save_hist[arrindex].utime = usrtime; + + /* find matching entry from previous pass */ + for (i = 0; i < prev_count; i++) { + if (save_history[i].pid == this->pid) { + total_time -= save_history[i].ticks; + systime -= save_history[i].stime; + usrtime -= save_history[i].utime; + + i = prev_count; + } + } + + /* + * Calculate percent cpu time for this task. + */ + this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time; + if (this->pcpu > 999) + this->pcpu = 999; + + arrindex++; + n++; + } + + /* + * Display stats. + */ + if (pass > 0 && show_stats) { + printf("%d processes: %d sleeping, %d running, %d zombie, " + "%d stopped", + n, sleeping, running, zombie, stopped); + PUTP(top_clrtoeol); + putchar('\n'); + four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks); + printf("CPU states:" + " %# 5.1f%% user, %# 5.1f%% system," + " %# 5.1f%% nice, %# 5.1f%% idle", + user_ticks, + system_ticks, + nice_ticks, + idle_ticks + ); + PUTP(top_clrtoeol); + putchar('\n'); + } + /* + * Save this frame's information. + */ + for (i = 0; i < n; i++) { + /* copy the relevant info for the next pass */ + save_history[i].pid = New_save_hist[i].pid; + save_history[i].ticks = New_save_hist[i].ticks; + save_history[i].stime = New_save_hist[i].stime; + save_history[i].utime = New_save_hist[i].utime; + } + free(New_save_hist); + + prev_count = n; + qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp); +} + + +/* + * Process keyboard input during the main loop + */ +static void do_key(char c) +{ + int numinput, i; + char rcfile[MAXNAMELEN]; + FILE *fp; + + /* + * First the commands which don't require a terminal mode switch. + */ + if (c == 'q') + end(0); + else if (c == ' ') + return; + else if (c == 12) { + clear_screen(); + return; + } else if (c == 'I') { + Irixmode=(Irixmode) ? 0 : 1; + return; + } + + /* + * Switch the terminal to normal mode. (Will the original + * attributes always be normal? Does it matter? I suppose the + * shell will be set up the way the user wants it.) + */ + if (!Batch) tcsetattr(0, TCSANOW, &Savetty); + + /* + * Handle the rest of the commands. + */ + switch (c) { + case '?': + case 'h': + PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr); + printf("Proc-Top Revision 1.2"); + PUTP(me); putchar('\n'); + printf("Secure mode "); + PUTP(md); + fputs(Secure ? "on" : "off", stdout); + PUTP(me); + fputs("; cumulative mode ", stdout); + PUTP(md); + fputs(Cumulative ? "on" : "off", stdout); + PUTP(me); + fputs("; noidle mode ", stdout); + PUTP(md); + fputs(Noidle ? "on" : "off", stdout); + PUTP(me); + fputs("\n\n", stdout); + printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN); + if (!Batch) tcsetattr(0, TCSANOW, &Rawtty); + (void) getchar(); + break; + case 'i': + Noidle = !Noidle; + SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off")); + break; + case 'u': + SHOWMESSAGE(("Which User (Blank for All): ")); + strcpy(CurrUser,getstr()); + break; + case 'k': + if (Secure) + SHOWMESSAGE(("\aCan't kill in secure mode")); + else { + int pid, signo; + PUTP(md); + SHOWMESSAGE(("PID to kill: ")); + pid = getint(); + if (pid == BAD_INPUT) + break; + PUTP(top_clrtoeol); + SHOWMESSAGE(("Kill process %d with what signal? [15] ", pid)); + PUTP(me); + signo = getsig(); + /* FIXME: -1 may mean an unknown signal */ + if (signo == -1) + signo = SIGTERM; + if (kill(pid, signo)) + SHOWMESSAGE(("\aKill of PID %d with %d failed: %s", + pid, signo, strerror(errno))); + } + break; + case 'l': + SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off")); + if (show_loadav) { + show_loadav = 0; + header_lines--; + } else { + show_loadav = 1; + header_lines++; + } + Numfields = make_header(); + break; + case 'm': + SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off")); + if (show_memory) { + show_memory = 0; + header_lines -= 2; + } else { + show_memory = 1; + header_lines += 2; + } + Numfields = make_header(); + break; + case 'M': + SHOWMESSAGE(("Sort by memory usage")); + sort_type = S_MEM; + reset_sort_options(); + register_sort_function(-1, (cmp_t)mem_sort); + break; + case 'n': + case '#': + printf("Processes to display (0 for unlimited): "); + numinput = getint(); + if (numinput != -1) { + Display_procs = numinput; + window_size(0); + } + break; + case 'r': + if (Secure) + SHOWMESSAGE(("\aCan't renice in secure mode")); + else { + int pid, val; + + printf("PID to renice: "); + pid = getint(); + if (pid == BAD_INPUT) + break; + PUTP(tgoto(cm, 0, header_lines - 2)); + PUTP(top_clrtoeol); + printf("Renice PID %d to value: ", pid); + val = getint(); + if (val == BAD_INPUT) + val = 10; + if (setpriority(PRIO_PROCESS, pid, val)) + SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s", + pid, val, strerror(errno))); + } + break; + case 'P': + SHOWMESSAGE(("Sort by CPU usage")); + sort_type = S_PCPU; + reset_sort_options(); + register_sort_function(-1, (cmp_t)pcpu_sort); + break; + case 'A': + SHOWMESSAGE(("Sort by age")); + sort_type = S_AGE; + reset_sort_options(); + register_sort_function(-1, (cmp_t)age_sort); + break; + case 'N': + SHOWMESSAGE(("Sort numerically by pid")); + sort_type = S_NONE; + reset_sort_options(); + break; + case 'c': + show_cmd = !show_cmd; + SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line")); + break; + case 'S': + Cumulative = !Cumulative; + SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off")); + if (Cumulative) + headers[22][1] = 'C'; + else + headers[22][1] = ' '; + Numfields = make_header(); + break; + case 's': + if (Secure) + SHOWMESSAGE(("\aCan't change delay in secure mode")); + else { + double tmp; + printf("Delay between updates: "); + tmp = getfloat(); + if (!(tmp < 0)) + Sleeptime = tmp; + } + break; + case 't': + SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off")); + if (show_stats) { + show_stats = 0; + header_lines -= 2; + } else { + show_stats = 1; + header_lines += 2; + } + Numfields = make_header(); + break; + case 'T': + SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : "")); + sort_type = S_TIME; + reset_sort_options(); + register_sort_function( -1, (cmp_t)time_sort); + break; + case 'f': + case 'F': + change_fields(); + break; + case 'o': + case 'O': + change_order(); + break; + case 'W': + if (getenv("HOME")) { + strcpy(rcfile, getenv("HOME")); + strcat(rcfile, "/"); + strcat(rcfile, RCFILE); + fp = fopen(rcfile, "w"); + if (fp != NULL) { + fprintf(fp, "%s\n", Fields); + i = (int) Sleeptime; + if (i < 2) + i = 2; + if (i > 9) + i = 9; + fprintf(fp, "%d", i); + if (Secure) + fprintf(fp, "%c", 's'); + if (Cumulative) + fprintf(fp, "%c", 'S'); + if (!show_cmd) + fprintf(fp, "%c", 'c'); + if (Noidle) + fprintf(fp, "%c", 'i'); + if (!show_memory) + fprintf(fp, "%c", 'm'); + if (!show_loadav) + fprintf(fp, "%c", 'l'); + if (!show_stats) + fprintf(fp, "%c", 't'); + if (!Irixmode) + fprintf(fp, "%c", 'I'); + fprintf(fp, "\n"); + fclose(fp); + SHOWMESSAGE(("Wrote configuration to %s", rcfile)); + } else { + SHOWMESSAGE(("Couldn't open %s", rcfile)); + } + } else { + SHOWMESSAGE(("Couldn't get $HOME -- not saving")); + } + break; + default: + SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c)); + } + + /* + * Return to raw mode. + */ + if (!Batch) tcsetattr(0, TCSANOW, &Rawtty); + return; +} + + +/*##################################################################### + *####### A readproctable function that uses already allocated ##### + *####### table entries. ##### + *##################################################################### + */ +#define Do(x) (flags & PROC_ ## x) + +static proc_t** readproctab2(int flags, proc_t** tab, ...) { + PROCTAB* PT = NULL; + static proc_t *buff; + int n = 0; + static int len = 0; + va_list ap; + + va_start(ap, tab); /* pass through args to openproc */ + if (Do(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 (Do(PID)) { + PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */ + /* work around a bug in openproc() */ + PT->procfs = NULL; + /* share some process time, since we skipped opendir("/proc") */ + usleep (50*1000); + } + else if (Do(TTY) || Do(STAT)) + PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */ + else + PT = openproc(flags); + va_end(ap); + buff = (proc_t *) 1; + while (ncmdline) { + free((void*)*tab[n]->cmdline); + tab[n]->cmdline = NULL; + } + buff = readproc(PT, tab[n]); + if (buff) n++; + } + if (buff) { + do { /* (ii) not yet allocated chunks */ + tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */ + buff = readproc(PT, NULL); /* final null to terminate */ + if(buff) tab[n]=buff; + len++; + n++; + } while (buff); /* stop when NULL reached */ + tab[n-1] = xcalloc(NULL, sizeof (proc_t)); + tab[n-1]->pid=-1; /* Mark end of Table */ + } else { + if (n == len) { + tab = xrealloc(tab, (n+1)*sizeof(proc_t*)); + tab[n] = xcalloc(NULL, sizeof (proc_t)); + len++; + } + tab[n]->pid=-1; /* Use this instead of NULL when not at the end of */ + } /* the allocated space */ + closeproc(PT); + return tab; +} diff --git a/top.desktop b/top.desktop new file mode 100644 index 00000000..cf1c1917 --- /dev/null +++ b/top.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Top +Type=Application +Comment=Repeatedly display processes by CPU time, memory usage, etc. +Exec=top +Terminal=true diff --git a/top.h b/top.h new file mode 100644 index 00000000..e7cc2e2e --- /dev/null +++ b/top.h @@ -0,0 +1,228 @@ +/* + * top.h header file 1996/05/18, + * + * function prototypes, global data definitions and string constants. + */ + +static proc_t** readproctab2(int flags, proc_t** tab, ...); +static void parse_options(char *Options, int secure); +static void get_options(void); +static void error_end(int rno); +static void end(int signo); +static void stop(int signo); +static void window_size(int signo); +static int make_header(void); +static char *getstr(void); +static int getsig(void); +static float getfloat(void); +static int time_sort(proc_t **P, proc_t **Q); +static int pcpu_sort(proc_t **P, proc_t **Q); +static int mem_sort(proc_t **P, proc_t **Q); +static int age_sort(proc_t **P, proc_t **Q); +static void show_fields(void); +static void change_order(void); +static void change_fields(void); +static void show_task_info(proc_t *task); +static void show_procs(void); +static float get_elapsed_time(void); +static void show_meminfo(void); +static void do_stats(proc_t** p, float elapsed_time, int pass); +static void do_key(char c); + + +/* configurable field display support */ + +static int pflags[30]; +static int Numfields; + + + /* Name of the config file (in $HOME) */ +#ifndef RCFILE +#define RCFILE ".toprc" +#endif + +#ifndef SYS_TOPRC +#define SYS_TOPRC "/etc/toprc" +#endif + +#define MAXLINES 2048 +#define MAXNAMELEN 1024 + +/* this is what procps top does by default, so let's do this, if nothing is + * specified + */ +#ifndef DEFAULT_SHOW +/* 0 1 2 3 */ +/* 0123456789012345678901234567890 */ +#define DEFAULT_SHOW "AbcDgHIjklMnoTP|qrsuzyV{EFWX" +#endif +static char Fields[256] = ""; + + +/* This structure stores some critical information from one frame to + the next. mostly used for sorting. Added cumulative and resident fields. */ +struct save_hist { + int ticks; + int pid; + int pcpu; + int utime; + int stime; +}; + + /* The original terminal attributes. */ +static struct termios Savetty; + /* The new terminal attributes. */ +static struct termios Rawtty; + /* Cached termcap entries. */ +static char *cm, *cl, *top_clrtobot, *top_clrtoeol, *ho, *md, *me, *mr; + /* Current window size. Note that it is legal to set Display_procs + larger than can fit; if the window is later resized, all will be ok. + In other words: Display_procs is the specified max number of + processes to display (zero for infinite), and Maxlines is the actual + number. */ +static int Lines, Cols, Maxlines, Display_procs; + /* Maximum length to display of the command line of a process. */ +static unsigned Maxcmd; + + /* Controls how long we sleep between screen updates. Accurate to + microseconds. */ +static float Sleeptime = 5; + /* for opening/closing the system map */ +static int psdbsucc = 0; + /* Mode flags. */ +static int Irixmode = 1; +static int Secure = 0; +static int Cumulative = 0; +static int Noidle = 0; + +static int CPU_states = 0; +static char CurrUser[BUFSIZ]; + +static int CL_pg_shift = (PAGE_SHIFT - 10); +static int CL_wchan_nout = -1; + +static int show_stats = 1; /* show status summary */ +static int show_memory = 1; /* show memory summary */ +static int show_loadav = 1; /* show load average and uptime */ +static int show_cmd = 1; /* show command name instead of commandline */ + +static pid_t monpids[520]; /* randomly chosen value */ +static const int monpids_max = sizeof(monpids)/sizeof(pid_t); +static int monpids_index = 0; + +static int Loops = -1; /* number of iterations. -1 loops forever */ +static int Batch = 0; /* batch mode. Collect no input, dumb output */ + +/* sorting order: cpu%, mem, time (cumulative, if in cumulative mode) */ +enum { + S_PCPU, S_MEM, S_TIME, S_AGE, S_NONE +}; +/* default sorting by CPU% */ +static int sort_type = S_PCPU; + +/* flags for each possible field. At the moment up to 30 are supported */ +enum { + P_PID, P_PPID, P_EUID, P_EUSER, + P_PCPU, P_PMEM, P_TTY, P_PRI, + P_NICE, P_PAGEIN, P_TSIZ, P_DSIZ, + P_SIZE, P_TRS, P_SWAP, P_SHARE, + P_A, P_WP, P_DT, P_RSS, + P_WCHAN, P_STAT, P_TIME, P_COMMAND, + P_LCPU, P_FLAGS, P_END +}; +/* corresponding headers */ +static char *headers[] = +{ + " PID ", " PPID ", " UID ", + "USER ", "%CPU ", "%MEM ", + "TTY ", "PRI ", " NI ", + "PAGEIN ", "TSIZE ", "DSIZE ", + " SIZE ", " TRS ", "SWAP ", + "SHARE ", " A ", " WP ", + " D ", " RSS ", "WCHAN ", + "STAT ", " TIME ", "COMMAND", + "LC ", + " FLAGS " +}; +/* corresponding field desciptions */ +static char *headers2[] = +{ + "Process Id", "Parent Process Id", "User Id", + "User Name", "CPU Usage", "Memory Usage", + "Controlling tty", "Priority", "Nice Value", + "Page Fault Count", "Code Size (kb)", "Data+Stack Size (kb)", + "Virtual Image Size (kb)", "Resident Text Size (kb)", "Swapped kb", + "Shared Pages (kb)", "Accessed Page count", "Write Protected Pages", + "Dirty Pages", "Resident Set Size (kb)", "Sleeping in Function", + "Process Status", "CPU Time", "Command", + "Last used CPU (expect this to change regularly)", + "Task Flags (see linux/sched.h)" +}; + + /* The header printed at the top of the process list.*/ +static char Header[MAXLINES]; + + /* The response to the interactive 'h' command. */ +#define HELP_SCREEN "\ +Interactive commands are:\n\ +\n\ +space\tUpdate display\n\ +^L\tRedraw the screen\n\ +fF\tadd and remove fields\n\ +oO\tChange order of displayed fields\n\ +h or ?\tPrint this list\n\ +S\tToggle cumulative mode\n\ +i\tToggle display of idle proceses\n\ +I\tToggle between Irix and Solaris views (SMP-only)\n\ +c\tToggle display of command name/line\n\ +l\tToggle display of load average\n\ +m\tToggle display of memory information\n\ +t\tToggle display of summary information\n\ +k\tKill a task (with any signal)\n\ +r\tRenice a task\n\ +N\tSort by pid (Numerically)\n\ +A\tSort by age\n\ +P\tSort by CPU usage\n\ +M\tSort by resident memory usage\n\ +T\tSort by time / cumulative time\n\ +u\tShow only a specific user\n\ +n or #\tSet the number of process to show\n\ +s\tSet the delay in seconds between updates\n\ +W\tWrite configuration file ~/.toprc\n\ +q\tQuit" +#define SECURE_HELP_SCREEN "\ +Interactive commands available in secure mode are:\n\ +\n\ +space\tUpdate display\n\ +^L\tRedraw the screen\n\ +fF\tadd and remove fields\n\ +h or ?\tPrint this list\n\ +S\tToggle cumulative mode\n\ +i\tToggle display of idle proceses\n\ +c\tToggle display of command name/line\n\ +l\tToggle display of load average\n\ +m\tToggle display of memory information\n\ +t\tToggle display of summary information\n\ +n or #\tSet the number of process to show\n\ +u\tShow only a specific user\n\ +oO\tChange order of displayed fields\n\ +W\tWrite configuration file ~/.toprc\n\ +q\tQuit" + + /* Number of lines needed to display the header information. */ +static int header_lines; + +/* ############## Some Macro definitions for screen handling ######### */ + /* String to use in error messages. */ +#define PROGNAME "top" + /* Clear the screen. */ +#define clear_screen() \ + printf("%s", cl) + /* Show an error in the context of the spiffy full-screen display. */ +#define SHOWMESSAGE(x) do { \ + printf("%s%s%s%s", tgoto(cm, 0, header_lines-2), top_clrtoeol,md,mr); \ + printf x; \ + printf ("%s",me); \ + fflush(stdout); \ + sleep(2); \ + } while (0) diff --git a/uptime.1 b/uptime.1 new file mode 100644 index 00000000..ecb4e70b --- /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 00000000..e63621ef --- /dev/null +++ b/uptime.c @@ -0,0 +1,9 @@ +#include +#include "proc/whattime.h" +#include "proc/version.h" + +int main(int argc, char *argv[]) { + if(argc == 1) print_uptime(); + if((argc == 2) && (!strcmp(argv[1], "-V"))) display_version(); + return 0; +} diff --git a/utmp.c b/utmp.c new file mode 100644 index 00000000..936a7f6c --- /dev/null +++ b/utmp.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* examine and fix utmp entries. Note that the code for fixing entries + is not complete, and indeed does nothing at all at this time. No bug + reports, please, as I am still actively working on this. It is not + here for general use, but only so that I can ferret out any bugs that + exist on other peoples systems without having to log in to their systems + myself ;-) */ + + +int main (int argc, char **argv) { + + FILE *ut; /* /var/run/utmp */ + struct utmp uts; /* utmp record */ + char user[UT_NAMESIZE + 1]; + char host[17]; + char ch; + int print_all = 0, list = 0, fix = 0; + +/* get options */ + while ((ch = getopt(argc, argv, "laf")) != EOF) + switch (ch) { + case 'a': + print_all = 1; + break; + case 'l': + list = 1; + break; + case 'f': + fix = 1; + break; + } + +/* check argument options */ + if ( (!list && !print_all && !fix)) { + fprintf(stderr, "You must specify a command line option:\n\tl = list\n\ +\tf = fix\n\ta = all (requires l or f)\n"); + exit(1); + } + + + if (list) { + ut = fopen(UTMP_FILE, "r"); + while (fread(&uts, sizeof(uts), 1, ut)) + if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000')) + || print_all) { + strncpy(user, uts.ut_user, UT_NAMESIZE); + user[UT_NAMESIZE]=0; + strncpy(host, uts.ut_host, 16); + host[16]=0; + printf("ut_type: %d\n", uts.ut_type); + printf("ut_pid: %d\n", uts.ut_pid); + printf("ut_line: %s\n", uts.ut_line); + printf("ut_id: %2s\n", uts.ut_id); + printf("ut_time: %d\n", uts.ut_time); + printf("ut_user: %s\n", user); + printf("ut_host: %s\n", host); + printf("ut_addr: %d\n\n", uts.ut_addr); + } + fclose(ut); + } + + + if (fix) { + ut = fopen(UTMP_FILE, "r"); + while (fread(&uts, sizeof(uts), 1, ut)) + if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000')) + || print_all) { + /* Display entry in utmp */ + strncpy(user, uts.ut_user, UT_NAMESIZE); + user[UT_NAMESIZE]=0; + strncpy(host, uts.ut_host, 16); + host[16]=0; + printf("ut_type: %d\n", uts.ut_type); + printf("ut_pid: %d\n", uts.ut_pid); + printf("ut_line: %s\n", uts.ut_line); + printf("ut_id: %s2\n", uts.ut_id); + printf("ut_time: %d\n", uts.ut_time); + printf("ut_user: %s\n", user); + printf("ut_host: %s\n", host); + printf("ut_addr: %d\n\n", uts.ut_addr); + + printf("Modify this record? (y/N): "); fflush(stdout); + /* Ask if to delete or no */ + if ((ch = getchar()) == 'y' || ch == 'Y') { + while (getchar() != '\n'); + printf("Change ut_type? "); fflush(stdout); + if ((ch = getchar()) == 'y' || ch == 'Y') { + while (getchar() != '\n'); + printf("INIT, LOGIN, USER, or DEAD_PROCESS? (I/L/U/D): "); + fflush(stdout); + ch = getchar(); + switch (ch) { + case 'i': + case 'I': + uts.ut_type = INIT_PROCESS; + break; + case 'l': + case 'L': + uts.ut_type = LOGIN_PROCESS; + break; + case 'u': + case 'U': + uts.ut_type = USER_PROCESS; + break; + case 'd': + case 'D': + uts.ut_type = DEAD_PROCESS; + break; + default: + printf("Invalid choice: %c\n", ch); + } + if (ch != '\n') while ((ch = getchar()) != '\n'); + } + if (ch != '\n') while ((ch = getchar()) != '\n'); + printf("Change ut_id field? (y/N): "); fflush(stdout); + if ((ch = getchar()) == 'y' || ch == 'Y') { + while (getchar() != '\n'); + printf("Please enter the two characters for ut_id: "); + fflush(stdout); + uts.ut_id[0] = getchar(); + uts.ut_id[1] = getchar(); + while ((ch = getchar()) != '\n'); + } + if (ch != '\n') while ((ch = getchar()) != '\n'); + printf("Change the ut_user field? (y/N): "); fflush(stdout); + if ((ch = getchar()) == 'y' || ch == 'Y') { + int i; + while (getchar() != '\n'); + printf("Please enter the new ut_name, up to %c characters: ", + UT_NAMESIZE); + fflush(stdout); + for (i=0; i +.\" 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 [ "\-n" ] +.RI [ delay " [ " count ]] +.br +.BR vmstat [ "\-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-n\fP switch causes the header to be displayed only once rather than periodically. +.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-V\fP switch results in displaying version information. +.PP +.SH FIELD DESCRIPTIONS +.SS +.B "Procs" +.nf +r: The number of processes waiting for run time. +b: The number of processes in uninterruptable sleep. +w: The number of processes swapped out but otherwise runnable. This + field is calculated, but Linux never desperation swaps. +.fi +.PP +.SS +.B "Memory" +.nf +swpd: the amount of virtual memory used (kB). +free: the amount of idle memory (kB). +buff: the amount of memory used as buffers (kB). +.fi +.PP +.SS +.B "Swap" +.nf +si: Amount of memory swapped in from disk (kB/s). +so: Amount of memory swapped to disk (kB/s). +.fi +.PP +.SS +.B "IO" +.nf +bi: Blocks sent to a block device (blocks/s). +bo: Blocks received from 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: user time +sy: system time +id: idle time +.nf +.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 1k, except for CD-ROM blocks which are 2k. +.PP +.SH FILES +.ta +.nf +/proc/meminfo +/proc/stat +/proc/*/stat +.fi + +.SH "SEE ALSO" +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 AUTHOR +Written by Henry Ware . diff --git a/vmstat.c b/vmstat.c new file mode 100644 index 00000000..a3664e29 --- /dev/null +++ b/vmstat.c @@ -0,0 +1,278 @@ +#define VERSION Version: 0.99, last modified 15 January 94: ALPHA +#define PROGNAME "vmstat" +/* Copyright 1994 by Henry Ware . Copyleft same year. */ +/* This attempts to display virtual memory statistics. + vmstat does not need to be run as root. +*/ +/* TODO etc. + * Count the system calls. How? I don't even know where to start. + * add more items- aim at Posix 1003.2 (1003.7?) compliance, if relevant (I. + don't think it is, but let me know.) + * sometimes d(inter); report bugs to . +*/ + +#include "proc/sysinfo.h" +#include "proc/version.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NDEBUG !DEBUG + +#define BUFFSIZE 4096 +#define FALSE 0 +#define TRUE 1 + +static char buff[BUFFSIZE]; /* used in the procedures */ + +typedef unsigned long jiff; + + +/****************************************************************/ + + +static void usage(void) { + fprintf(stderr,"usage: %s [-V] [-n] [delay [count]]\n",PROGNAME); + fprintf(stderr," -V prints version.\n"); + fprintf(stderr," -n causes the headers not to be reprinted regularly.\n"); + fprintf(stderr," delay is the delay between updates in seconds. \n"); + fprintf(stderr," count is the number of updates.\n"); + exit(EXIT_FAILURE); +} + +static void crash(char *filename) { + perror(filename); + exit(EXIT_FAILURE); +} + +static int winhi(void) { + struct winsize win; + int rows = 24; + + if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_row > 0) + rows = win.ws_row; + + return rows; +} + + +static void showheader(void){ + printf("%8s%28s%8s%12s%11s%12s\n", + "procs","memory","swap","io","system","cpu"); + printf("%2s %2s %2s %6s %6s %6s %6s %3s %3s %5s %5s %4s %5s %3s %3s %3s\n", + "r","b","w","swpd","free","buff","cache","si","so","bi","bo", + "in","cs","us","sy","id"); +} + +static void getstat(jiff *cuse, jiff *cice, jiff *csys, jiff long *cide, + unsigned *pin, unsigned *pout, unsigned *s_in, unsigned *sout, + unsigned *itot, unsigned *i1, unsigned *ct) { + static int Stat; + + if ((Stat=open("/proc/stat", O_RDONLY, 0)) != -1) { + char* b; + buff[BUFFSIZE-1] = 0; /* ensure null termination in buffer */ + read(Stat,buff,BUFFSIZE-1); + close(Stat); + *itot = 0; + *i1 = 1; /* ensure assert below will fail if the sscanf bombs */ + b = strstr(buff, "cpu "); + sscanf(b, "cpu %lu %lu %lu %lu", cuse, cice, csys, cide); + b = strstr(buff, "page "); + sscanf(b, "page %u %u", pin, pout); + b = strstr(buff, "swap "); + sscanf(b, "swap %u %u", s_in, sout); + b = strstr(buff, "intr "); + sscanf(b, "intr %u %u", itot, i1); + b = strstr(buff, "ctxt "); + sscanf(b, "ctxt %u", ct); + } + else { + crash("/proc/stat"); + } +} + +static void getrunners(unsigned int *running, unsigned int *blocked, + unsigned int *swapped) { + static struct direct *ent; + static char filename[80]; + static int fd; + static unsigned size; + static char c; + DIR *proc; + + *running=0; + *blocked=0; + *swapped=0; + + if ((proc=opendir("/proc"))==NULL) crash("/proc"); + + while((ent=readdir(proc))) { + if (isdigit(ent->d_name[0])) { /*just to weed out the obvious junk*/ + sprintf(filename, "/proc/%s/stat", ent->d_name); + if ((fd = open(filename, O_RDONLY, 0)) != -1) { /*this weeds the rest*/ + read(fd,buff,BUFFSIZE-1); + sscanf(buff, "%*d %*s %c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u\n",&c,&size); + close(fd); + + if (c=='R') { + if (size>0) (*running)++; + else (*swapped)++; + } + else if (c=='D') { + if (size>0) (*blocked)++; + else (*swapped)++; + } + } + } + } + closedir(proc); + +#if 1 + /* is this next line a good idea? It removes this thing which + uses (hopefully) little time, from the count of running processes */ + (*running)--; +#endif +} + + + + +int main(int argc, char *argv[]) { + + const char format[]="%2u %2u %2u %6u %6u %6u %6u %3u %3u %5u %5u %4u %5u %3u %3u %3u\n"; + unsigned int height=22; /* window height, reset later if needed. */ +#if 0 + unsigned long int args[2]={0,0}; +#endif + unsigned int moreheaders=TRUE; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i,hz; + unsigned int running,blocked,swapped; + jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2]; + jiff duse,dsys,didl,Div,divo2; + unsigned int pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; + unsigned int inter[2],ticks[2],ctxt[2]; + unsigned int per=0, pero2; + unsigned long num=0; + unsigned int kb_per_page = sysconf(_SC_PAGESIZE) / 1024; + + setlinebuf(stdout); + argc=0; /* redefined as number of integer arguments */ + per=1; + num=0; + for (argv++;*argv;argv++) { + if ('-' ==(**argv)) { + switch (*(++(*argv))) { + case 'V': + display_version(); + exit(0); + case 'n': + /* print only one header */ + moreheaders=FALSE; + break; + default: + /* no other aguments defined yet. */ + usage(); + } + } else { + argc++; + switch (argc) { + case 1: + if ((per = atoi(*argv)) == 0) + usage(); + num = ULONG_MAX; + break; + case 2: + num = atol(*argv); + break; + default: + usage(); + } /* switch */ + } + } + + if (moreheaders) { + int tmp=winhi()-3; + height=((tmp>0)?tmp:22); + } + + pero2=(per/2); + showheader(); + getrunners(&running,&blocked,&swapped); + meminfo(); + getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl, + pgpgin,pgpgout,pswpin,pswpout, + inter,ticks,ctxt); + duse= *cpu_use + *cpu_nic; + dsys= *cpu_sys; + didl= *cpu_idl; + Div= duse+dsys+didl; + hz=sysconf(_SC_CLK_TCK); /* get ticks/s from system */ + divo2= Div/2UL; + printf(format, + running,blocked,swapped, + kb_swap_used,kb_main_free,kb_main_buffers,kb_main_cached, + (*pswpin *kb_per_page*hz+divo2)/Div, + (*pswpout*kb_per_page*hz+divo2)/Div, + (*pgpgin *hz+divo2)/Div, + (*pgpgout *hz+divo2)/Div, + (*inter *hz+divo2)/Div, + (*ctxt *hz+divo2)/Div, + (100*duse+divo2)/Div, + (100*dsys+divo2)/Div, + (100*didl+divo2)/Div + ); + + for(i=1;i and Michael K. Johnson +. + +Please send bug reports to diff --git a/w.c b/w.c new file mode 100644 index 00000000..e847d869 --- /dev/null +++ b/w.c @@ -0,0 +1,298 @@ +/* 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. + */ +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ /* for HZ */ + +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(char* host, int len) { + 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; + } + } + if(!width){ /* blank fields screw up parsers */ + fputc('-', stdout); + ++width; + } + /* if *any* unprintables(or blanks), replace rest of line with spaces */ + while (width < 16) { + fputc(' ', stdout); + ++width; + } +} + +/***** 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(char *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[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, + *month [] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" }; + time_t curt; + struct tm *logtm, *curtm; + int hour; + char *merid; /* meridian indicator */ + int today; + + curt = time(NULL); + curtm = localtime(&curt); + /* localtime returns a pointer to static memory */ + today = curtm->tm_yday; + logtm = localtime(&logt); + hour = logtm->tm_hour; + merid = (hour < 12) ? "am" : "pm"; + if (hour >= 12) hour -= 12; + if (hour == 0) hour = 12; + 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%s", weekday[logtm->tm_wday], hour, merid); + } else { + fprintf(fout, " %02d:%02d%s", hour, logtm->tm_min, merid); + } +} + + +/* 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 proc_t *getproc(utmp_t *u, char *tty, int *jcpu, int *found_utpid) { + int line; + proc_t **p, *best = NULL, *secondbest = NULL; + unsigned uid = ~0U; + + 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 = *found_utpid = 0; + for(p = procs; *p; p++) { + if((**p).pid == u->ut_pid) *found_utpid = 1; + if((**p).tty != line) continue; + (*jcpu) += (**p).utime + (**p).stime; + secondbest = *p; + /* same time-logic here as for "best" below */ + if(! (secondbest && (**p).start_time <= secondbest->start_time) ){ + secondbest = *p; + } + if(!ignoreuser && uid != (**p).euid && uid != (**p).ruid) continue; + if((**p).pid != (**p).tpgid) continue; + if(best && (**p).start_time <= best->start_time) continue; + best = *p; + } + return best ? best : secondbest; +} + + +/***** showinfo */ +static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) { + int jcpu, ut_pid_found; + unsigned i; + char uname[USERSZ + 1] = "", + tty[5 + sizeof u->ut_line + 1] = "/dev/"; + 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) { + int 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 (best) { + if (best->cmdline) + print_strlist(stdout, best->cmdline, " ", maxcmd); + else + printf("%*.*s", -maxcmd, maxcmd, best->cmd); + } 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 + + 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_FILLCMD | PROC_FILLUSR); + + 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(); + while ((u=getutent())) { + if (u->ut_type == USER_PROCESS && + (user ? !strncmp(u->ut_user, user, USERSZ) : *u->ut_user)) + showinfo(u, longform, maxcmd, from); + } + endutent(); + + return 0; +} diff --git a/watch.1 b/watch.1 new file mode 100644 index 00000000..f6021a6d --- /dev/null +++ b/watch.1 @@ -0,0 +1,82 @@ +.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 [\-dhv] [\-n ] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=] [\-\-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. +.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 00000000..cd8ac4ea --- /dev/null +++ b/watch.c @@ -0,0 +1,277 @@ +/* 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 . + */ + + +#define VERSION "0.2.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct option longopts[] = + { + { "differences", optional_argument, 0, 'd' }, + { "help", no_argument, 0, 'h' }, + { "interval", required_argument, 0, 'n' }, + { "version", no_argument, 0, 'v' }, + { 0, 0, 0, 0 } + }; + +static char usage[] = "Usage: %s [-dhnv] [--differences[=cumulative]] [--help] [--interval=] [--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; + + +#define min(x,y) ((x) > (y) ? (y) : (x)) +#define max(x,y) ((x) > (y) ? (x) : (y)) + + +static void +do_usage(void) +{ + fprintf(stderr, usage, progname); + exit(1); +} + + +static void +do_exit(int status) { + if (curses_started) + endwin(); + exit(status); +} + + +/* signal handler */ +static void +die(int notused) +{ + (void)notused; + do_exit(0); +} + + +static void +winch_handler(int notused) +{ + (void)notused; + screen_size_changed = 1; +} + + +static void +get_terminal_size(void) +{ + struct winsize w; + if (ioctl(2, TIOCGWINSZ, &w) == 0) + { + if (w.ws_row > 0) + height = w.ws_row; + if (w.ws_col > 0) + width = w.ws_col; + } +} + + +int +main(int argc, char *argv[]) +{ + int optc; + int option_differences=0, + option_differences_cumulative=0, + option_help=0, + option_version=0; + int interval=2; + char *command; + int command_length=0; /* not including final \0 */ + + progname = argv[0]; + + while ((optc = getopt_long(argc, argv, "+d::hn:v", 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 'n': + { + char *s; + interval = strtol(optarg, &s, 10); + if (!*optarg || *s) + do_usage(); + } + 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); + exit(0); + } + + if (optind >= argc) + do_usage(); + + command = strdup(argv[optind++]); + command_length = strlen(command); + for (;optind 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=2; y