From: Hasan Wan Date: Fri, 25 May 2012 08:47:13 +0000 (+0800) Subject: Updated with Tizen:Base source codes X-Git-Tag: 2.0_alpha~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a486d28ab84bf75365fc428bbce3f3272437cbe3;p=external%2Fprocps.git Updated with Tizen:Base source codes --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0860b24 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,50 @@ +free: +Brian Edmonds + +oldps: +Branko Lankester +Michael K. Johnson +Michael Shields +Charles Blake +David Mossberger-Tang + +ps: +Albert Cahalan + +skill/kill/snice: +Albert Cahalan + +tload: +Branko Lankester +David Engel +Michael K. Johnson + +top: +Jim Warner + +oldtop: +Branko Lankester +Roger Binns +Robert Nation +Michael K. Johnson +Michael Shields +Tim Janik +Helmut Geyer +George Bonser + +uptime: +Larry Greenfield +Michael K. Johnson + +vmstat: +Henry Ware . + +w: +Larry Greenfield +Michael K. Johnson +Charles Blake + +watch: +Tony Rems +Mike Coleman + diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..ec45bde --- /dev/null +++ b/BUGS @@ -0,0 +1,74 @@ +BUG REPORTS + +Please read this file before sending in a bug report or patch. + +Also, PLEASE read the documentation first. 90% of the mail I get +complaining about procps is due to the sender not having read the +documentation! + + +Where to send +============= +Send comments, bug reports, patches, etc., to albert@users.sf.net + + +What to send +============ +It is much more useful to me if a program really crashes to recompile it +with make "CFLAGS=-ggdb -O", run it with "gdb prog" and "run" and send +me a stack trace ('bt' command). That said, any bug report is still +better than none. + +strace and ltrace output are very helpful: + + strace -o output-file ps --blah + bzip2 output-file + +I also like "ps --info" output, even if there isn't a ps problem. + +Kernel-Dependent Patches +======================== +If you send me patches which are specific to *running* with a particular +kernel version of /proc, please condition them with the runtime determined +variable 'linux_version_code' from libproc/version.c. It is the same +number as the macro LINUX_VERSION_CODE for which the kernel /proc fs +code was compiled. + +A macro is provide in libproc/version.h to construct the code from its +components, e.g. + if (linux_version_code < LINUX_VERSION(2,5,41)) + /* blah blah blah */ +A startup call to set_linux_version may also be necessary. + +Of course, if a bug is due to a change in kernel file formats, it would +be best to first try to generalize the parsing, since the code is then +more resilient against future change. + +Also unified diffs (diff -u) are my preference, context diffs (diff -c ) +are kind of usable, and standard diffs (diff) are more useless than a +generic text description of what you did. Just use + diff -Naurd oldfile newfile +or + diff -Naurd old-procps-dir new-procps-dir +to create your diffs and you will make me happy. Also make sure to +include a description of what the diff is for or I'm likely to ignore +it because of general lack of time... + +It might be nice to get rid of miscellaneous compiler warnings, but +don't bend over backwards to do it. + + +Code Structure +============== + +A goal is to encapsulate *all* parsing dependent on /proc +file formats into the libproc library. If the API is general enough +it can hopefully stabilize and then /proc changes might only require +updating libproc.so. Beyond that having the set of utilities be simple +command lines parsers and output formatters and encapsulating all kernel +divergence in libproc is the way to go. + +Hence if you are submitting a new program or are fixing an old one, keep +in mind that adding files to libproc which encapsulate such things is +more desirable than patching the actual driver program. (well, except +to move it toward the API of the library). diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..92b8903 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/CodingStyle b/CodingStyle new file mode 100644 index 0000000..962207d --- /dev/null +++ b/CodingStyle @@ -0,0 +1,101 @@ +If you start a new file, you get to choose the style. +If you change an existing file, follow the existing style. + +Hard tabs are OK, as long as you consider the tab stops to +be every 8 characters. You can also use 2, 3, or 4 spaces. +Tabs are kind of yucky, since cut-and-paste mangles them +sometimes and they make "diff -Naurd old new" output less +readable. + +Spaces within a line don't matter much, and won't be +considered part of the style. Just make it readable: + +if(x){ // OK +if( x ){ // OK +if (x) { // OK +if(x==y && a==b){ // OK +if(x == y && a == b){ // poor +if(x==y&&a==b){ // poor + +This is evil: + +szWinStallman +FoulCodingStyle (int iInsanity) + { + if (iInsanity) + { + GoHackEmacs () ; + } + else + { + SeekHelpForYourLisp () ; + } + } + + +Curly braces belong at the end of a line. If you must, go ahead +and make function bodies an exception to that rule. (as Linus does) + +Big fprintf() calls and similar go like this: + +fprintf(fd, "%d %d %d %d %d %d\n", + sdfsdf_sdfsdf + sdfs_iii, // not an example of good names! + iijjij, + kjfkkj_sdfssss_sfff, + sdflkjfdskj + sdf - sfds, + jksss, + sfssss + wwwwfwfw +); + +Keep these distinct: NULL, '\0', 0, 0.0 + +Command-line parsers need to be bomb-proof. It is not acceptable +to crash due to a messed up command-line. For an option "-x" that +takes an argument, accept both "-x arg" and "-xarg". Remember to +support "--" and "--version". + +Be extremely careful when handling data from other users. +For example, it is a security hole if /proc/123/cmdline can +overflow an array. It is often a security hole if you allow +non-ASCII characters to be printed. Assuming the console is +not in UTF-8 mode, all of these are bad: "\b\e\f\n\r\t\v\x9b". +(the "\x9b" is valid in UTF-8 mode, but equivalent to "\e[" +when not in UTF-8 mode -- which gives control of terminal +settings) It's best if you consider user-supplied data to +be unsafe, since this makes for less work in case the code +ends up needing to run setuid. Termcap data is user-supplied. +Except for the above security issues, don't bother to check +for something you can't handle... like printf() failing. +It is expected that /dev exists and so on. + +Remember that a read() may return early, with partial data +or with -1 and errno set to EINTR. You then must try again. + +char: may be signed or unsigned by default +int: always 32-bit +long long: always 64-bit +pointer: either 32-bit or 64-bit +long: same size as a pointer +KLONG: same size as a pointer or long IN THE KERNEL + +Functions used in just one file must be marked static. +Use the "const" and "restrict" keywords wherever you can. + +Put main() at the bottom of a file so you don't need all +those ugly forward declarations. + +Avoid the strcat() function. It is slow. For some odd +reason, snprintf() is faster than sprintf(). + +Reuse memory allocations when you can. When using realloc(), +do your increments by more than one. 25% is a nice amount. + +Avoid compile-time choices. They make documentation difficult, +and they are not friendly to binary distribution. + +Write programs that can handle a million processes without +getting hopelessly slow. Allow for /proc/123/cmdline to +be at least 128 kB. + +The LGPL license is strongly preferred. This allows use of +the code in the library. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09fb3ed --- /dev/null +++ b/Makefile @@ -0,0 +1,261 @@ +# procps Makefile +# Albert Cahalan, 2002-2004 +# +# Recursive make is considered harmful: +# http://google.com/search?q=%22recursive+make+considered+harmful%22 +# +# For now this Makefile uses explicit dependencies. The project +# hasn't grown big enough to need something complicated, and the +# dependency tracking files are an ugly annoyance. +# +# This file includes */module.mk files which add on to variables: +# FOO += bar/baz +# +# +# Set (or uncomment) SKIP if you wish to avoid something. +# For example, you may prefer the /bin/kill from util-linux or bsdutils. + + +VERSION := 3 +SUBVERSION := 2 +MINORVERSION := 8 +TARVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION) + +############ vars + +# so you can disable them or choose alternates +ldconfig := ldconfig +ln_f := ln -f +ln_sf := ln -sf +install := install -D --owner 0 --group 0 + +# Lame x86-64 /lib64 and /usr/lib64 abomination: +lib64 := lib$(shell [ -d /lib64 ] && echo 64) + +usr/bin := $(DESTDIR)/usr/bin/ +bin := $(DESTDIR)/bin/ +sbin := $(DESTDIR)/sbin/ +usr/proc/bin := $(DESTDIR)/usr/bin/ +man1 := $(DESTDIR)/usr/share/man/man1/ +man5 := $(DESTDIR)/usr/share/man/man5/ +man8 := $(DESTDIR)/usr/share/man/man8/ +lib := $(DESTDIR)/$(lib64)/ +usr/lib := $(DESTDIR)/usr/$(lib64)/ +usr/include := $(DESTDIR)/usr/include/ + +#SKIP := $(bin)kill $(man1)kill.1 + +BINFILES := $(usr/bin)uptime $(usr/bin)tload $(usr/bin)free $(usr/bin)w \ + $(usr/bin)top $(usr/bin)vmstat $(usr/bin)watch $(usr/bin)skill \ + $(usr/bin)snice $(bin)kill $(sbin)sysctl $(usr/bin)pmap \ + $(usr/proc/bin)pgrep $(usr/proc/bin)pkill $(usr/bin)slabtop \ + $(usr/proc/bin)pwdx + +MANFILES := $(man1)uptime.1 $(man1)tload.1 $(man1)free.1 $(man1)w.1 \ + $(man1)top.1 $(man1)watch.1 $(man1)skill.1 $(man1)kill.1 \ + $(man1)snice.1 $(man1)pgrep.1 $(man1)pkill.1 $(man1)pmap.1 \ + $(man5)sysctl.conf.5 $(man8)vmstat.8 $(man8)sysctl.8 \ + $(man1)slabtop.1 $(man1)pwdx.1 + +TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \ + Makefile procps.lsm procps.spec v t README.top CodingStyle \ + sysctl.conf minimal.c $(notdir $(MANFILES)) dummy.c \ + uptime.c tload.c free.c w.c top.c vmstat.c watch.c skill.c \ + sysctl.c pgrep.c top.h pmap.c slabtop.c pwdx.c + +# Stuff (tests, temporary hacks, etc.) left out of the standard tarball +# plus the top-level Makefile to make it work stand-alone. +_TARFILES := Makefile + +CURSES := -lncurses + +# This seems about right for the dynamic library stuff. +# Something like this is probably needed to make the SE Linux +# library loading not conflict with embedded systems stuff. +# +#ifeq ($(SHARED),1) +#ldl := -ldl +#LIBTYPE := -DSHAREDLIB +#else +#LIBTYPE := -DSTATICLIB +#endif + +# Preprocessor flags. +PKG_CPPFLAGS := -D_GNU_SOURCE -I proc +CPPFLAGS := -I/usr/include/ncurses +ALL_CPPFLAGS := $(PKG_CPPFLAGS) $(CPPFLAGS) + +# Left out -Wconversion due to noise in glibc headers. +# Left out -Wunreachable-code and -Wdisabled-optimization +# because gcc spews many useless warnings with them. +# +# Since none of the PKG_CFLAGS things are truly required +# to compile procps, they might best be moved to CFLAGS. +# On the other hand, they aren't normal -O -g things either. +# +# Note that -O2 includes -fomit-frame-pointer only if the arch +# doesn't lose some debugging ability. +# +PKG_CFLAGS := -fno-common -ffast-math \ + -W -Wall -Wshadow -Wcast-align -Wredundant-decls \ + -Wbad-function-cast -Wcast-qual -Wwrite-strings -Waggregate-return \ + -Wstrict-prototypes -Wmissing-prototypes +# Note that some stuff below is conditional on CFLAGS containing +# an option that starts with "-g". (-g, -g2, -g3, -ggdb, etc.) +CFLAGS := -O2 -s +ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS) + +PKG_LDFLAGS := -Wl,-warn-common +LDFLAGS := +ALL_LDFLAGS := $(PKG_LDFLAGS) $(LDFLAGS) + +############ Add some extra flags if gcc allows + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),tar) +ifneq ($(MAKECMDGOALS),extratar) +ifneq ($(MAKECMDGOALS),beta) + +# Unlike the kernel one, this check_gcc goes all the way to +# producing an executable. There might be a -m64 that works +# until you go looking for a 64-bit curses library. +check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o /dev/null $(CURSES) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) + +# Be 64-bit if at all possible. In a cross-compiling situation, one may +# do "make m64=-m32 lib64=lib" to produce 32-bit executables. DO NOT +# attempt to use a 32-bit executable on a 64-bit kernel. Packagers MUST +# produce separate executables for ppc and ppc64, s390 and s390x, +# i386 and x86-64, mips and mips64, sparc and sparc64, and so on. +# Failure to do so will cause data corruption. +m64 := $(call check_gcc,-m64,$(call check_gcc,-mabi=64,)) +ALL_CFLAGS += $(m64) + +ALL_CFLAGS += $(call check_gcc,-Wdeclaration-after-statement,) +ALL_CFLAGS += $(call check_gcc,-Wpadded,) +ALL_CFLAGS += $(call check_gcc,-Wstrict-aliasing,) + +# Adding -fno-gcse might be good for those files which +# use computed goto. +#ALL_CFLAGS += $(call check_gcc,-fno-gcse,) + +# if not debugging, enable things that could confuse gdb +ifeq (,$(findstring -g,$(filter -g%,$(CFLAGS)))) +ALL_CFLAGS += $(call check_gcc,-fweb,) +ALL_CFLAGS += $(call check_gcc,-frename-registers,) +ALL_CFLAGS += $(call check_gcc,-fomit-frame-pointer,) +endif + +# in case -O3 is enabled, avoid bloat +ALL_CFLAGS += $(call check_gcc,-fno-inline-functions,) + +endif +endif +endif +endif + +############ misc. + +# free.c pmap.c sysctl.c uptime.c vmstat.c watch.c pgrep.c skill.c tload.c top.c w.c +# utmp.c oldtop.c tmp-junk.c minimal.c + +.SUFFIXES: +.SUFFIXES: .a .o .c .s .h + +.PHONY: all clean do_all install tar extratar beta + +ALL := $(notdir $(BINFILES)) + +CLEAN := $(notdir $(BINFILES)) + +DIRS := + +INSTALL := $(BINFILES) $(MANFILES) + +# want this rule first, use := on ALL, and ALL not filled in yet +all: do_all + +-include */module.mk + +do_all: $(ALL) + +junk := DEADJOE *~ *.o core gmon.out + +# Remove $(junk) from all $(DIRS) +CLEAN += $(junk) $(foreach dir,$(DIRS),$(addprefix $(dir), $(junk))) + +########## +# not maintained because it isn't really needed: +# +#SRC := +#OBJ := $(patsubst %.c,%.o, $(filter %.c,$(SRC))) +# +#ifneq ($(MAKECMDGOALS),clean) +#-include $(OBJ:.o=.d) +#endif +# +#%.d: %.c +# depend.sh $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@ +############ + +# don't want to type "make procps-$(TARVERSION).tar.gz" +tar: $(TARFILES) + mkdir procps-$(TARVERSION) + (tar cf - $(TARFILES)) | (cd procps-$(TARVERSION) && tar xf -) + tar cf procps-$(TARVERSION).tar procps-$(TARVERSION) + gzip -9 procps-$(TARVERSION).tar + +extratar: $(_TARFILES) + mkdir procps-$(TARVERSION) + (tar cf - $(_TARFILES)) | (cd procps-$(TARVERSION) && tar xf -) + tar cf extra-$(TARVERSION).tar procps-$(TARVERSION) + gzip -9 extra-$(TARVERSION).tar + +beta: $(TARFILES) $(_TARFILES) + mkdir beta-$(TARVERSION) + (tar cf - $(TARFILES) $(_TARFILES)) | (cd beta-$(TARVERSION) && tar xf -) + tar cf beta-$(TARVERSION).tar beta-$(TARVERSION) + gzip -9 beta-$(TARVERSION).tar + +clean: + rm -f $(CLEAN) + +###### install + +$(BINFILES) : all + $(install) --mode a=rx $(notdir $@) $@ + +$(MANFILES) : all + $(install) --mode a=r $(notdir $@) $@ + +install: $(filter-out $(SKIP) $(addprefix $(DESTDIR),$(SKIP)),$(INSTALL)) + cd $(usr/bin) && $(ln_f) skill snice + cd $(usr/proc/bin) && $(ln_f) pgrep pkill + +############ prog.c --> prog.o + +top.o : top.h + +%.o : %.c + $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c -o $@ $< + +w.o: w.c + $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(W_SHOWFROM) -c $< + +############ prog.o --> prog + +pmap w uptime tload free sysctl vmstat utmp pgrep skill pwdx: % : %.o $(LIBPROC) + $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ + +slabtop top: % : %.o $(LIBPROC) + $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES) + +watch: % : %.o + $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES) + +############ progX --> progY + +snice kill: skill + $(ln_f) skill $@ + +pkill: pgrep + $(ln_f) pgrep pkill diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..60acc88 --- /dev/null +++ b/NEWS @@ -0,0 +1,368 @@ +procps-3.2.7 --> procps-3.2.8 + +ps: allow "+" in sort specifications, as in man page rh208217 +ps: recognize SCHED_ISO and SCHED_IDLE +ps: document SCHED_BATCH and add a "see also" for stime +ps: man page less ambiguous +top: normal exit code should be 0 #341272 #354255 rh199174 +top: misc fixes +pgrep: usage error should exit with 2 #413383 +vmstat: use EXIT_FAILURE -- thanks Yoshio Nakamura #425492 +sysctl: fix crash -- thanks Steinar Gunderson #423704 +watch: tolerate umlauts #207103 +pmap: range limits with -A low,high +update /dev/tty* info to May 2009 devices.txt +don't read off end of string const rh469495 rh498182 + +procps-3.2.6 --> procps-3.2.7 + +top: document H option -- thanks Tony Ernst +top: terabytes -- thanks Tony Ernst +top: CPU usage column width fixes -- thanks Tony Ernst +top: *roff change #326517 +ps: SCHED_BATCH is B +ps: fix s format (signals) output with thread display +watch: avoid integer overflow for the time delay +pwdx: buffer overflow fixed -- thanks Ulf Harnhammar +procps.spec needed a slash -- thanks Jesse Brandeburg +w: stale utmp entries snuck in via uninitialized var -- thanks Robert A Basch +pgrep/pkill: fix some realloc-related crashes #353894 +pgrep/pkill: g is criteria (PLD Linux, patch found in locked filing cabinet) +sysctl: use - for stdin (PLD Linux: beware of the leopard) +top: show CPU time stolen from a virtual machine + +procps-3.2.5 --> procps-3.2.6 + +vmstat: /proc/stat buffer big enough for 1024 CPUs +dietlibc needs termios.h for struct winsize -- thanks Thomas Ogrisegg +top: can do per-task display -- thanks John Blackwood rh114012 +more MIPS crud -- thanks Jim Gifford and Ryan Oliver +begin prep for setuid +top: fix %CPU max on 2..9 CPU SMP -- thanks Ga*tan LEURENT rh110555 +ps: fix crash related to realloc -- thanks David Houlder +ps: man page more detailed #334682 +spelling fixes #300333 #334684 #334685 +top: crash on resize fixed -- thanks Michal Maruska #320289 +vmstat: -p handles /dev/ and does not overflow #319523 #330969 + +procps-3.2.4 --> procps-3.2.5 + +display problem on 64-bit systems fixed #287947 +top: variable-width PID and PPID +top: variable-width %CPU rh110555 +sysctl: better error messages +ps: security labels can contain any printable ASCII +top: help and version message on stdout, with exit(0) #283541 +ps: SIGTSTP and SIGTTOU shouldn't print bug email address #246123 +slabtop: compile with glibc 2.2.17 (and older, likely) +slabtop: fix overflow on huge NUMA boxes #264640 +slabtop: accept any slabinfo 2.x format g77301 #287691 rh145369 rh145906 +ps: alignment after WCHAN fixed ub5385 +pmap: when no -x or -d option, show full path rh142751 + +procps-3.2.3 --> procps-3.2.4 + +support 64-bit MIPS with n32 binary +sparc32 optimized for sparc32 again +pwdx: new command -- thanks Nicholas Miell +ps: UTF-8 username + command -- thanks Karel Zak rh134763,rh112518,rh134780 +ps: more room for some columns +ps: tolerate SubDomain security module CONTEXT/LABEL data #277401 +watch: passes COLUMNS and LINES in environment +top: in batch mode, tolerate unknown $TERM -- thanks Daniel Walsh +pkill: quiet about processes that die before kill() + +procps-3.2.2 --> procps-3.2.3 + +avoid truncating long usernames +avoid warning about -lncurses when not linking (more) +new names for shared libraries (packagers: watch out!) +"make install" no longer rebuilds everything +wchan now '*' for multi-threaded processes +ps: new man page -- thanks Nicolas Francois +ps: STAT shows l for multi-threaded processes +vmstat: some overflow problems fixed -- thanks Holger Kiehl +sysctl: man page fix + +procps-3.2.1 --> procps-3.2.2 + +new packager (downstream maintainer) guidelines in README +move striping from install command to CFLAGS +new gcc options in use: -fweb, -frename-registers, etc. +avoid warning about -lncurses when not linking -- thanks FLWM +tolerate IA-64 headers without PAGE_SIZE +ps: k option, same as --sort +ps: personality-specific -x support (HP-UX and SVR4-MP) +pgrep: man page SEE ALSO updated #226817 +sysctl: -q and -N options +sysctl: better error handling of failed writes +top: tolerate sparse CPU numbering +top: try to handle terminals lacking rmam and smam #235003 +top: xterm dislikes clear-to-eol at eol (char lost) +vmstat: fixed -d +watch: allow sub-second intervals -- thanks Thomas Stewart + +procps-3.2.0 --> procps-3.2.1 + +build even w/ curses in an odd location -- thanks to Segher Boessenkool +ps: STAT flags for fg process group and session leader +ps: STAT flags for swapped out process dropped (was broken) +ps: new -M and Z options for security data (SE Linux, etc.) +slabtop: detect broken /proc/slabinfo -- thanks to Fabian Frederick +slabtop: ensure that error messages show up on the screen -- FF again + +procps-3.1.15 --> procps-3.2.0 + +build on IA-64 again #227933 +pmap: output like Solaris 9, not Solaris 7 +ps: also handle SELinux on the 2.4.xx kernels +top: during a ^Z, the terminal was messed up #228822 +future-proof the tty handling (thanks to Zhou Wei) +slabtop (Chris Rivera and Robert Love) #226778 +pmap: detect the primary stack +pmap: -d format +free: report high and low memory + +procps-3.1.14 --> procps-3.1.15 + +install to /lib64 if it exists +hide kernel PID bug (Linux 2.4.13-pre1 to 2.4.MAX) #217278 #219730 #217525 #224470 +ps: faster threaded display +top: auto-margin problem #217559 +ps: support NSA SELinux, all builds, Linux 2.6+ #193648 +sysctl: tweak man page for ESR's broken parser + +procps-3.1.13 --> procps-3.1.14 + +top: displays on more genuine serial terminals +handle 32-bit dev_t of Linux 2.6 +ps: finally, m and -m satisfy the original design +ps: distinct per-thread and whole-process pending signals + +procps-3.1.12 --> procps-3.1.13 + +ps: can display NPTL threads w/ kernel patch +no seLinux for now (new kernel interface) + +procps-3.1.11 --> procps-3.1.12 + +ps: explicit width ("ps -o pid,wchan:42,args") +ps: $PS_FORMAT works properly #201575 +top: new Linux 2.6.0-test4 CPU stats shown +top: multiple -p options work again +top: fixed 4 GB wrap-around +ps: has a set of tests to ensure correctness +man page: /var/run/utmp, not /etc/utmp #206583 +required flags moved out of CFLAGS #205429 +RPM generation handles /lib64 +WCHAN skips leading '.' +vmstat: numerous new features + +procps-3.1.10 --> procps-3.1.11 + +compile with gcc 2.95 again (C99 issue) + +procps-3.1.9 --> procps-3.1.10 + +handle GPLONLY_ symbols #143549 #188374 +kill: better man page +skill: better man page +ps: PID-like columns change width as needed +top: COMMAND instead of Command +vmstat: -m displays slabinfo +vmstat: -d displays disk stats + +procps-3.1.8 --> procps-3.1.9 + +memory sizes fixed for 64-bit w/ gcc 3.x #194376 #191933 +ps: detect broken OS install w/o /proc mounted #172735 +top: fix suspend/resume behavior +top: ditch warning until a GOOD interface is found #188271 +kill: more info in the man page #182414 +ps: document the -o, o, -O, and O options #169301 +vmstat: choose units you like: 1000, 1024, 1000000... + +procps-3.1.7 --> procps-3.1.8 + +top: fix keyboard handling (help screen, etc.) + +procps-3.1.6 --> procps-3.1.7 + +Makefile: made SKIP feature easier to use +watch: --help now explains -t, --no-title #182246 +ps: warning directs users to the FAQ +top: batch mode can refresh by fractional seconds +top: faster start-up +top: do not refresh like crazy +ps: better crash message + +procps-3.1.5 --> procps-3.1.6 + +handle the 2.5.61 kernel +top: memory leak fixed +ps: new --ppid option selects by PPID +watch: new --no-title option #179862 +handle SPARC Linux badness +rare crash fixed +compile with gcc 2.91.xx again +more informative "ps --info" +README update +ps: compare more with "ps -C verylongname" #178127 + +procps-3.1.4 --> procps-3.1.5 + +ancient (2.x.xx era) data corruption fixed +serious hidden-process problem (3.1.3+) fixed +w: escape sequence vulnerability fixed + +procps-3.1.3 --> procps-3.1.4 + +top: was trashing every "3" in a command name +top: when killing a process, the PID was cut at a "3" +top: more reliable %CPU +update copyright dates (GPL & LGPL require this) +RPM generation works now + +procps-3.1.2 --> procps-3.1.3 + +uses /proc/*/wchan files when available +top: user selection +sysctl: add -e for Red Hat 8.0 boot scripts +sysctl: the obvious --help, -V, and --version +sysctl: some command line error checking +w: stdout, not stderr -- thanks to Sander van Malssen + +procps-3.1.1 --> procps-3.1.2 + +better RPM generation +use C99 features +some seLinux fixes +now count Inact_laundry as needed #172163 +ps: fewer globals +ps: hardware-enforced buffer protection +ps: 1 kB smaller +top: B command added (for bold on/off) +top: handle old (and future) config files +top: man page tweak +top: old sort keys #167249 +top: out-of-bounds RT as "RT" +top: several times faster +top: t command fixed +vmstat: -f +vmstat: -s +w: much faster +watch: don't drop empty lines #171005 +watch: re-indented + +procps-3.1.0 --> procps-3.1.1 + +vmstat faster on 2.5.xx kernels +vmstat header fixed +vmstat -a re-fixed + +procps-3.0.5 --> procps-3.1.0 + +vmstat displays IO-wait time instead of bogus "w" +can build w/o shared library (set SHARED=0) +when IO-wait hidden, count as idle, not as sys +pmap command added (like Sun has) +do not crash GNU make 3.79 +top slightly faster + +procps-3.0.4 --> procps-3.0.5 + +top tolerates super-wide displays +better (?) RPM generation +XConsole and top.desktop removed +old build system removed +code cleanup +pgrep and pkill get "-o" (oldest matching process) +had vmstat "bi" and "bo" output interchanged on 2.5.xx +fix man page tbl directives +top man page cleaned up + +procps-3.0.3 --> procps-3.0.4 + +make top go faster +Linux 2.2.xx ELF note warning removed +only show IO-wait on recent kernels +fix top's SMP stats +fix top for "dumb" and "vt510" terminals +in top, limit the priority values to -99 ... 99 + +procps-3.0.2 --> procps-3.0.3 + +more "make install" fixes +lib CFLAGS working again +top.1 codes fixed +bad (int*) cast in top removed +top runs faster +libproc memory corruption fixed +rant moved out of top.1 man page +ability to SKIP installing things +fixed ps --sort crash + +procps-3.0.1 --> procps-3.0.2 + +top defaults to the old layout +top defaults to sorting by %CPU +fix top for non-SMP 2.2.xx and 2.0.xx +new "make install" fixed +vmstat -a fixed +vmstat compiles with latest gcc-3.x +vmstat does 64-bit time + +procps-3.0.0 --> procps-3.0.1 + +sysctl handles net/ipv4/conf/eth1.0123/tag (VLAN interface) +sysctl handles net.ipv4.conf.eth1/0123.tag (VLAN interface) +"ps" is now about 2x faster than in procps-2.x.x +"ps -F" now documented +w works in KOI8-R locale +vmstat documentation update +"skill -n blah blah blah" lets you test options +simple "make && make install" now + +procps-2.x.x --> procps-3.0.0 + +designed to support Linux 2.0 through 2.5.41 and beyond +new top, with optional: color, windowing, SMP stats +runs faster +more "it crashes" bugs fixed +top shows IO-wait time +vmstat can show active/inactive memory stats +real-time info supported in ps +correct "ps -o size" and "ps --sort size" +new maintainers +reduced memory usage for ps +allow large PIDs to be specified +SELINUX support is just a recompile away +the "F" column shrank, so "ps -l" has more command name room +64-bit time reduces the overflow problem +support S/390, IA-64 emulator, and user-mode Linux +oldps is gone +configure script -- use "make -f Makefile.noam" as a backup +"w" program better at determining what a user is doing +more stable +code at http://procps.sf.net/ now (SourceForge) + +Earlier changes, for those not using Debian already: + +more stable +runs faster +-F format option +better error reporting in ps for unknown format specifiers +BSD's sysctl options -b and -X +top displays well on large-memory systems +old BSD-style select-by-PID ("ps l$$") +15-character user names +ps 'f' ASCII art forest fixed +add SIGSYS on i386 +top reports real RSS value +large-memory systems work +minimal ps program for embedded systems (minimal.c) +BSD personality process selection fixed +support locale (French) with ',' and '.' mixed up +pgrep program +includes the "kill" and "nice" programs +don't chop non-tty ps output at 80 columns diff --git a/README b/README new file mode 100644 index 0000000..fd76134 --- /dev/null +++ b/README @@ -0,0 +1,72 @@ +COMPATIBILITY + + This code is intended for use with Linux 2.2.xx, 2.4.xx, + 2.6.xx, and hopefully all future kernels. You should be + running a system with libc 6, but libc 5 might work too. + +INSTALLATION + + make + make install + + Only the second ("make install") is needed if you just + want to build and install procps in the normal way. + + If you wish to test before installing, use the scripts + named t, v, and p to ensure that the correct libproc + (the new one) is used during your testing. + + You may set SKIP to avoid building or installing things. + For example: + + make SKIP='/bin/kill /usr/share/man/man1/kill.1' install + + Use SHARED=0 to build procps without shared libraries. + This may be useful for installing in your home directory. + + make SHARED=0 DESTDIR=$HOME install + + Suppose you wanted to install stuff in strange places. + You might do something like this: + + make usr/bin=/tmp/Q/i/ DESTDIR=/tmp/Q install="install -D" ldconfig=echo install + + If cross-compiling, you might need to set lib64 to + either "lib" or "lib64". You might need to set m64 to + -m64, -m32, or nothing at all. Some examples: + + make lib64=lib m64=-m32 # for a bi-arch gcc + make lib64=lib64 CC=x86_64-gcc + make lib64=lib CC=alpha-gcc + +PACKAGING + + If you are a downstream maintainer (packager) for a Linux distribution, + please avoid causing troubles. This section applies to you. + + Send patches in regularly. Many patches made by vendors have been buggy, + some quite severely so. Sending in a patch will at least get it reviewed, + if not included. There is a procps test suite that must be passed. + Forward all bug reports. If your bug database is public and busy enough + to bother with, please make this known. Follow Debian's lead in making + the bug database easy to comment on via email w/o need for an account. + + Do not change the user interface. Many of the programs are intended to be + compatible with Solaris, FreeBSD, AIX, IRIX, Tru64, and the UNIX standard. + Your nice new command options WILL BE BROKEN as needed to ensure that + procps remains compatible with the rest of the world. Sysadmins hate to + deal with incompatible behavior. If you need a new option, ask for it. + + For normal packages, ensure that you do not add debugging flags + to the CFLAGS variable. If debugging flags are present, the Makefile + will avoid adding several optimizations that would interfere with gdb. + + There should be no need to modify the Makefile. You can set variables + on the "make" command line or use "make -e" to pass variables from + the environment. + +BUG REPORTS + + Debian users should use the Debian bug tracking system. + Email to albert@users.sf.net or csmall@debian.org or + procps-feedback@lists.sf.net will also work. diff --git a/README.top b/README.top new file mode 100644 index 0000000..3f225d5 --- /dev/null +++ b/README.top @@ -0,0 +1,545 @@ +Credit for this belongs to: +Jim / James C. Warner, + +---------------------------------- + +Ok, ok, I yield -- most of what follows has been removed from the manual page +and packaged separately as this README (hey, it was only TEMPORARY insanity). + +Of course, that means that now absolutely nobody will ever read it. + +This is probably a good thing... + + +## Table of Contents ---------------------------------------------------## + # the only darn thing that wasn't in the man page + CUSTOMIZING the Sources + # the following carry their original topic numbers + DIFFERENCES / New Features + Interface Etiquette + Expanded Configurable Display Support + Enhanced Field/Column Management + Customization Flexibility + NOTES and Rantings + The top Binary + Comparing Performance + Cost of Stuff + The top Sources + EXAMPLES of Windows + The 'A' Mode Command Toggle + STACKIN' & WHACKIN' Windows + ALL TOGETHER Now, Window(s) + + +## CUSTOMIZING the Sources ---------------------------------------------## + +Listed below are the conditionals available should you wish to recompile +this top. The author's favorite is: PRETEND4CPUS. + +That's the #define allowing you to simulate an SMP environment, and +(perhaps) impress your friends. It's currently set to display four +separate CPUs, but could easily be changed. + + Caution: do NOT use this provision in an effort to impress someone + who truly possesses such a machine! The fact that all 4 + CPUs show the same dynamic results will likely have the + opposite effect. + + +//#define ATEOJ_REPORT /* report a bunch of stuff, at end-of-job */ +//#define CASEUP_HEXES /* show any hex values in upper case */ +//#define CASEUP_SCALE /* show scaled time/num suffix upper case */ +//#define CASEUP_SUMMK /* show memory summary kilobytes with 'K' */ +//#define POSIX_CMDLIN /* use '[ ]' for kernel threads, not '( )' */ +//#define PRETEND2_5_X /* pretend we're linux 2.5.x (for IO-wait) */ +//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */ +//#define PRETENDNOCAP /* use a terminal without essential caps */ +//#define SORT_SUPRESS /* *attempt* to reduce qsort overhead */ +//#define STDOUT_IOLBF /* disable our own stdout _IOFBF override */ +//#define USE_LIB_STA3 /* use lib status (3 ch) vs. proc_t (1 ch) */ +//#define WARN_NOT_SMP /* restrict '1' & 'I' commands to true smp */ + + +## 6. DIFFERENCES / New Features ---------------------------------------## + The following summarizes differences between this top and your + former top. It was originally based on procps-2.0.7. However, + except for the separate/summary CPU toggle, all of these differ- + ences also apply through procps-2.0.10. + + 6a. Interface Etiquette + -*- Input and output are far more carefully implemented in + this top. You won't be subjected to 4 - 5 'Unknown command' + messages should you press the wrong key. + + -*- You need suffer a confirmation message only when the results + of a command are not obvious by their effects on the display. + + -*- The Help screen will no longer overflow, even when running + with a 24 row xterm (vt100). + + -*- The fields selection/ordering screens do not carelessly + destroy important information through unintended line wraps. + + -*- Should you narrow a xterm window to less than 80 columns + while this top is running, you will not be left with an + utterly worthless, embarrassing display. + + 6b. Expanded Configurable Display Support + -*- In an SMP environment, you can choose between a summary dis- + play or you may show each cpu separately. No longer must + this choice be irrevocably made at startup. + + -*- There are new fields and with this top, any field is + selectable for sorting. Plus, your sorted column can be + instantly reversed with just a single keystroke. + + -*- You may optionally apply 2 distinct types of highlighting to + running tasks and/or sorted columns. With this top, you'll + be able to instantly spot running tasks and always know the + current sort field. + + -*- While you could continue to use the more familiar (and + boring) monochrome display, you might want to try this top's + new color display. You can even create your own unique col- + ors used in summaries, messages, headings and tasks, each of + which can be made persistent until you choose to change them. + + -*- Up to four separate windows can be displayed simultaneously, + giving you four separate ways to sort and view the tasks cur- + rently cluttering up your system. You could have one view by + pids, another by cpu usage, yet another showing memory con- + sumption. You get the idea... + + -*- Each window comes with pre-configured (but user configurable) + fields and you can size each window individually. + + -*- Virtually every one of this top's options (summaries, fields, + colors, sorted column, etc.) is separately configurable for + each of those four windows. + + Heck, you can even change a window's name, if you don't care + for top's choices. Your changes will be reflected not only + when you're in what top calls alternate-display mode but also + on his special new 'Windows' help screen. + + -*- And, [ ** Drum-Roll + Ta-Da ** ] with just one keystroke you + can quickly switch between full-screen and multiple window + modes! Or, with a different keystroke, toggle a single win- + dow Off for now, then On again later!! + + 6c. Enhanced Field/Column Management + -*- Many Field/Column names have been changed to make them more + intuitive, more self-descriptive. And with this top you + won't be fooled with field choices that are "not yet imple- + mented". + + -*- Task memory statistics are more meaningful and more accurate. + + -*- You'll finally have complete display integrity regardless of + field selections, their order or screen width. And that + means the command column no longer need be kept as the right- + most field, lest your screen turn to when all the + following columns get misaligned. + + 6d. Customization Flexibility + -*- You have complete program naming freedom with no internal + ties to a specific personal configuration file. Symbolic + links could be used to establish different configuration + files reflecting the different personalities of your cus- + tomized "tops", under whatever aliases you've used. + + Thus, you could have an alias for running top in 'Batch + mode', another for when you work from the Linux console and + maybe a third used with X-Windows. All of that, yet still + just a single binary image! + + -*- All of your configuration choices can be preserved in a per- + sonal configuration file, including any changes made on a + per-window basis. Thus, once you personalize things they + remain personalized until you decide to change them again. + This top has been completely cured of: + i-cant-remember-so-please-do-that-all-over-again + ( and again, and again ... ) + + The bottom line is this: if you save your configuration + before quitting top, upon restart the display will appear + exactly as you left it. And that means you no longer have to + keep top running until-the-end-of-time (ok, a long time + anyway), lest your customizations go bye-bye. + + +## 7. NOTES and Rantings -----------------------------------------------## + 7a. The top Binary + To whom it may (should) concern: this top, even with its vastly + expanded capabilities, is only slightly larger than the old top. + Were it not for extensive help text and additional sort callbacks, + it would be smaller. + Throw source carelessly at objectives, it will + produce equally careless machine instructions. + example: (num_pages - an_address)/1024 == duh? + kicker: document result as broken, due to elf! + ---------------------------------------------- + I know you're out there, are you getting this? + + Now, as for all those new capabilities like colors and windows and + highlighting, you'd expect this top to be the "mother of all pigs" + compared to old top -- right? + + Yea, with this top expect following piglets: + . A smaller virtual image and resident footprint + . Slightly fewer major page faults + . A large reduction in minor page faults for SMP + . The same or better response time + . The same or even less CPU costs + + Ideally any comparison of the old and new top should be against + the same libproc format (32-bit or 64-bit tics) and run in a true + or simulated SMP environment (producing separate CPU stats). This + latter requirement will coax old top into handling his own + '/proc/stat' access -- something this top always does, but with + less cost. + + 7b. Comparing Performance + Even with equivalent libraries and '/proc/stat' access, it's dif- + ficult to accurately compare tops using their own displays. + Results for these cpu-intensive programs (who frequently exceed + their time-slice) generally show a wide disparity in %CPU. This + is due to differing call patterns, kernel preemptions and the tim- + ing of process snapshots. For slightly better results, start each + program with the following commands: + ./old-top -d 0.5 + nice -n-10 ./new-top -d 0.4 + + While actually putting this top at a performance disadvantage, the + higher scheduling priority and staggered timing will periodically + yield a somewhat truer picture. You could even reverse those + roles and get similar results. + + The most consistent performance results will be obtained 'off- + line', using your shell's time pipe or the time program itself. + And even in a single processor environment or without equivalent + libraries, total cpu costs (user time + system time) are similar. + + However, this top's cpu costs ARE influenced by the capabilities + you choose to exploit, even if they don't SEEM to be reflected in + such timings. So let's examine some... + + 7c. Cost of Stuff + Colors Cost -- Nada (almost). + Once the terminfo strings are built (at and during a user's + behest) they are SAVED with each window's stuff. And while + there will be extra tty escape sequences transmitted because of + colors, it makes no difference which 'char *' is actually used. + + Highlighting Cost -- Nada (maybe), or blame it on Rio. + On second thought, let's blame it on the user. + + For row highlighting, there is only the cost of those extra tty + escape sequences (same as for colors). For column highlight- + ing, there is a fairly significant cost associated with column + transition management combined with even more tty output. + These increased costs are incurred on every task display row. + + Sooo... hey USER -- do NOT highlight COLUMNS. You shouldn't + need a constant visual reminder of your chosen sort field. + However, if you forget which field top is sorting it can serve + as a quick visual reminder. + + Windows Cost -- Nada (if just 1 window). + If more than 1 window, almost certainly NOT Nada so blame it on + reality. Colors are not an issue, but those sort fields are. + + If we could trust the user to always select the same 'c' state, + 'S' state and sort field (hey, why ya got multiple windows then + user, huh?) AND if we can trust someone to recompile top with a + #define enabled, then we could achieve 'Nada'. + + Ok, not likely, so we're gonna' be doing multiple sorts. BUT, + it may not be as bad as it sounds. Those sorts involve point- + ers only. And, that's as good as it gets ! (right Mr. N?) + + 7d. The top Sources + top.h + Unlike his predecessor, this top has a proper header file. It + contains ONLY declarations, NOT definitions. And there are + several conditionals present to help with further customiza- + tions and experimentation. All are Off by default. + + top.c + Hopefully proves that source code needn't be a disorganized, + misaligned MESS. And, WHO says a source listing shouldn't + occasionally make you SMILE? Why, top.c even does a darn good + job of following the suggestions in a document hardly anybody + seems to observe. + + the Linus Torvalds CodingStyle guidelines ... + -*- -*- -*- on indentation + etc. -*- -*- -*- + well almost all, except for those stinkin'... + + I suppose even Linus Torvalds is entitled to err now and again. + How so you say? Tabs, me' bucko, stinkin' tabs! That, plus the + simplistic position regarding indentation espoused in that other- + wise excellent document. + + -*- Rant On, and on -*- + Let's compare two approaches to the tab/indentation issue with a + small code sample using tabs then spaces. This snippet happens to + be the key to top's use of dynamic colors on many static screens, + while also ensuring screen width isn't exceeded so as to avoid + line wraps. We'll view just the first 40 columns, assuming one + wishes to occasionally provide comments to the right of actual + code (you do, don't you?). + + Then YOU decide which approach makes the most SENSE! + + Stinkin' Tabs versus Spaces: the Linus way + Hey, where'd my +----+----1----+----2----+----3----+----4+ + many code lines | while (*sub_beg) { : + up-and-gone-to? | switch (*sub_end: + | case 0: : + Gosh, wonder if | \ Tabs Induced / : + Linus expects a | case 1: : + fellow to stick | + WASTE-Lands! + case 5: : + his comments on | : + the left side?! | + Not a Living + : + | : + Ever see source | + line-of-code + : + with not enough | : + whitespace; and | / To Be Found! \ : + this is better? | default:: + | : + Oh lookie here, \ } : + there's just a hint of REAL code! ----> if (0 >= room) b: + / } /* end: while 'subtrin: + +----------------------------------------+ + + Spaces versus Stinkin' Tabs: the other way + +----+----1----+----2----+----3----+----4+ + Wow, now this is | while (*sub_beg) { : + Visible hackin'! | switch (*sub_end) { : + | case 0: : + Hmmm, wonder how | *(sub_end + 1) = '\0'; : + many programmers | case 1: case 2: case 3: case: + read those lines | case 5: case 6: case 7: case: + from the LEFT to | cap = Curwin->captab[(int: + the RIGHT? This | *sub_end = '\0'; : + "innovation" may | PUTP("%s%.*s%s", cap, roo: + possibly benefit | room -= (sub_end - sub_be: + those particular | sub_beg = ++sub_end; : + kinds of people, | break; : + you agree? Duh! | default: : + | ++sub_end; : + AND, there might | } : + even be room for | if (0 >= room) break; : + unseen comments! | } /* end: while 'subtrings' */ : + +----------------------------------------+ + + Gosh, I just don't KNOW -- it's such a TOUGH choice... + + Oh you Stinkin' Tabs: correspondence, Who-Cares; documentation, + Oh-Alright; even scripts, Well-If-You-Must. But you have NO place + within the code-space of MY C-source listing! So be gone + already!! + + In Summation... + - If you want to use tabs to the right of the code, go-for-it. + But PLEASE, not ever in the C-source code-space, thank-you- + kindly. Just use three little ol' spaces (exactly 3, no-more, + no-less) where you WOULD have stuck a stinkin' tab. + + We'll get far more READABLE files, much less WAISTED precious + horizontal space, more consistent CURSORS and on, and ON, AND + ON! Plus, without those awful *the-devil's-own-handiwork*, the + aforementioned document need NEVER speak of their EVILS again. + + - Lastly, since SPACES (not stinkin' tabs) are SO beneficial, + maybe we should use just a few more of 'em. Some of those C- + thingies are VERY sensitive -- they don't like being TOUCHED + by any other syntax element! Which ones? Why these guys: + + braces, reserved words and binary operators + ( it's the TRUTH, they told me themselves ) + + It's so EASY to keep 'em HAPPY! And lo-and-behold, the combi- + nation of thingy turns out to be a darn effective bug + repellent, too. So much so, one can actually code while + TOTALLY NUDE yet still avoid them ol' bug-bytes (sic-sic)! + step + down_from + me_punctilious + soap-box_once_again + [1 +5 +5 +5 = huh?] + + +## 4c. EXAMPLES of Windows ---------------------------------------------## + + -*- The 'A' Mode Command Toggle -*- + Here's what you'll see when you first invoke the alternate-display + mode interactive command. + + This particular display was produce on a VT100 xterm, with only 24 + rows. All four task displays are visible, but they could not be sized + the same. Available lines are parceled out in the fairest way possi- + ble so the last two task displays have an extra line each. + + Notice the 'current' window name in the summary area -- it's been + emphasized because the associated task display is visible. Since + 1:Def has a task area, the full range of interactive commands would be + at your disposal. But remember, many of those commands will apply + only to window 1:Def. + + +--------------------------------------+ + 1:Def name is bold, |1:Def - 15:46:37 up 16:25, 9 users, : + thus all commands |Tasks: 76 total, 1 running, 75 sle: + will be available. |Cpu(s): 0.7% user, 1.3% system, : + |Mem: 126588k total, 123688k used,: + |Swap: 265032k total, 8232k used,: + |______________________________________: + Tough luck windows |1__PID_USER______PR__NI_%CPU____TIME+_: + #1 & 2 - you lost | 7343 jtwm 16 0 0.9 0:00.59: + one line each -- | 7339 jtwm 9 0 0.0 0:00.02: + guess you'll just |__7337_root_______9___0__0.0___0:01.30: + have to learn how |2__PID__PPID_Command____________TIME+_: + to live with it. | 997 952 kdeinit 17:59.59: + | 1115 952 kdeinit 2:16.47: + |__1803__1116_led_______________1:55.30: + |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: + The #3 & #4 windows | 4634 12.3 15620 0 15m 860 14m : + better not gloat | 7337 11.3 14396 92 13m 36 13m : + over 1 extra line. | 923 10.6 30524 16m 13m 1120 12m : + That user could yet |___991__7.2__9492__316_9176___12_9164_: + sock 'em with the |4_UID_USER_____GROUP____TTY________PID: + 'n' command and | 43 xfs xfs ? 806: + take those lines, | 0 ykde users pts/7 5561: + plus others, away! | 0 wgnome users pts/7 5560: + | 0 root root pts/7 5325: + +--------------------------------------+ + + So, what say we start applying some of those "full range of interac- + tive commands"? + + Onward + Downward... + + -*- STACKIN' & WHACKIN' Windows -*- + Whoa, hold on mate. Someone has already whacked these windows. See, + there are no task areas for windows 1:Def and 4:Usr. Well, we can at + least retrace their steps... + + Here's what was done, after issuing the 'A' command and entering + alternate-display mode. + 1) When #1 was the 'current' window, '-' was pressed, + toggling Off the associated task display + ( if 'l t m' had been applied to its summary, too ) + ( then there'll be only a msg line when 'current' ) + 2) Then the 'w' key was struck to cycle backward, + making 4:Usr the 'current' window + (could have used 'a a a', if one likes to type) + 3) Then step #1 was repeated, and bye-bye window #4 + 4) Finally, window #2 was made the 'current' window + ( Q. how many keystrokes were used? ) + ( A. minimum of 2: 'a a' or 'w w'. ) + + +--------------------------------------+ + No 'l','t','m','1' |2:Top - 15:48:35 up 16:27, 9 users, : + commands have been |Tasks: 75 total, 1 running, 74 sle: + issued here, |Cpu(s): 2.0% user, 0.7% system, : + but... |Mem: 126588k total, 123712k used,: + |Swap: 265032k total, 8232k used,: + |______________________________________: + #2's been changed; |2__PID__PPID_Command____________TIME+_: + user applied a 'c' | 997 952 kdeinit: konsol 18:00.70: + command (when it | 1115 952 kdeinit: konsol 2:16.47: + was current) - now | 1803 1116 led tiptop.HELP 1:55.30: + shows cmd lines vs. | 923 922 X :0 1:09.60: + program names; | 973 1 klaptopdaemon 0:59.63: + still seems to be | 981 952 /usr/bin/artsd 0:48.63: + sorted on TIME+ | 987 1 kdeinit: kdeskt 0:24.34: + though |___991_____1_kdeinit:_kicker___0:04.59: + |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: + This #3 guy appears | 4634 12.3 15620 0 15m 860 14m : + to still be running | 7337 11.3 14396 92 13m 36 13m : + with the supplied | 923 10.6 30544 16m 13m 1120 12m : + defaults, but no | 991 7.2 9492 316 9176 12 9164 : + telling what damage | 7329 7.0 9036 140 8896 36 8860 : + might have been | 1115 6.9 8956 160 8796 36 8760 : + done to it's | 987 6.4 8668 524 8144 20 8124 : + summary info stuff | 1131 6.4 8268 144 8124 36 8088 : + +--------------------------------------+ + + And that's what brought us to this current state. No, wait. Oh + lordy, will you look at that -- someone has changed the name of win- + dow #2 from 'Job' to 'Top'! + + How'd they do that? Well, they just issued the 'g' interactive com- + mand, of course. That command is available whenever alternate-display + mode is active and always impacts just the 'current' window. Gosh, + you can even issue the 'g' command when 'l' has toggled Off the very + summary area line containing the window name! + + Almost Done... + + -*- ALL TOGETHER Now, Window(s) -*- + Here, the window 1:Def task display has been toggled Off but it + remains the 'current' window. Since there is no task area, many com- + mands will be restricted. However, the commands ('l', 't', 'm', '1') + affecting the summary area, as well as some other global commands + ('k', 'Z', etc.), would still be active. + + Notice that the Mem and Swap lines are not shown. This means that the + loser (oops, user) has, in fact, issued the 'm' command! Now, if you + were to cycle the 'current' window with the 'a' or 'w' commands, the + task display would remain the same (except possibly growing/shrinking + slightly) but the summary area would change periodically. + + The comments to the left of the image provide additional insights into + how things came to be. Note especially the comments for window 4:Usr + -- the one with some empty rows... + + 1:Def no highlight, +--------------------------------------+ + thus disabled cmds: |1:Def - 15:50:32 up 16:29, 9 users, : + b,i,n,u,x,y, etc. |Tasks: 75 total, 2 running, 73 sle: + & m = lost Mem/Swap |Cpu(s): 10.6% user, 0.0% system, : + |______________________________________: + 2:Job was very busy: |2__PID__PPID_Command____________TIME+_: + 'n' cmd, w/ 7 tasks | 80 1 ( khubd ) 0:00.00: + 'c' cmd, cmd line | 6 0 ( kreclaimd ) 0:00.00: + 'O' cmd, sort cmd | 9 1 ( mdrecoveryd ) 0:00.00: + 'R' cmd, sort bkwd | 11358 1 /bin/bash/ /usr 0:00.00: + 'x' cmd, hi column | 1297 1 /sbin/mingetty 0:00.00: + (when 2 WAS current) | 683 1 xinetd -stayali 0:00.00: + |___836_____1_login_--_root_____0:00.00: + 3:Mem has altered |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_: + some std defaults: | 4634 12.3 15620 0 15m 860 14m : + 'y' turned Off | 7337 11.3 14396 92 13m 36 13m : + 'x' turned On | 923 10.6 30544 16m 13m 1120 12m : + (when 3 WAS current) | 991 7.2 9492 316 9176 12 9164 : + |__7329__7.0__9036__140_8896___36_8860_: + Huh? 4:Usr has some |4_UID_USER_____GROUP____TTY________PID: + blank rows! ? ? ? ? | 0 jtwm root pts/2 5561: + Aha, the 'i' command | 0 root root ? 5560: + applied (when 4 WAS | : + current); could be | : + reversed with '=', | : + when 4 IS current! +--------------------------------------+ + + Ok now, how about that 'current' window 1:Def and its unseen tasks? + At any time, you can quickly retrieve lost tasks in a number of ways: + 1) Press '-', toggling just the 'current' window + 2) Press '_', toggling all visible/invisible windows + ( 1:Def is the only window currently not shown ) + ( afterward, it'll be the only window showing! ) + * 3) Press '+', forcing all task displays to become visible + 4) Press 'A' to return to full-screen mode, + with only 1:Def tasks shown and without a window name + + Now that should be enough ways of getting a task area visible again to + satisfy almost any user, don't ya think? + + Note: Use #3 above when you've messed up your screen beyond + redemption. The four task displays will reappear, nice and even. + They will also have retained any customizations you had previously + applied, except for the 'i' (idle tasks) and 'n' (max tasks) com- + mands. + + That's It ! Piece of Cake !! Enjoy them there windows !!! + diff --git a/TODO b/TODO new file mode 100644 index 0000000..7831ea0 --- /dev/null +++ b/TODO @@ -0,0 +1,151 @@ +-------------------------- general ------------------------ + +Consider using glibc obstacks for memory allocation. + +Implement /usr/proc/bin tools like Solaris has. +The prstat command is interesting, like top in batch mode. +SCO has a pstat command. + +Don't these really belong in the procps package? + killall pstree fuser lsof who +(they are maintained elsewhere, which causes version problems) + +OpenBSD has a pfind command. + +Cache results of dev_to_tty. + +---------------------- kernel ------------------------- + +Add an "adopted child" flag to mark processes that are not +natural children of init. This can make --forest work better. + +Supply better data for top's CPU state display. Currently top has +to subtract old numbers from new numbers and divide that result by +the number of processors. The kernel won't even supply the number +of processors in a portable way. + +Supply data for the ADDR and JOBC fields. + +Support & supply data for SL and RE. + +Add a /proc/*/tty symlink to eliminate guessing when /proc/*/fd is +not accessable. + +Add /proc/*/.bindata files to avoid string parsing. It should be an array +of 64-bit values on all machines. New entries go on the end and obsolete +ones get filled in with something logical -- entries must never be deleted! + +Add all the stuff Solaris has. This would also replace ptrace. + +---------------------- watch -------------------------- + +Tolerate UTF-8. + +Tolerate color, bold, underline, etc. #129334 + +Tolerate stderr. #420377 #155227 #225549 + +Tolerate VT100 line-drawing characters. Maybe translate them. + +---------------------- w -------------------------- + +The LOGIN@ column sometimes has a space in it. This makes correct +scripting difficult. + +Time formats are demented. + +---------------------- vmstat -------------------------- + +Extract /proc/diskstats parsing from vmstat into libproc somewhere. + +--------------------- libproc ---------------------- + +Stop storing fields with duplicate info (often different +units: kB and pages, seconds and jiffies) in the proc_t struct. + +Use own readdir code (assembly language) because glibc sucks ass. + +---------------------- top ------------------------- + +Share more stuff with ps. + +don't truncate long usernames + +have a --config option + +---------------- ps for now, maybe move to libproc ------------------ + +With forest output and a tty named /dev/this_is_my_tty, the position +of the command name gets messed up. (we print too many spaces) (fixed?) + +Fix missing stuff for these formats: FB_j FB_l FB_v HP_f HP_l HP_fl JFMT OL_m +(jobc,cpu,sl,re,cpu,prmgrp,m_swap,m_share,vm_lib,m_dt) +Note that "cpu" has two meanings. + +Add Beowulf support. This is ugly, since the current patches use a +daemon to collect info and add a HOST field after the PID field. + +Query optimizer, put cheap/required process selection first. + +Avoid reading both /proc/*/status and /proc/*/stat. + +Support printing the client hostname (the FROM that w(1) uses) in place +of a pty. + +Disambiguate narrow tty info. (/dev/tty7 == /dev/pts/7 now) +1------8 1--4 +ttyS2 S2 +ttyI31 I31 +pts/7 7 Short form could be /999. +pts/9999 9999 Short form could just be trunctuated to /999. +tty7 7 Short form could be vc-7. +tty63 63 Short form could be vc63. + +Internationalization, as specified by XPG3, Volume 1, Commands and Utilities. +(and suggested by Unix98) LC_TIME affects date format. + +----------------------- ps ----------------------- + +Add an option to select all processes that a user can kill. +(related to RUID, EUID, tty, etc. -- but maybe ignore root power) + +Add a nice display option for killing things. +ruser,euser,ppid,pid,pmem,stime,args + +For RT stuff: +pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm + +For job control: +stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm + +Make the column alignment algorithm support this: + FOO BAR + 8 44444 + 453 45 + 45 2989 + 63666 0 + 34 333 +(useful for the UNIX tty and time values, since the time might look +like 100-10:40:32 for old processes and the tty might have extra room) + +Improve long sort/format specifiers documentation and fill in the missing +code as much as the kernel can support. Make sure that memory amounts are in +pages when they should be and in kB when they should be, not backwards. + +output encoding: UTF8 --nul --null + +Make BSD formats use non-standard BSD time format, at least when it +doesn't violate the "no whitespace" rule. + +Better unmangling of '?' as a tty. The shell destroys '?' when there +is a filename that matches. If the argument seems like garbage, +check for a file that might have screwed up the '?'. + +If the 'O' option is given something already implied by 'O', +assume the user wanted a sorting option. + +Conflict: +Digital THREAD is user,pcpu,pri,scnt,wchan,usertime,systime +AIX THREAD is uname,pid,ppid,tid,S,C,PRI,scount,WCHAN,F,tty,bnd,comm +AIX looks like this: + USER PID PPID TID S C PRI SC WCHAN FLAG TTY BND CMD diff --git a/dummy.c b/dummy.c new file mode 100644 index 0000000..95e7824 --- /dev/null +++ b/dummy.c @@ -0,0 +1,31 @@ +// This is to test the compiler. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Foul POS defines all sorts of stuff... +#include +#undef tab + +#include +#include +#include +#include + +int main(int argc, char *argv[]){ + (void)argc; + (void)argv; + return 0; +} diff --git a/free.1 b/free.1 new file mode 100644 index 0000000..954c8d9 --- /dev/null +++ b/free.1 @@ -0,0 +1,47 @@ +.\" -*-Nroff-*- +.\" This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu. +.\" Freely distributable under the terms of the GPL +.TH FREE 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual" +.SH NAME +free \- Display amount of free and used memory in the system +.SH SYNOPSIS +.BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s" +.I delay +.RB "] [" "\-t" "] [" "\-V" ] +.SH DESCRIPTION +\fBfree\fP displays the total amount of free and used physical and swap +memory in the system, as well as the buffers used by the kernel. +The shared memory column should be ignored; it is obsolete. +.SS Options +The \fB-b\fP switch displays the amount of memory in bytes; the +\fB-k\fP switch (set by default) displays it in kilobytes; the \fB-m\fP +switch displays it in megabytes. +.PP +The \fB-t\fP switch displays a line containing the totals. +.PP +The \fB-o\fP switch disables the display of a "buffer adjusted" line. +If the -o option is not specified, \fBfree\fP subtracts buffer memory +from the used memory and adds it to the free memory reported. +.PP +The \fB-s\fP switch activates continuous polling \fIdelay\fP seconds apart. You +may actually specify any floating point number for \fIdelay\fP, +.BR usleep (3) +is used for microsecond resolution delay times. +.PP +The \fB\-V\fP displays version information. +.SH FILES +.ta +.IR /proc/meminfo "\-\- memory information" +.fi + +.SH "SEE ALSO" +.BR ps (1), +.BR slabtop (1), +.BR vmstat (8), +.BR top(1) + +.SH AUTHORS +Written by Brian Edmonds. + +Send bug reports to + diff --git a/free.c b/free.c new file mode 100644 index 0000000..bd78f02 --- /dev/null +++ b/free.c @@ -0,0 +1,122 @@ +// free.c - free(1) +// procps utility to display free memory information +// +// All new, Robert Love 18 Nov 2002 +// Original by Brian Edmonds and Rafal Maszkowski 14 Dec 1992 +// +// This program is licensed under the GNU Library General Public License, v2 +// +// Copyright 2003 Robert Love +// Copyright 2004 Albert Cahalan + +#include "proc/sysinfo.h" +#include "proc/version.h" +//#include +#include +#include +#include +#include +#include +#include + +#define S(X) ( ((unsigned long long)(X) << 10) >> shift) + +const char help_message[] = +"usage: free [-b|-k|-m|-g] [-l] [-o] [-t] [-s delay] [-c count] [-V]\n" +" -b,-k,-m,-g show output in bytes, KB, MB, or GB\n" +" -l show detailed low and high memory statistics\n" +" -o use old format (no -/+buffers/cache line)\n" +" -t display total for RAM + swap\n" +" -s update every [delay] seconds\n" +" -c update [count] times\n" +" -V display version information and exit\n" +; + +int main(int argc, char *argv[]){ + int i; + int count = 0; + int shift = 10; + int pause_length = 0; + int show_high = 0; + int show_total = 0; + int old_fmt = 0; + + /* check startup flags */ + while( (i = getopt(argc, argv, "bkmglotc:s:V") ) != -1 ) + switch (i) { + case 'b': shift = 0; break; + case 'k': shift = 10; break; + case 'm': shift = 20; break; + case 'g': shift = 30; break; + case 'l': show_high = 1; break; + case 'o': old_fmt = 1; break; + case 't': show_total = 1; break; + case 's': pause_length = 1000000 * atof(optarg); break; + case 'c': count = strtoul(optarg, NULL, 10); break; + case 'V': display_version(); exit(0); + default: + fwrite(help_message,1,strlen(help_message),stderr); + return 1; + } + + do { + meminfo(); + printf(" total used free shared buffers cached\n"); + printf( + "%-7s %10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n", "Mem:", + S(kb_main_total), + S(kb_main_used), + S(kb_main_free), + S(kb_main_shared), + S(kb_main_buffers), + S(kb_main_cached) + ); + // Print low vs. high information, if the user requested it. + // Note we check if low_total==0: if so, then this kernel does + // not export the low and high stats. Note we still want to + // print the high info, even if it is zero. + if (show_high) { + printf( + "%-7s %10Lu %10Lu %10Lu\n", "Low:", + S(kb_low_total), + S(kb_low_total - kb_low_free), + S(kb_low_free) + ); + printf( + "%-7s %10Lu %10Lu %10Lu\n", "High:", + S(kb_high_total), + S(kb_high_total - kb_high_free), + S(kb_high_free) + ); + } + if(!old_fmt){ + unsigned KLONG buffers_plus_cached = kb_main_buffers + kb_main_cached; + printf( + "-/+ buffers/cache: %10Lu %10Lu\n", + S(kb_main_used - buffers_plus_cached), + S(kb_main_free + buffers_plus_cached) + ); + } + printf( + "%-7s %10Lu %10Lu %10Lu\n", "Swap:", + S(kb_swap_total), + S(kb_swap_used), + S(kb_swap_free) + ); + if(show_total){ + printf( + "%-7s %10Lu %10Lu %10Lu\n", "Total:", + S(kb_main_total + kb_swap_total), + S(kb_main_used + kb_swap_used), + S(kb_main_free + kb_swap_free) + ); + } + if(pause_length){ + fputc('\n', stdout); + fflush(stdout); + if (count != 1) usleep(pause_length); + } + } while(pause_length && --count); + + return 0; +} diff --git a/kill.1 b/kill.1 new file mode 100644 index 0000000..aef9dbf --- /dev/null +++ b/kill.1 @@ -0,0 +1,118 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for kill. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan; converted to a man page by +.\" Michael K. Johnson +.TH KILL 1 "November 21, 1999" "Linux" "Linux User's Manual" +.SH NAME +kill \- send a signal to a process + +.SH SYNOPSIS +.TS +l l. +kill pid ... Send SIGTERM to every process listed. +kill -signal pid ... Send a signal to every process listed. +kill -s signal pid ... Send a signal to every process listed. +kill -l List all signal names. +kill -L List all signal names in a nice table. +kill -l signal Convert a signal number into a name. +kill -V,--version Show version of program +.TE + +.SH DESCRIPTION +The default signal for kill is TERM. Use -l or -L to list available signals. +Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0. +Alternate signals may be specified in three ways: -9 -SIGKILL -KILL. +Negative PID values may be used to choose whole process groups; see the +PGID column in ps command output. A PID of -1 is special; it indicates +all processes except the kill process itself and init. + +.SH SIGNALS +The signals listed below may be available for use with kill. +When known constant, numbers and default behavior are shown. + +.TS +lB rB lB lB +lfCW r l l. +Name Num Action Description +.TH +0 0 n/a exit code indicates if a signal may be sent +ALRM 14 exit +HUP 1 exit +INT 2 exit +KILL 9 exit this signal may not be blocked +PIPE 13 exit +POLL exit +PROF exit +TERM 15 exit +USR1 exit +USR2 exit +VTALRM exit +STKFLT exit may not be implemented +PWR ignore may exit on some systems +WINCH ignore +CHLD ignore +URG ignore +TSTP stop may interact with the shell +TTIN stop may interact with the shell +TTOU stop may interact with the shell +STOP stop this signal may not be blocked +CONT restart continue if stopped, otherwise ignore +ABRT 6 core +FPE 8 core +ILL 4 core +QUIT 3 core +SEGV 11 core +TRAP 5 core +SYS core may not be implemented +EMT core may not be implemented +BUS core core dump may fail +XCPU core core dump may fail +XFSZ core core dump may fail +.TE + +.SH NOTES +Your shell (command line interpreter) may have a built-in kill command. +You may need to run the command described here as /bin/kill to solve +the conflict. + +.SH EXAMPLES + +.SS +.B "kill -9 -1" +.nf +Kill all processes you can kill. +.fi +.PP +.SS +.B "kill -l 11" +.nf +Translate number 11 into a signal name. +.fi +.PP +.SS +.B "kill -L" +.nf +List the available signal choices in a nice table. +.fi +.PP +.SS +.B "kill 123 543 2341 3453" +.nf +Send the default signal, SIGTERM, to all those processes. +.fi +.PP +.SH "SEE ALSO" +pkill(1) skill(1) kill(2) renice(1) nice(1) signal(7) killall(1) + +.SH STANDARDS +This command meets appropriate standards. The -L flag is Linux-specific. + +.SH AUTHOR +Albert Cahalan wrote kill in 1999 to replace a +bsdutils one that was not standards compliant. The util-linux one might +also work correctly. + +Please send bug reports to diff --git a/minimal.c b/minimal.c new file mode 100644 index 0000000..a38e4c7 --- /dev/null +++ b/minimal.c @@ -0,0 +1,670 @@ +/* + * Copyright 1998,2004 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* This is a minimal /bin/ps, designed to be smaller than the old ps + * while still supporting some of the more important features of the + * new ps. (for total size, note that this ps does not need libproc) + * It is suitable for Linux-on-a-floppy systems only. + * + * Maintainers: do not compile or install for normal systems. + * Anyone needing this will want to tweak their compiler anyway. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEV_ENCODE(M,m) ( \ + ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \ +) + +/////////////////////////////////////////////////////// +#ifdef __sun__ +#include +#define _STRUCTURED_PROC 1 +#include +#define NO_TTY_VALUE DEV_ENCODE(-1,-1) +#define HZ 1 // only bother with seconds +#endif + +/////////////////////////////////////////////////////// +#ifdef __FreeBSD__ +#include +#include +#include +#include +#include +#define NO_TTY_VALUE DEV_ENCODE(-1,-1) +#define HZ 1 // only bother with seconds +#endif + +/////////////////////////////////////////////////////// +#ifdef __linux__ +#include /* HZ */ +#include /* PAGE_SIZE */ +#define NO_TTY_VALUE DEV_ENCODE(0,0) +#ifndef HZ +#warning HZ not defined, assuming it is 100 +#define HZ 100 +#endif +#endif + +/////////////////////////////////////////////////////////// + +#ifndef PAGE_SIZE +#warning PAGE_SIZE not defined, assuming it is 4096 +#define PAGE_SIZE 4096 +#endif + + + +static char P_tty_text[16]; +static char P_cmd[16]; +static char P_state; +static int P_euid; +static int P_pid; +static int P_ppid, P_pgrp, P_session, P_tty_num, P_tpgid; +static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime; +static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_alarm; +static unsigned long P_start_time, P_vsize; +static long P_rss; +static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip; +static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch; +static unsigned long P_wchan, P_nswap, P_cnswap; + + + +#if 0 +static int screen_cols = 80; +static int w_count; +#endif + +static int want_one_pid; +static const char *want_one_command; +static int select_notty; +static int select_all; + +static int ps_format; +static int old_h_option; + +/* we only pretend to support this */ +static int show_args; /* implicit with -f and all BSD options */ +static int bsd_c_option; /* this option overrides the above */ + +static int ps_argc; /* global argc */ +static char **ps_argv; /* global argv */ +static int thisarg; /* index into ps_argv */ +static char *flagptr; /* current location in ps_argv[thisarg] */ + + + + +static void usage(void){ + fprintf(stderr, + "-C select by command name (minimal ps only accepts one)\n" + "-p select by process ID (minimal ps only accepts one)\n" + "-e all processes (same as ax)\n" + "a all processes w/ tty, including other users\n" + "x processes w/o controlling ttys\n" + "-f full format\n" + "-j,j job control format\n" + "v virtual memory format\n" + "-l,l long format\n" + "u user-oriented format\n" + "-o user-defined format (limited support, only \"ps -o pid=\")\n" + "h no header\n" +/* + "-A all processes (same as ax)\n" + "c true command name\n" + "-w,w wide output\n" +*/ + ); + exit(1); +} + +/* + * Return the next argument, or call the usage function. + * This handles both: -oFOO -o FOO + */ +static const char *get_opt_arg(void){ + const char *ret; + ret = flagptr+1; /* assume argument is part of ps_argv[thisarg] */ + if(*ret) return ret; + if(++thisarg >= ps_argc) usage(); /* there is nothing left */ + /* argument is the new ps_argv[thisarg] */ + ret = ps_argv[thisarg]; + if(!ret || !*ret) usage(); + return ret; +} + + +/* return the PID, or 0 if nothing good */ +static void parse_pid(const char *str){ + char *endp; + int num; + if(!str) goto bad; + num = strtol(str, &endp, 0); + if(*endp != '\0') goto bad; + if(num<1) goto bad; + if(want_one_pid) goto bad; + want_one_pid = num; + return; +bad: + usage(); +} + +/***************** parse SysV options, including Unix98 *****************/ +static void parse_sysv_option(void){ + do{ + switch(*flagptr){ + /**** selection ****/ + case 'C': /* end */ + if(want_one_command) usage(); + want_one_command = get_opt_arg(); + return; /* can't have any more options */ + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + case 'A': + case 'e': + select_all++; + select_notty++; +case 'w': /* here for now, since the real one is not used */ + break; + /**** output format ****/ + case 'f': + show_args = 1; + /* FALL THROUGH */ + case 'j': + case 'l': + if(ps_format) usage(); + ps_format = *flagptr; + break; + case 'o': /* end */ + /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */ + if(strcmp(get_opt_arg(),"pid=")) usage(); + if(ps_format) usage(); + ps_format = 'o'; + old_h_option++; + return; /* can't have any more options */ + /**** other stuff ****/ +#if 0 + case 'w': + w_count++; + break; +#endif + default: + usage(); + } /* switch */ + }while(*++flagptr); +} + +/************************* parse BSD options **********************/ +static void parse_bsd_option(void){ + do{ + switch(*flagptr){ + /**** selection ****/ + case 'a': + select_all++; + break; + case 'x': + select_notty++; + break; + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + /**** output format ****/ + case 'j': + case 'l': + case 'u': + case 'v': + if(ps_format) usage(); + ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */ + break; + /**** other stuff ****/ + case 'c': + bsd_c_option++; +#if 0 + break; +#endif + case 'w': +#if 0 + w_count++; +#endif + break; + case 'h': + old_h_option++; + break; + default: + usage(); + } /* switch */ + }while(*++flagptr); +} + +#if 0 +#include +/* not used yet */ +static void choose_dimensions(void){ + struct winsize ws; + char *columns; + /* screen_cols is 80 by default */ + if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col; + columns = getenv("COLUMNS"); + if(columns && *columns){ + long t; + char *endptr; + t = strtol(columns, &endptr, 0); + if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t; + } + if(w_count && (screen_cols<132)) screen_cols=132; + if(w_count>1) screen_cols=999999999; +} +#endif + +static void arg_parse(int argc, char *argv[]){ + int sel = 0; /* to verify option sanity */ + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + /**** iterate over the args ****/ + while(++thisarg < ps_argc){ + flagptr = ps_argv[thisarg]; + switch(*flagptr){ + case '0' ... '9': + show_args = 1; + parse_pid(flagptr); + break; + case '-': + flagptr++; + parse_sysv_option(); + break; + default: + show_args = 1; + parse_bsd_option(); + break; + } + } + /**** sanity check and clean-up ****/ + if(want_one_pid) sel++; + if(want_one_command) sel++; + if(select_notty || select_all) sel++; + if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage(); + if(bsd_c_option) show_args = 0; +} + +#ifdef __sun__ +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) { + struct psinfo p; // /proc/*/psinfo, struct psinfo, psinfo_t + char buf[32]; + int num; + int fd; + int tty_maj, tty_min; + snprintf(buf, sizeof buf, "/proc/%d/psinfo", pid); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0; + num = read(fd, &p, sizeof p); + close(fd); + if(num != sizeof p) return 0; + + num = PRFNSZ; + if (num >= sizeof P_cmd) num = sizeof P_cmd - 1; + memcpy(P_cmd, p.pr_fname, num); // p.pr_fname or p.pr_lwp.pr_name + P_cmd[num] = '\0'; + + P_pid = p.pr_pid; + P_ppid = p.pr_ppid; + P_pgrp = p.pr_pgid; + P_session = p.pr_sid; + P_euid = p.pr_euid; + P_rss = p.pr_rssize; + P_vsize = p.pr_size; + P_start_time = p.pr_start.tv_sec; + P_wchan = p.pr_lwp.pr_wchan; + P_state = p.pr_lwp.pr_sname; + P_nice = p.pr_lwp.pr_nice; + P_priority = p.pr_lwp.pr_pri; // or pr_oldpri +// P_ruid = p.pr_uid; +// P_rgid = p.pr_gid; +// P_egid = p.pr_egid; + +#if 0 + // don't support these + P_tpgid; P_flags, + P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime; + P_cutime, P_cstime, P_timeout, P_alarm; + P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip; + P_signal, P_blocked, P_sigignore, P_sigcatch; + P_nswap, P_cnswap; +#endif + + // we like it Linux-encoded :-) + tty_maj = major(p.pr_ttydev); + tty_min = minor(p.pr_ttydev); + P_tty_num = DEV_ENCODE(tty_maj,tty_min); + + snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min); +#if 1 + if (tty_maj == 24) snprintf(P_tty_text, sizeof P_tty_text, "pts/%-3u", tty_min); + if (P_tty_num == NO_TTY_VALUE) memcpy(P_tty_text, " ? ", 8); + if (P_tty_num == DEV_ENCODE(0,0)) memcpy(P_tty_text, "console", 8); +#endif + + if(P_pid != pid) return 0; + return 1; +} +#endif + +#ifdef __FreeBSD__ +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) { + char buf[400]; + int num; + int fd; + char* tmp; + int tty_maj, tty_min; + snprintf(buf, 32, "/proc/%d/status", pid); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0; + num = read(fd, buf, sizeof buf - 1); + close(fd); + if(num<43) return 0; + buf[num] = '\0'; + + P_state = '-'; + + // FreeBSD /proc/*/status is seriously fucked. Unlike the Linux + // files, we can't use strrchr to find the end of a command name. + // Spaces in command names do not get escaped. To avoid spoofing, + // one may skip 20 characters and then look _forward_ only to + // find a pattern of entries that are {with,with,without} a comma. + // The entry without a comma is wchan. Then count backwards! + // + // Don't bother for now. FreeBSD isn't worth the trouble. + + tmp = strchr(buf,' '); + num = tmp - buf; + if (num >= sizeof P_cmd) num = sizeof P_cmd - 1; + memcpy(P_cmd,buf,num); + P_cmd[num] = '\0'; + + num = sscanf(tmp+1, + "%d %d %d %d " + "%d,%d " + "%*s " + "%ld,%*d " + "%ld,%*d " + "%ld,%*d " + "%*s " + "%d %d ", + &P_pid, &P_ppid, &P_pgrp, &P_session, + &tty_maj, &tty_min, + /* SKIP funny flags thing */ + &P_start_time, /* SKIP microseconds */ + &P_utime, /* SKIP microseconds */ + &P_stime, /* SKIP microseconds */ + /* SKIP &P_wchan, for now -- it is a string */ + &P_euid, &P_euid // don't know which is which + ); +/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + + snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min); + P_tty_num = DEV_ENCODE(tty_maj,tty_min); +// tty decode is 224 to 256 bytes on i386 +#if 1 + tmp = NULL; + if (tty_maj == 5) tmp = " ttyp%c "; + if (tty_maj == 12) tmp = " ttyv%c "; + if (tty_maj == 28) tmp = " ttyd%c "; + if (P_tty_num == NO_TTY_VALUE) tmp = " ? "; + if (P_tty_num == DEV_ENCODE(0,0)) tmp = "console"; + if (P_tty_num == DEV_ENCODE(12,255)) tmp = "consolectl"; + if (tmp) { + snprintf( + P_tty_text, + sizeof P_tty_text, + tmp, + "0123456789abcdefghijklmnopqrstuvwxyz"[tty_min&31] + ); + } +#endif + + if(num < 9) return 0; + if(P_pid != pid) return 0; + return 1; +} +#endif + +#ifdef __linux__ +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) { + char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */ + int num; + int fd; + char* tmp; + struct stat sb; /* stat() used to get EUID */ + snprintf(buf, 32, "/proc/%d/stat", pid); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0; + num = read(fd, buf, sizeof buf - 1); + fstat(fd, &sb); + P_euid = sb.st_uid; + close(fd); + if(num<80) return 0; + buf[num] = '\0'; + tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ + *tmp = '\0'; /* replace trailing ')' with NUL */ + /* parse these two strings separately, skipping the leading "(". */ + memset(P_cmd, 0, sizeof P_cmd); /* clear */ + sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */ + num = sscanf(tmp + 2, /* skip space after ')' too */ + "%c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %ld %ld " + "%lu %lu " + "%ld " + "%lu %lu %lu %lu %lu %lu " + "%u %u %u %u " /* no use for RT signals */ + "%lu %lu %lu", + &P_state, + &P_ppid, &P_pgrp, &P_session, &P_tty_num, &P_tpgid, + &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime, + &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_alarm, + &P_start_time, &P_vsize, + &P_rss, + &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip, + &P_signal, &P_blocked, &P_sigignore, &P_sigcatch, + &P_wchan, &P_nswap, &P_cnswap + ); +/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + P_vsize /= 1024; + P_rss *= (PAGE_SIZE/1024); + + memcpy(P_tty_text, " ? ", 8); + if (P_tty_num != NO_TTY_VALUE) { + int tty_maj = (P_tty_num>>8)&0xfff; + int tty_min = (P_tty_num&0xff) | ((P_tty_num>>12)&0xfff00); + snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min); + } + + if(num < 30) return 0; + if(P_pid != pid) return 0; + return 1; +} +#endif + +static const char *do_time(unsigned long t){ + int hh,mm,ss; + static char buf[32]; + int cnt = 0; + t /= HZ; + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t); + snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss); + return buf; +} + +static const char *do_user(void){ + static char buf[32]; + static struct passwd *p; + static int lastuid = -1; + if(P_euid != lastuid){ + p = getpwuid(P_euid); + if(p) snprintf(buf, sizeof buf, "%-8.8s", p->pw_name); + else snprintf(buf, sizeof buf, "%5d ", P_euid); + } + return buf; +} + +static const char *do_cpu(int longform){ + static char buf[8]; + strcpy(buf," - "); + if(!longform) buf[2] = '\0'; + return buf; +} + +static const char *do_mem(int longform){ + static char buf[8]; + strcpy(buf," - "); + if(!longform) buf[2] = '\0'; + return buf; +} + +static const char *do_stime(void){ + static char buf[32]; + strcpy(buf," - "); + return buf; +} + +static void print_proc(void){ + switch(ps_format){ + case 0: + printf("%5d %s %s", P_pid, P_tty_text, do_time(P_utime+P_stime)); + break; + case 'o': + printf("%d\n", P_pid); + return; /* don't want the command */ + case 'l': + printf( + "0 %c %5d %5d %5d %s %3d %3d - " + "%5ld %06x %s %s", + P_state, P_euid, P_pid, P_ppid, do_cpu(0), + (int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024), + (unsigned)(P_wchan&0xffffff), P_tty_text, do_time(P_utime+P_stime) + ); + break; + case 'f': + printf( + "%8s %5d %5d %s %s %s %s", + do_user(), P_pid, P_ppid, do_cpu(0), do_stime(), P_tty_text, do_time(P_utime+P_stime) + ); + break; + case 'j': + printf( + "%5d %5d %5d %s %s", + P_pid, P_pgrp, P_session, P_tty_text, do_time(P_utime+P_stime) + ); + break; + case 'u'|0x80: + printf( + "%8s %5d %s %s %5ld %4ld %s %c %s %s", + do_user(), P_pid, do_cpu(1), do_mem(1), P_vsize, P_rss, P_tty_text, P_state, + do_stime(), do_time(P_utime+P_stime) + ); + break; + case 'v'|0x80: + printf( + "%5d %s %c %s %6d - - %5d %s", + P_pid, P_tty_text, P_state, do_time(P_utime+P_stime), (int)P_maj_flt, + (int)P_rss, do_mem(1) + ); + break; + case 'j'|0x80: + printf( + "%5d %5d %5d %5d %s %5d %c %5d %s", + P_ppid, P_pid, P_pgrp, P_session, P_tty_text, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime) + ); + break; + case 'l'|0x80: + printf( + "0 %5d %5d %5d %3d %3d " + "%5ld %4ld %06x %c %s %s", + P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice, + P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, P_tty_text, do_time(P_utime+P_stime) + ); + break; + default: + ; + } + if(show_args) printf(" [%s]\n", P_cmd); + else printf(" %s\n", P_cmd); +} + + +int main(int argc, char *argv[]){ + arg_parse(argc, argv); +#if 0 + choose_dimensions(); +#endif + if(!old_h_option){ + const char *head; + switch(ps_format){ + default: /* can't happen */ + case 0: head = " PID TTY TIME CMD"; break; + case 'l': head = "F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break; + case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break; + case 'j': head = " PID PGID SID TTY TIME CMD"; break; + case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break; + case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break; + case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break; + case 'l'|0x80: head = "F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break; + } + printf("%s\n",head); + } + if(want_one_pid){ + if(stat2proc(want_one_pid)) print_proc(); + else exit(1); + }else{ + struct dirent *ent; /* dirent handle */ + DIR *dir; + int ouruid; + int found_a_proc; + found_a_proc = 0; + ouruid = getuid(); + dir = opendir("/proc"); + while(( ent = readdir(dir) )){ + if(*ent->d_name<'0' || *ent->d_name>'9') continue; + if(!stat2proc(atoi(ent->d_name))) continue; + if(want_one_command){ + if(strcmp(want_one_command,P_cmd)) continue; + }else{ + if(!select_notty && P_tty_num==NO_TTY_VALUE) continue; + if(!select_all && P_euid!=ouruid) continue; + } + found_a_proc++; + print_proc(); + } + closedir(dir); + exit(!found_a_proc); + } + return 0; +} diff --git a/packaging/FAQ b/packaging/FAQ new file mode 100644 index 0000000..26f10dc --- /dev/null +++ b/packaging/FAQ @@ -0,0 +1,82 @@ +Why does "ps -aux" complain about a bogus '-'? + +According to the POSIX and UNIX standards, the above command asks to display +all processes with a TTY (generally the commands users are running) plus all +processes owned by a user named "x". If that user doesn't exist, then ps will +assume you really meant "ps aux". The warning is given to gently break you of a +habit that will cause you trouble if a user named "x" were created. + + +Why don't I see SMP (per-CPU) stats in top? + +You didn't enable it. Press '?' for built-in help or read the man page. Per-CPU +stats are disabled by default because they take up too much space. Some Linux +systems have hundreds of CPUs. + + +Why do long usernames get printed as numbers? + +The UNIX and POSIX standards require that user names and group names be printed +as decimal integers when there is not enough room in the column. Truncating the +names, besides being a violation of the standard, would lead to confusion +between names like MichelleRichards and MichelleRichardson. The UNIX and POSIX +way to change column width is to rename the column: + + ps -o pid,user=CumbersomeUserNames -o comm + +The easy way is to directly specify the desired width: + + ps -o pid,user:19,comm + + +Why is %CPU underreported for multi-threaded (Java, etc.) apps? + +You need to upgrade to the 2.6.10 kernel at least. Older kernels do not provide +a reasonable way to get this information. + + +Why do ps and top show threads individually? + +The 2.4.xx kernel does not provide proper support for grouping threads by +process. Hacks exist to group them anyway, but such hacks will falsely group +similar tasks and will fail to group tasks due to race conditions. The hacks +are also slow. As none of this is acceptable in a critical system tool, task +grouping is not currently available for the 2.4.xx kernel. The 2.6.xx kernel +allows for proper thread grouping and reporting. To take advantage of this, +your programs must use a threading library that features the CLONE_THREAD flag. +The NPTL pthreads provided by recent glibc releases use CLONE_THREAD. + + +What systems are supported? + +Linux 2.4.xx and 2.6.xx are commonly tested and expected to work well. SMP is +well supported. Multi-node cluster views require a multi-node /proc filesystem; +without that you will see a single-node view. + + +Where to I send bug reports? + +You may use the Debian bug tracking system or send your report to +procps-feedback@lists.sf.net (no subscription required) instead. + + +Why are there so many procps projects? + +The original maintainer seems to have had little time for procps. Whatever his +reasons, the project didn't get maintained. Starting in 1997, Albert Cahalan +wrote a new ps program for the package. For the next few years, Albert quietly +helped the Debian package maintainer fix bugs. In 2001, Rik van Riel decided to +do something about what appeared to be the lack of a maintainer. He picked up +the buggy old code in Red Hat's CVS and started adding patches. Meanwhile, +other people have patched procps in a great many ways. In 2002, Albert moved +procps to this site. This was done to ensure that years of testing and bug +fixes would not be lost. The major version number was changed to 3, partly to +avoid confusing users and partly because the top program has been redone. + + +Why does ps get signal 17? + +No ps release has ever had this problem. Most likely your system has been +broken into. You might want to install a more recent version of the OS. If +you'd rather take your chances, simply upgrade procps. + diff --git a/packaging/MD5SUM b/packaging/MD5SUM new file mode 100644 index 0000000..ef79eeb --- /dev/null +++ b/packaging/MD5SUM @@ -0,0 +1 @@ +9532714b6846013ca9898984ba4cd7e0 procps-3.2.8.tar.gz diff --git a/packaging/fix-makefile-error-with-as-needed.patch b/packaging/fix-makefile-error-with-as-needed.patch new file mode 100644 index 0000000..5282a42 --- /dev/null +++ b/packaging/fix-makefile-error-with-as-needed.patch @@ -0,0 +1,13 @@ +diff --git a/Makefile b/Makefile +index 3f67bbf..e4a1adc 100644 +--- a/Makefile ++++ b/Makefile +@@ -67,7 +67,7 @@ TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \ + # plus the top-level Makefile to make it work stand-alone. + _TARFILES := Makefile + +-CURSES := -lncursesw ++CURSES := -ltinfo -lncursesw + + # This seems about right for the dynamic library stuff. + # Something like this is probably needed to make the SE Linux diff --git a/packaging/procps-3.1.15-misc.patch b/packaging/procps-3.1.15-misc.patch new file mode 100644 index 0000000..047674b --- /dev/null +++ b/packaging/procps-3.1.15-misc.patch @@ -0,0 +1,35 @@ +--- procps-3.2.2/watch.c.misc 2004-07-10 14:58:26.000000000 -0400 ++++ procps-3.2.2/watch.c 2004-07-19 10:54:05.905274300 -0400 +@@ -24,6 +24,9 @@ + #include + #include + #include "proc/procps.h" ++#include ++#include ++#include + + static struct option longopts[] = { + {"differences", optional_argument, 0, 'd'}, +@@ -93,6 +96,7 @@ + main(int argc, char *argv[]) + { + int optc; ++ int fd; + int option_differences = 0, + option_differences_cumulative = 0, + option_help = 0, option_version = 0; +@@ -213,7 +217,13 @@ + mvaddstr(0, width - tsl + 1, ts); + free(header); + } +- ++ close(0); ++ fd=open("/dev/null",O_RDWR); ++ if (fd!=0) { ++ perror("open"); ++ do_exit(2); ++ } ++ + if (!(p = popen(command, "r"))) { + perror("popen"); + do_exit(2); diff --git a/packaging/procps-3.2.1-selinux-workaround.patch b/packaging/procps-3.2.1-selinux-workaround.patch new file mode 100644 index 0000000..dde47e3 --- /dev/null +++ b/packaging/procps-3.2.1-selinux-workaround.patch @@ -0,0 +1,13 @@ +--- procps-3.2.1/w.c.selinux-workaround 2003-09-20 02:42:26.000000000 -0400 ++++ procps-3.2.1/w.c 2004-06-15 10:45:53.000000000 -0400 +@@ -162,6 +162,10 @@ + if(best && tmp->start_time <= best->start_time) continue; + best = tmp; + } ++ /* It is there but SELinux wouldn't allow us to know the detail. Really ++ w should just be given rights */ ++ if(!kill(u->ut_pid, 0) || errno != ESRCH) ++ *found_utpid = 1; + return best ? best : secondbest; + } + diff --git a/packaging/procps-3.2.3-FAQ.patch b/packaging/procps-3.2.3-FAQ.patch new file mode 100644 index 0000000..df8dabb --- /dev/null +++ b/packaging/procps-3.2.3-FAQ.patch @@ -0,0 +1,46 @@ +--- procps-3.2.6/proc/version.h.FAQ 2002-12-15 01:08:32.000000000 +0100 ++++ procps-3.2.6/proc/version.h 2005-10-31 20:29:18.000000000 +0100 +@@ -16,6 +16,7 @@ + + extern void display_version(void); /* display suite version */ + extern const char procps_version[]; /* global buf for suite version */ ++extern const char procps_number_version[]; /* global buf for suite number version */ + + extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE + in /usr/include/linux/version.h */ +--- procps-3.2.6/proc/version.c.FAQ 2003-01-29 02:11:43.000000000 +0100 ++++ procps-3.2.6/proc/version.c 2005-10-31 20:29:18.000000000 +0100 +@@ -16,8 +16,10 @@ + + #ifdef MINORVERSION + const char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION; ++const char procps_number_version[] = VERSION "." SUBVERSION "." MINORVERSION; + #else +-const char procps_version[] = "procps version " VERSION "." SUBVERSION; ++const char procps_version[] = "procps version " VERSION "." SUBVERSION; ++const char procps_number_version[] = VERSION "." SUBVERSION; + #endif + + void display_version(void) { +--- procps-3.2.6/proc/library.map.FAQ 2005-03-14 05:32:40.000000000 +0100 ++++ procps-3.2.6/proc/library.map 2005-10-31 20:32:39.000000000 +0100 +@@ -9,7 +9,7 @@ + escape_str; escape_strlist; + openproc; closeproc; + tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan; +- display_version; procps_version; linux_version_code; ++ display_version; procps_version; procps_number_version; linux_version_code; + Hertz; smp_num_cpus; have_privs; + sprint_uptime; uptime; user_from_uid; print_uptime; loadavg; + pretty_print_signals; print_given_signals; unix_print_signals; signal_name_to_number; signal_number_to_name; +--- procps-3.2.6/ps/parser.c.FAQ 2005-10-31 20:29:18.000000000 +0100 ++++ procps-3.2.6/ps/parser.c 2005-10-31 20:29:18.000000000 +0100 +@@ -1238,7 +1238,7 @@ + // as SysV options... and you're screwed if you've been patching + // out the friendly warning. Cut-over is likely to be in 2005. + if(!(personality & PER_FORCE_BSD)) +- fprintf(stderr, "Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html\n"); ++ fprintf(stderr, "Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-%s/FAQ\n",procps_number_version); + // Remember: contact albert@users.sf.net or procps-feedback@lists.sf.net + // if you should feel tempted. Be damn sure you understand all + // the issues. The same goes for other stuff too, BTW. Please ask. diff --git a/packaging/procps-3.2.3-noproc.patch b/packaging/procps-3.2.3-noproc.patch new file mode 100644 index 0000000..d373473 --- /dev/null +++ b/packaging/procps-3.2.3-noproc.patch @@ -0,0 +1,18 @@ +--- procps-3.2.3/top.c~ 2004-09-28 13:39:31.352488862 -0400 ++++ procps-3.2.3/top.c 2004-09-28 13:42:47.659416849 -0400 +@@ -3201,12 +3201,15 @@ + + int main (int dont_care_argc, char *argv[]) + { ++ struct stat isproc; + (void)dont_care_argc; + before(*argv); + // +-------------+ + windows_stage1(); // top (sic) slice + configs_read(); // > spread etc, < + parse_args(&argv[1]); // > lean stuff, < ++ if (stat("/proc/self", &isproc)==-1) ++ std_err("/proc is not mounted, required for output data"); + whack_terminal(); // > onions etc. < + windows_stage2(); // as bottom slice + // +-------------+ diff --git a/packaging/procps-3.2.3-pseudo.patch b/packaging/procps-3.2.3-pseudo.patch new file mode 100644 index 0000000..4a00b61 --- /dev/null +++ b/packaging/procps-3.2.3-pseudo.patch @@ -0,0 +1,18 @@ +--- procps-3.2.5/top.h.pseudo 2005-03-17 13:32:05.305659080 +0100 ++++ procps-3.2.5/top.h 2005-03-17 13:34:04.837487480 +0100 +@@ -149,8 +149,13 @@ + int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ + if (Batch) _ptr = _str; \ + else { \ +- _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ +- if (memcmp(_ptr, _str, _len)) { \ ++ int _rest; \ ++ _ptr = &Pseudo_scrn[Pseudo_row * Pseudo_cols]; \ ++ _rest = Pseudo_size-(_ptr-Pseudo_scrn); \ ++ Pseudo_row = (Pseudo_row+1) % Screen_rows; \ ++ if (_rest < _len) \ ++ _len = _rest; \ ++ if (memcmp(_ptr, _str, _len)) { \ + memcpy(_ptr, _str, _len); \ + } else { \ + _ptr = "\n"; \ diff --git a/packaging/procps-3.2.4-0x9b.patch b/packaging/procps-3.2.4-0x9b.patch new file mode 100644 index 0000000..6ba79e8 --- /dev/null +++ b/packaging/procps-3.2.4-0x9b.patch @@ -0,0 +1,27 @@ +--- procps-3.2.4beta/proc/escape.c.0x9b 2004-10-23 19:44:28.000000000 +0200 ++++ procps-3.2.4beta/proc/escape.c 2004-11-04 15:22:00.196344424 +0100 +@@ -79,6 +79,7 @@ + // multibyte - printable + // Got space? + if (my_cells+wlen > *maxcells || my_bytes+1+len >= bufsize) break; ++#ifdef OUTPUT_PARANOID + // 0x9b is control byte for some terminals + if (memchr(src, 0x9B, len)) { + // unsafe multibyte +@@ -87,13 +88,16 @@ + my_cells++; + my_bytes++; + } else { ++#endif + // safe multibyte + memcpy(dst, src, len); + my_cells += wlen; + dst += len; + my_bytes += len; + src += len; ++#ifdef OUTPUT_PARANOID + } ++#endif + } + } + //fprintf(stdout, "cells: %d\n", my_cells); diff --git a/packaging/procps-3.2.5-sysctl-writeonly.patch b/packaging/procps-3.2.5-sysctl-writeonly.patch new file mode 100644 index 0000000..3e04ccf --- /dev/null +++ b/packaging/procps-3.2.5-sysctl-writeonly.patch @@ -0,0 +1,55 @@ +--- procps-3.2.6/sysctl.c.writeonly 2005-01-05 22:00:47.000000000 +0100 ++++ procps-3.2.6/sysctl.c 2005-11-08 11:24:19.000000000 +0100 +@@ -127,7 +127,8 @@ + char *restrict tmpname; + char *restrict outname; + char inbuf[1025]; +- FILE *restrict fp; ++ FILE *restrict fp = NULL; ++ struct stat st; + + if (!name || !*name) { + fprintf(stderr, ERR_INVALID_KEY, name); +@@ -144,7 +145,13 @@ + outname = strdup(name); + slashdot(outname,'/','.'); /* change / to . */ + +- fp = fopen(tmpname, "r"); ++ if (stat(tmpname, &st)==0) { ++ if (st.st_mode & (S_IRUSR|S_IROTH|S_IRGRP)) ++ fp = fopen(tmpname, "r"); ++ else ++ /* ignore write-only files */ ++ return -1; ++ } + + if (!fp) { + switch(errno) { +@@ -158,12 +165,16 @@ + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; ++ case EINVAL: /* fopen()/stat() invalid argument */ ++ case ENOTTY: /* inappropriate ioctl for device */ ++ break; + default: + fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname); + rc = -1; + break; + } + } else { ++ errno=0; + if(fgets(inbuf, sizeof inbuf - 1, fp)) { + // this loop is required, see + // /sbin/sysctl -a | egrep -6 dev.cdrom.info +@@ -183,7 +194,9 @@ + } + } + } while(fgets(inbuf, sizeof inbuf - 1, fp)); +- } else { ++ ++ /* fgets() returns NULL and errno without change if the file is empty */ ++ } else if (errno) { + switch(errno) { + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); diff --git a/packaging/procps-3.2.5-top-rc.patch b/packaging/procps-3.2.5-top-rc.patch new file mode 100644 index 0000000..eb179db --- /dev/null +++ b/packaging/procps-3.2.5-top-rc.patch @@ -0,0 +1,14 @@ +--- procps-3.2.5/top.c.rc 2005-06-28 22:36:24.000000000 +0200 ++++ procps-3.2.5/top.c 2005-06-28 22:40:27.000000000 +0200 +@@ -2393,7 +2393,10 @@ + int i; + + for (i = 0; i < GROUPSMAX; i++) { +- win_names(&Winstk[i], Winstk[i].rc.winname); ++ /* Please, never use something like sprintf(x, "%s", x); ... see win_names() */ ++ char buf[WINNAMSIZ]; ++ strncpy(buf, Winstk[i].rc.winname, WINNAMSIZ); ++ win_names(&Winstk[i], buf); + capsmk(&Winstk[i]); + } + // rely on this next guy to force a call (eventually) to reframewins diff --git a/packaging/procps-3.2.6-libselinux.patch b/packaging/procps-3.2.6-libselinux.patch new file mode 100644 index 0000000..7b2c3b7 --- /dev/null +++ b/packaging/procps-3.2.6-libselinux.patch @@ -0,0 +1,59 @@ +--- procps-3.2.6/ps/output.c~ 2005-10-29 23:18:19.000000000 -0400 ++++ procps-3.2.6/ps/output.c 2005-12-13 16:27:15.000000000 -0500 +@@ -1090,7 +1090,7 @@ + return snprintf(outbuf, COLWID, "*"); + } + +- ++#ifdef STATICLIB + /****************** FLASK & seLinux security stuff **********************/ + // move the bulk of this to libproc sometime + +@@ -1122,7 +1122,7 @@ + return 1; + } + +-#if 0 ++#else + // This needs more study, considering: + // 1. the static linking option (maybe disable this in that case) + // 2. the -z and -Z option issue +@@ -1134,7 +1134,7 @@ + char *context; + + if(!ps_getpidcon && !tried_load){ +- void *handle = dlopen("libselinux.so.1", RTLD_NOW); ++ void *handle = dlopen("libselinux.so", RTLD_NOW); + if(handle){ + dlerror(); + ps_getpidcon = dlsym(handle, "getpidcon"); +--- procps-3.2.6/Makefile~ 2005-10-30 01:27:04.000000000 -0500 ++++ procps-3.2.6/Makefile 2005-12-13 16:32:09.000000000 -0500 +@@ -73,12 +73,12 @@ + # Something like this is probably needed to make the SE Linux + # library loading not conflict with embedded systems stuff. + # +-#ifeq ($(SHARED),1) +-#ldl := -ldl +-#LIBTYPE := -DSHAREDLIB +-#else +-#LIBTYPE := -DSTATICLIB +-#endif ++ifeq ($(SHARED),1) ++ldl := -ldl ++LIBTYPE := -DSHAREDLIB ++else ++LIBTYPE := -DSTATICLIB ++endif + + # Preprocessor flags. + PKG_CPPFLAGS := -D_GNU_SOURCE -I proc +@@ -103,7 +103,7 @@ + # Note that some stuff below is conditional on CFLAGS containing + # an option that starts with "-g". (-g, -g2, -g3, -ggdb, etc.) + CFLAGS := -O2 -s +-ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS) ++ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS) $(LIBTYPE) + + PKG_LDFLAGS := -Wl,-warn-common + LDFLAGS := diff --git a/packaging/procps-3.2.6-top-env-cpuloop.patch b/packaging/procps-3.2.6-top-env-cpuloop.patch new file mode 100644 index 0000000..cb819c2 --- /dev/null +++ b/packaging/procps-3.2.6-top-env-cpuloop.patch @@ -0,0 +1,81 @@ +--- procps-3.2.7/top.c.kzak 2006-08-31 10:44:42.000000000 +0200 ++++ procps-3.2.7/top.c 2006-08-31 10:46:15.000000000 +0200 +@@ -121,6 +121,7 @@ + Secure_mode = 0; // set if some functionality restricted + + static int VIRT_vmsize = 0; //use vm_size for VIRT ++static int CPU_loop = 0; // wait for reliable CPU data + + /* Some cap's stuff to reduce runtime calls -- + to accomodate 'Batch' mode, they begin life as empty strings */ +@@ -1679,6 +1680,8 @@ + + if (( getenv("STATSIZE")) && ( atoi(getenv("STATSIZE")) == 1 )) + VIRT_vmsize = 1; ++ if (( getenv("CPULOOP")) && ( atoi(getenv("CPULOOP")) == 1 )) ++ CPU_loop = 1; + } + + +@@ -2978,6 +2981,7 @@ + { + static proc_t **p_table = NULL; + static CPU_t *smpcpu = NULL; ++ static int first = 0; + + // whoa first time, gotta' prime the pump... + if (!p_table) { +@@ -3025,6 +3029,28 @@ + + smpcpu = cpus_refresh(smpcpu); + ++ if (first==0 && CPU_loop) ++ { ++ int i; ++ CPU_t *cpu; ++ for (i = 0; i < Cpu_tot; i++) { ++ cpu = &smpcpu[i]; ++ cpu->u_sav = cpu->u; ++ cpu->s_sav = cpu->s; ++ cpu->n_sav = cpu->n; ++ cpu->i_sav = cpu->i; ++ cpu->w_sav = cpu->w; ++ cpu->x_sav = cpu->x; ++ cpu->y_sav = cpu->y; ++ cpu->z_sav = cpu->z; ++ } ++ tv.tv_sec = Rc.delay_time; ++ tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000; ++ select(0, NULL, NULL, NULL, &tv); ++ ++ smpcpu = cpus_refresh(smpcpu); ++ } ++ + if (CHKw(Curwin, View_CPUSUM)) { + // display just the 1st /proc/stat line + summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):"); +@@ -3050,6 +3076,8 @@ + } + + SETw(Curwin, NEWFRAM_cwo); ++ ++ first = 1; + return p_table; + } + +--- procps-3.2.7/top.1.kzak 2006-08-31 10:44:42.000000000 +0200 ++++ procps-3.2.7/top.1 2006-08-31 10:44:42.000000000 +0200 +@@ -1183,6 +1183,13 @@ + Send bug reports to: + Albert D\. Cahalan, + ++The top command calculates Cpu(s) by looking at the change in CPU time values ++between samples. When you first run it, it has no previous sample to compare ++to, so these initial values are the percentages since boot. It means you need ++at least two loops or you have to ignore summary output from the first loop. ++This is problem for example for batch mode. There is a possible workaround if ++you define the CPULOOP=1 environment variable. The top command will be run one ++extra hidden loop for CPU data before standard output. + + .\" ---------------------------------------------------------------------- + .SH 8. HISTORY Former top diff --git a/packaging/procps-3.2.6-top-env-vmsize.patch b/packaging/procps-3.2.6-top-env-vmsize.patch new file mode 100644 index 0000000..ec46b99 --- /dev/null +++ b/packaging/procps-3.2.6-top-env-vmsize.patch @@ -0,0 +1,53 @@ +--- procps-3.2.6/top.1.env-vmsize 2006-04-19 16:50:37.000000000 +0200 ++++ procps-3.2.6/top.1 2006-04-19 16:59:23.000000000 +0200 +@@ -395,7 +395,8 @@ + o:\fB VIRT\fR \*(EM Virtual Image (kb) + The total amount of \*(MV used by the task. + It includes all code, data and shared libraries plus pages that have been +-swapped out. ++swapped out. (Note: you can define the STATSIZE=1 environment variable and the VIRT ++will be calculated from the /proc/#/state VmSize field.) + + VIRT = SWAP + RES. + +--- procps-3.2.6/top.c.env-vmsize 2006-04-19 16:35:20.000000000 +0200 ++++ procps-3.2.6/top.c 2006-04-19 16:50:25.000000000 +0200 +@@ -120,6 +120,8 @@ + Loops = -1, // number of iterations, -1 loops forever + Secure_mode = 0; // set if some functionality restricted + ++static int VIRT_vmsize = 0; //use vm_size for VIRT ++ + /* Some cap's stuff to reduce runtime calls -- + to accomodate 'Batch' mode, they begin life as empty strings */ + static char Cap_clr_eol [CAPBUFSIZ], +@@ -1667,6 +1669,9 @@ + Fieldstab[P_PPD].fmts = pid_fmt; + Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits; + } ++ ++ if (( getenv("STATSIZE")) && ( atoi(getenv("STATSIZE")) == 1 )) ++ VIRT_vmsize = 1; + } + + +@@ -2194,6 +2199,7 @@ + } + if (!Frames_libflags) Frames_libflags = L_DEFAULT; + if (selection_type=='p') Frames_libflags |= PROC_PID; ++ if (VIRT_vmsize) Frames_libflags |= L_status; + } + + +@@ -3157,7 +3163,10 @@ + MKCOL(p->ruser); + break; + case P_VRT: +- MKCOL(scale_num(PAGES_TO_KB(p->size), w, s)); ++ if (VIRT_vmsize) ++ MKCOL(scale_num(PAGES_TO_KB(p->vm_size), w, s)); ++ else ++ MKCOL(scale_num(PAGES_TO_KB(p->size), w, s)); + break; + case P_WCH: + if (No_ksyms) { diff --git a/packaging/procps-3.2.7-free-hlmem.patch b/packaging/procps-3.2.7-free-hlmem.patch new file mode 100644 index 0000000..c5ef9cc --- /dev/null +++ b/packaging/procps-3.2.7-free-hlmem.patch @@ -0,0 +1,23 @@ +diff -up procps-3.2.7/free.1.hlmem procps-3.2.7/free.1 +--- procps-3.2.7/free.1.hlmem 2009-02-24 10:54:35.000000000 +0100 ++++ procps-3.2.7/free.1 2009-02-24 11:00:26.000000000 +0100 +@@ -7,7 +7,7 @@ free \- Display amount of free and used + .SH SYNOPSIS + .BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s" + .I delay +-.RB "] [" "\-t" "] [" "\-V" ] ++.RB "] [" "\-t" "] [" "\-l" "] [" "\-V" "] + .SH DESCRIPTION + \fBfree\fP displays the total amount of free and used physical and swap + memory in the system, as well as the buffers used by the kernel. +@@ -28,7 +28,9 @@ may actually specify any floating point + .BR usleep (3) + is used for microsecond resolution delay times. + .PP +-The \fB\-V\fP displays version information. ++The \fB-l\fP switch shows detailed low and high memory statistics. ++.PP ++The \fB\-V\fP switch displays version information. + .SH FILES + .ta + .IR /proc/meminfo "\-\- memory information" diff --git a/packaging/procps-3.2.7-longcmd.patch b/packaging/procps-3.2.7-longcmd.patch new file mode 100644 index 0000000..7c4fdca --- /dev/null +++ b/packaging/procps-3.2.7-longcmd.patch @@ -0,0 +1,22 @@ +--- procps-3.2.7/proc/readproc.c.kzak 2006-06-16 10:18:13.000000000 +0200 ++++ procps-3.2.7/proc/readproc.c 2006-09-27 11:25:13.000000000 +0200 +@@ -432,14 +432,17 @@ + if(fd==-1) return NULL; + + /* read whole file into a memory buffer, allocating as we go */ +- while ((n = read(fd, buf, sizeof buf - 1)) > 0) { ++ while ((n = read(fd, buf, sizeof buf - 1)) >= 0) { + if (n < (int)(sizeof buf - 1)) + end_of_file = 1; +- if (n == 0 && rbuf == 0) ++ if (n == 0 && rbuf == 0) { ++ close(fd); + return NULL; /* process died between our open and read */ ++ } + if (n < 0) { + if (rbuf) + free(rbuf); ++ close(fd); + return NULL; /* read error */ + } + if (end_of_file && buf[n-1]) /* last read char not null */ diff --git a/packaging/procps-3.2.7-ps-eip64.patch b/packaging/procps-3.2.7-ps-eip64.patch new file mode 100644 index 0000000..9eb851f --- /dev/null +++ b/packaging/procps-3.2.7-ps-eip64.patch @@ -0,0 +1,48 @@ +--- procps-3.2.7/ps/ps.1.eip64 2007-06-14 11:08:35.000000000 +0200 ++++ procps-3.2.7/ps/ps.1 2007-06-14 11:03:28.000000000 +0200 +@@ -1156,6 +1156,14 @@ + or\ a\ decimal representation otherwise. + T} + ++rip RIP T{ ++64-bit instruction pointer. ++T} ++ ++rsp RSP T{ ++64-bit stack pointer. ++T} ++ + rss RSS T{ + resident set size, the non\-swapped physical memory that + a task has used (in\ kiloBytes). +--- procps-3.2.7/ps/output.c.eip64 2007-06-14 11:08:24.000000000 +0200 ++++ procps-3.2.7/ps/output.c 2007-06-14 10:58:21.000000000 +0200 +@@ -764,10 +764,18 @@ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp)); + } + ++static int pr_rsp(char *restrict const outbuf, const proc_t *restrict const pp){ ++ return snprintf(outbuf, COLWID, "%016lx", (unsigned long)(pp->kstk_esp)); ++} ++ + static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip)); + } + ++static int pr_rip(char *restrict const outbuf, const proc_t *restrict const pp){ ++ return snprintf(outbuf, COLWID, "%016lx", (unsigned long)(pp->kstk_eip)); ++} ++ + /* This function helps print old-style time formats */ + static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) { + if(!t) return snprintf(dst, COLWID, " -"); +@@ -1403,7 +1411,9 @@ + {"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT}, + {"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT}, + {"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */ ++{"rip", "RIP", pr_rip, sr_kstk_eip, 16, 0, LNX, TO|RIGHT}, + {"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, ++{"rsp", "RSP", pr_rsp, sr_kstk_eip, 16, 0, LNX, TO|RIGHT}, + {"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */ + {"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/ + {"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/ diff --git a/packaging/procps-3.2.7-ps-libselinux.patch b/packaging/procps-3.2.7-ps-libselinux.patch new file mode 100644 index 0000000..4721358 --- /dev/null +++ b/packaging/procps-3.2.7-ps-libselinux.patch @@ -0,0 +1,11 @@ +--- procps-3.2.7/ps/output.c.libselinux 2007-08-27 08:34:13.000000000 +0200 ++++ procps-3.2.7/ps/output.c 2007-08-27 08:34:30.000000000 +0200 +@@ -1141,7 +1141,7 @@ + char *context; + + if(!ps_getpidcon && !tried_load){ +- void *handle = dlopen("libselinux.so", RTLD_NOW); ++ void *handle = dlopen("libselinux.so.1", RTLD_NOW); + if(handle){ + dlerror(); + ps_getpidcon = dlsym(handle, "getpidcon"); diff --git a/packaging/procps-3.2.7-ps-man-fmt.patch b/packaging/procps-3.2.7-ps-man-fmt.patch new file mode 100644 index 0000000..f3a80dc --- /dev/null +++ b/packaging/procps-3.2.7-ps-man-fmt.patch @@ -0,0 +1,12 @@ +diff -up procps-3.2.7/ps/ps.1.ps-man-fmt procps-3.2.7/ps/ps.1 +--- procps-3.2.7/ps/ps.1.ps-man-fmt 2008-09-01 11:43:18.000000000 +0200 ++++ procps-3.2.7/ps/ps.1 2008-09-01 11:43:41.000000000 +0200 +@@ -30,7 +30,7 @@ + .\" the space needed for the 1st two columns. + .\" Making it messy: inches, ens, points, scaled points... + .\" +-.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-20u)n) ++.nr ColSize ((\n[.l] - \n[.i]) / 1n - 20) + .\" + .\" This is for command options + .nr OptSize (16u) diff --git a/packaging/procps-3.2.7-ps-stime.patch b/packaging/procps-3.2.7-ps-stime.patch new file mode 100644 index 0000000..d58b77e --- /dev/null +++ b/packaging/procps-3.2.7-ps-stime.patch @@ -0,0 +1,71 @@ +--- procps-3.2.7/ps/output.c.jitter 2007-04-26 13:15:47.000000000 +0200 ++++ procps-3.2.7/ps/output.c 2007-04-26 13:31:24.000000000 +0200 +@@ -77,7 +77,6 @@ + static int wide_signals; /* true if we have room */ + + static unsigned long seconds_since_1970; +-static unsigned long time_of_boot; + static unsigned long page_shift; + + +@@ -1952,7 +1951,6 @@ + // available space: page_size*outbuf_pages-SPACE_AMOUNT + + seconds_since_1970 = time(NULL); +- time_of_boot = seconds_since_1970 - seconds_since_boot; + + meminfo(); + +--- procps-3.2.7/ps/common.h.jitter 2005-01-27 04:43:22.000000000 +0100 ++++ procps-3.2.7/ps/common.h 2007-04-26 12:44:01.000000000 +0200 +@@ -302,6 +302,7 @@ + extern int screen_cols; + extern int screen_rows; + extern unsigned long seconds_since_boot; ++extern unsigned long time_of_boot; + extern selection_node *selection_list; + extern unsigned simple_select; + extern sort_node *sort_list; +--- procps-3.2.7/ps/global.c.jitter 2005-10-30 01:43:34.000000000 +0200 ++++ procps-3.2.7/ps/global.c 2007-04-26 13:26:38.000000000 +0200 +@@ -70,6 +70,7 @@ + int screen_cols = -1; + int screen_rows = -1; + unsigned long seconds_since_boot = -1; ++unsigned long time_of_boot = -1; + selection_node *selection_list = (selection_node *)0xdeadbeef; + unsigned simple_select = 0xffffffff; + sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */ +@@ -361,7 +362,23 @@ + look_up_our_self(&p); + set_screen_size(); + set_personality(); +- ++ int fd; ++ char *buf[BUFFSIZE]; ++ const char *b; ++ ++ /* get boot time from /proc/stat */ ++ fd = open("/proc/stat", O_RDONLY, 0); ++ if (fd != -1) { ++ buf[BUFFSIZE-1] = 0; ++ read(fd, buf, BUFFSIZE-1); ++ b = strstr(buf, "btime "); ++ if (b) { ++ sscanf(b, "btime %lu", &time_of_boot); ++ seconds_since_boot = time(0) - time_of_boot; ++ } ++ close(fd); ++ } ++ + all_processes = 0; + bsd_c_option = 0; + bsd_e_option = 0; +@@ -380,7 +397,6 @@ + negate_selection = 0; + page_size = getpagesize(); + running_only = 0; +- seconds_since_boot = uptime(0,0); + selection_list = NULL; + simple_select = 0; + sort_list = NULL; diff --git a/packaging/procps-3.2.7-psman.patch b/packaging/procps-3.2.7-psman.patch new file mode 100644 index 0000000..7ba6cff --- /dev/null +++ b/packaging/procps-3.2.7-psman.patch @@ -0,0 +1,22 @@ +--- procps-3.2.7/ps/ps.1.psman 2007-06-20 08:30:47.000000000 +0200 ++++ procps-3.2.7/ps/ps.1 2007-06-20 08:50:10.000000000 +0200 +@@ -21,8 +21,8 @@ + .\" See /usr/share/groff/current/tmac/an-old.tmac for what these do. + .\" Setting them to zero provides extra space, but only do that for + .\" plain text output. PostScript and such will remain indented. +-.if n .nr IN 0n +-.if n .nr an-prevailing-indent 0n ++.\" .if n .nr IN 0n ++.\" .if n .nr an-prevailing-indent 0n + .\" + .\" + .\" ColSize is used for the format spec table. +@@ -838,6 +838,8 @@ + .\" lB1 lB1 lBw(5.5i) + .\" lB1 l1 l. + .\" ++.RE ++ + .TS + expand; + lB1 lB1 lBw(\n[ColSize]n) diff --git a/packaging/procps-3.2.7-selinux.patch b/packaging/procps-3.2.7-selinux.patch new file mode 100644 index 0000000..9935c94 --- /dev/null +++ b/packaging/procps-3.2.7-selinux.patch @@ -0,0 +1,23 @@ +--- procps-3.2.7/ps/parser.c.selinux 2006-06-25 09:14:58.000000000 +0200 ++++ procps-3.2.7/ps/parser.c 2006-07-10 10:14:22.000000000 +0200 +@@ -231,7 +231,7 @@ + // In the meantime, please do not add to it. The list is + // intended to ONLY contain flags defined by the POSIX and UNIX + // standards published by The Open Group, IEEE, and ISO. +- if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX ++ if(!strchr("aAdefgGlnoptuUZ", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX + + switch(*flagptr){ + case 'A': +--- procps-3.2.7/ps/ps.1.selinux 2005-10-30 03:44:03.000000000 +0100 ++++ procps-3.2.7/ps/ps.1 2006-07-10 10:08:12.000000000 +0200 +@@ -450,6 +450,9 @@ + Do not show flags; show rss in place of addr. + This option can only be used with \fB\-l\fR. + ++.opt \-Z ++display security context format (SELinux, etc.) ++ + .opt \-\-format \ format + user\-defined format. Identical to \fB\-o\fR and \fBo\fR. + diff --git a/packaging/procps-3.2.7-slabtop-once.patch b/packaging/procps-3.2.7-slabtop-once.patch new file mode 100644 index 0000000..d3319cf --- /dev/null +++ b/packaging/procps-3.2.7-slabtop-once.patch @@ -0,0 +1,113 @@ +diff -up procps-3.2.7/slabtop.c.once procps-3.2.7/slabtop.c +--- procps-3.2.7/slabtop.c.once 2008-12-11 13:24:52.000000000 +0100 ++++ procps-3.2.7/slabtop.c 2008-12-11 13:33:12.000000000 +0100 +@@ -268,9 +268,24 @@ static void parse_input(char c) + } + } + ++/*printw or printf depending on the context*/ ++static void printwf(int once, const char *s,...) ++{ ++va_list va; ++ ++va_start(va,s); ++if(once) ++ vprintf(s,va); ++else ++ vwprintw(stdscr, s, va); ++va_end(va); ++} ++ ++ + int main(int argc, char *argv[]) + { + int o; ++ int once = 0; + unsigned short old_rows; + struct slab_info *slab_list = NULL; + +@@ -307,6 +322,7 @@ int main(int argc, char *argv[]) + break; + case 'o': + delay = 0; ++ once = 1; + break; + case 'V': + display_version(); +@@ -322,12 +338,18 @@ int main(int argc, char *argv[]) + if (tcgetattr(0, &saved_tty) == -1) + perror("tcgetattr"); + +- initscr(); +- term_size(0); +- old_rows = rows; +- resizeterm(rows, cols); +- signal(SIGWINCH, term_size); +- signal(SIGINT, sigint_handler); ++ if(!once) { ++ initscr(); ++ term_size(0); ++ old_rows = rows; ++ resizeterm(rows, cols); ++ signal(SIGWINCH, term_size); ++ signal(SIGINT, sigint_handler); ++ } else { ++ old_rows = rows; ++ rows = 80; ++ cols = 24; ++ } + + do { + struct slab_info *curr; +@@ -341,12 +363,12 @@ int main(int argc, char *argv[]) + break; + + if (old_rows != rows) { +- resizeterm(rows, cols); ++ if(!once) resizeterm(rows, cols); + old_rows = rows; + } + + move(0,0); +- printw( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n" ++ printwf(once, " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n" +@@ -360,15 +382,15 @@ int main(int argc, char *argv[]) + + slab_list = slabsort(slab_list); + +- attron(A_REVERSE); +- printw( "%6s %6s %4s %8s %6s %8s %10s %-23s\n", ++ if(!once) attron(A_REVERSE); ++ printwf(once, "%6s %6s %4s %8s %6s %8s %10s %-23s\n", + "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS", + "OBJ/SLAB", "CACHE SIZE", "NAME"); +- attroff(A_REVERSE); ++ if(!once) attroff(A_REVERSE); + + curr = slab_list; + for (i = 0; i < rows - 8 && curr->next; i++) { +- printw("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", ++ printwf(once, "%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", + curr->nr_objs, curr->nr_active_objs, curr->use, + curr->obj_size / 1024.0, curr->nr_slabs, + curr->objs_per_slab, (unsigned)(curr->cache_size / 1024), +@@ -376,7 +398,7 @@ int main(int argc, char *argv[]) + curr = curr->next; + } + +- refresh(); ++ if(!once) refresh(); + put_slabinfo(slab_list); + + FD_ZERO(&readfds); +@@ -392,6 +414,6 @@ int main(int argc, char *argv[]) + + tcsetattr(0, TCSAFLUSH, &saved_tty); + free_slabinfo(slab_list); +- endwin(); ++ if(!once) endwin(); + return 0; + } diff --git a/packaging/procps-3.2.7-sysctl-ignore.patch b/packaging/procps-3.2.7-sysctl-ignore.patch new file mode 100644 index 0000000..a7d6880 --- /dev/null +++ b/packaging/procps-3.2.7-sysctl-ignore.patch @@ -0,0 +1,87 @@ +--- procps-3.2.7/sysctl.c.kzak 2007-04-03 01:26:01.000000000 +0200 ++++ procps-3.2.7/sysctl.c 2007-04-03 01:26:13.000000000 +0200 +@@ -63,6 +63,44 @@ + static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n"; + static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n"; + ++/* Ignore deprecated sysctls ++ * -- we use this list when we scan (DisplayAll) /proc/sys only. We don't use it ++ * in case when user direcly uses deprecated key. It's better when user can read ++ * an error message from kernel. ++ */ ++struct sysctl_ignore { ++ const char *prefix; ++ int prefix_len; ++ const char *option; ++ int option_len; ++}; ++ ++#define IGNORE_ENTRY(prefix, option) \ ++ { prefix, sizeof(prefix)-1, option, sizeof(option)-1 } ++ ++static struct sysctl_ignore Ignore[] = ++{ ++ IGNORE_ENTRY( "net.ipv6.neigh", "base_reachable_time" ), ++ IGNORE_ENTRY( "net.ipv6.neigh", "retrans_time" ) ++}; ++ ++static bool IsIgnored(const char *name) ++{ ++ unsigned int i; ++ int sz = strlen(name); ++ ++ for (i = 0; i < sizeof(Ignore)/sizeof(struct sysctl_ignore); i++) { ++ struct sysctl_ignore *p = &Ignore[i]; ++ ++ if (sz < (p->prefix_len + p->option_len)) ++ continue; ++ ++ if (strncmp(name, p->prefix, p->prefix_len) == 0 && ++ strcmp(name + (sz - p->option_len), p->option) == 0) ++ return true; ++ } ++ return false; ++} + + static void slashdot(char *restrict p, char old, char new){ + p = strpbrk(p,"/."); +@@ -122,7 +160,7 @@ + * Read a sysctl setting + * + */ +-static int ReadSetting(const char *restrict const name) { ++static int ReadSetting(const char *restrict const name, bool useign) { + int rc = 0; + char *restrict tmpname; + char *restrict outname; +@@ -145,6 +183,12 @@ + outname = strdup(name); + slashdot(outname,'/','.'); /* change / to . */ + ++ if (useign && IsIgnored(outname)) { ++ free(outname); ++ free(tmpname); ++ return rc; ++ } ++ + if (stat(tmpname, &st)==0) { + if (st.st_mode & (S_IRUSR|S_IROTH|S_IRGRP)) + fp = fopen(tmpname, "r"); +@@ -257,7 +301,7 @@ + strcat(tmpdir, "/"); + DisplayAll(tmpdir); + } else { +- rc |= ReadSetting(tmpdir+strlen(PROC_PATH)); ++ rc |= ReadSetting(tmpdir+strlen(PROC_PATH), true); + } + } + free(tmpdir); +@@ -519,7 +563,7 @@ + if (WriteMode || index(*argv, '=')) + ReturnCode = WriteSetting(*argv); + else +- ReturnCode = ReadSetting(*argv); ++ ReturnCode = ReadSetting(*argv, false); + } + } + diff --git a/packaging/procps-3.2.7-top-clrscr.patch b/packaging/procps-3.2.7-top-clrscr.patch new file mode 100644 index 0000000..8a02568 --- /dev/null +++ b/packaging/procps-3.2.7-top-clrscr.patch @@ -0,0 +1,19 @@ +diff -up procps-3.2.7/top.c.clrscr procps-3.2.7/top.c +--- procps-3.2.7/top.c.clrscr 2008-04-08 08:49:48.000000000 +0200 ++++ procps-3.2.7/top.c 2008-04-08 08:50:07.000000000 +0200 +@@ -2109,6 +2109,7 @@ static void fields_sort (void) + *p = x + 'A'; + Curwin->rc.sortindx = x; + putp(Cap_curs_norm); ++ putp(Cap_clr_scr); + } + + +@@ -2134,6 +2135,7 @@ static void fields_toggle (void) + *p = i + 'A'; + } + putp(Cap_curs_norm); ++ putp(Cap_clr_scr); + } + + /*###### Windows/Field Groups support #################################*/ diff --git a/packaging/procps-3.2.7-top-cpu0.patch b/packaging/procps-3.2.7-top-cpu0.patch new file mode 100644 index 0000000..eb81121 --- /dev/null +++ b/packaging/procps-3.2.7-top-cpu0.patch @@ -0,0 +1,20 @@ +--- procps-3.2.7/top.c.orig 2007-09-07 21:28:41.000000000 +0100 ++++ procps-3.2.7/top.c 2007-09-07 21:29:15.000000000 +0100 +@@ -959,8 +959,15 @@ + + // and just in case we're 2.2.xx compiled without SMP support... + if (Cpu_tot == 1) { +- cpus[1].id = 0; +- memcpy(cpus, &cpus[1], sizeof(CPU_t)); ++ cpus[0].id = cpus[1].id = 0; ++ cpus[0].u = cpus[1].u; ++ cpus[0].n = cpus[1].n; ++ cpus[0].s = cpus[1].s; ++ cpus[0].i = cpus[1].i; ++ cpus[0].w = cpus[1].w; ++ cpus[0].x = cpus[1].x; ++ cpus[0].y = cpus[1].y; ++ cpus[0].z = cpus[1].z; + } + + // now value each separate cpu's tics diff --git a/packaging/procps-3.2.7-top-cpuint.patch b/packaging/procps-3.2.7-top-cpuint.patch new file mode 100644 index 0000000..9d204ed --- /dev/null +++ b/packaging/procps-3.2.7-top-cpuint.patch @@ -0,0 +1,12 @@ +diff -up procps-3.2.7/top.c.cpuint procps-3.2.7/top.c +--- procps-3.2.7/top.c.cpuint 2008-01-11 08:32:44.000000000 +0100 ++++ procps-3.2.7/top.c 2008-01-11 08:32:52.000000000 +0100 +@@ -1824,7 +1824,7 @@ static void configs_read (void) + if(Rc.mode_irixps && smp_num_cpus>1){ + // good for 100 CPUs per process + pcpu_max_value = 9999.0; +- Fieldstab[P_CPU].fmts = " %4.0f"; ++ Fieldstab[P_CPU].fmts = " %4.1f"; + } + + // lastly, establish the true runtime secure mode and delay time diff --git a/packaging/procps-3.2.7-top-manpage.patch b/packaging/procps-3.2.7-top-manpage.patch new file mode 100644 index 0000000..0f16bd9 --- /dev/null +++ b/packaging/procps-3.2.7-top-manpage.patch @@ -0,0 +1,24 @@ +diff -up procps-3.2.7/top.1.cpudesc procps-3.2.7/top.1 +--- procps-3.2.7/top.1.cpudesc 2008-01-18 08:04:19.000000000 +0100 ++++ procps-3.2.7/top.1 2008-01-18 08:15:49.000000000 +0100 +@@ -540,6 +540,20 @@ You\fI move\fR a field to the\fB left\fR + upper case\fR letter and to the\fB right\fR with the\fB lower case\fR + letter. + ++.\" ...................................................................... ++.SS 2c. SUMMARY Area Fields ++.\" ---------------------------------------------------------------------- ++The summary area fields describing CPU statistics are abbreviated. They provide ++information about times spent in: ++ \fR us = user mode ++ \fR sy = system mode ++ \fR ni = low priority user mode (nice) ++ \fR id = idle task ++ \fR wa = I/O waiting ++ \fR hi = servicing IRQs ++ \fR si = servicing soft IRQs ++ \fR st = steal (time given to other DomU instances) ++ + + .\" ---------------------------------------------------------------------- + .SH 3. INTERACTIVE Commands diff --git a/packaging/procps-3.2.7-top-remcpu.patch b/packaging/procps-3.2.7-top-remcpu.patch new file mode 100644 index 0000000..88d14ef --- /dev/null +++ b/packaging/procps-3.2.7-top-remcpu.patch @@ -0,0 +1,98 @@ +--- procps-3.2.7/top.c.remcpu 2006-07-10 10:41:11.000000000 +0200 ++++ procps-3.2.7/top.c 2006-07-10 10:41:35.000000000 +0200 +@@ -912,6 +912,7 @@ + static CPU_t *cpus_refresh (CPU_t *cpus) + { + static FILE *fp = NULL; ++ static int cpu_max; + int i; + int num; + // enough for a /proc/stat CPU line (not the intr line) +@@ -926,24 +927,29 @@ + can hold tics representing the /proc/stat cpu summary (the first + line read) -- that slot supports our View_CPUSUM toggle */ + cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t)); ++ cpu_max = Cpu_tot; + } ++ else if (cpu_max > Cpu_tot) ++ /* move saved CUPs summary to cpu_max possition */ ++ memcpy(&cpus[cpu_max], &cpus[Cpu_tot], sizeof(CPU_t)); ++ + rewind(fp); + fflush(fp); + + // first value the last slot with the cpu summary line + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); +- cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number +- cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number +- cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number ++ cpus[cpu_max].x = 0; // FIXME: can't tell by kernel version number ++ cpus[cpu_max].y = 0; // FIXME: can't tell by kernel version number ++ cpus[cpu_max].z = 0; // FIXME: can't tell by kernel version number + num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", +- &cpus[Cpu_tot].u, +- &cpus[Cpu_tot].n, +- &cpus[Cpu_tot].s, +- &cpus[Cpu_tot].i, +- &cpus[Cpu_tot].w, +- &cpus[Cpu_tot].x, +- &cpus[Cpu_tot].y, +- &cpus[Cpu_tot].z ++ &cpus[cpu_max].u, ++ &cpus[cpu_max].n, ++ &cpus[cpu_max].s, ++ &cpus[cpu_max].i, ++ &cpus[cpu_max].w, ++ &cpus[cpu_max].x, ++ &cpus[cpu_max].y, ++ &cpus[cpu_max].z + ); + if (num < 4) + std_err("failed /proc/stat read"); +@@ -955,7 +961,7 @@ + } + + // now value each separate cpu's tics +- for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) { ++ for (i = 0; ; i++) { + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); + cpus[i].x = 0; // FIXME: can't tell by kernel version number + cpus[i].y = 0; // FIXME: can't tell by kernel version number +@@ -964,9 +970,35 @@ + &cpus[i].id, + &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z + ); +- if (num < 4) +- std_err("failed /proc/stat read"); ++ if (num < 4) { ++ Cpu_tot = i; ++ break; ++ } ++ if (i == cpu_max - 1) { ++ // Bump cpu_max and extend cpus ++ cpu_max++; ++ cpus = realloc(cpus, (1 + cpu_max) * sizeof(CPU_t)); ++ if (!cpus) std_err("realloc failed"); ++ memcpy(&cpus[cpu_max], &cpus[cpu_max-1], sizeof(CPU_t)); ++ } ++ } ++ ++ if (cpu_max > Cpu_tot) ++ memcpy(&cpus[Cpu_tot], &cpus[cpu_max], sizeof(CPU_t)); ++ ++ // and just in case we're 2.2.xx compiled without SMP support... ++ if (Cpu_tot == 1) { ++ cpus[0].id = cpus[1].id = 0; ++ cpus[0].u = cpus[1].u; ++ cpus[0].n = cpus[1].n; ++ cpus[0].s = cpus[1].s; ++ cpus[0].i = cpus[1].i; ++ cpus[0].w = cpus[1].w; ++ cpus[0].x = cpus[1].x; ++ cpus[0].y = cpus[1].y; ++ cpus[0].z = cpus[1].z; + } ++ + return cpus; + } + diff --git a/packaging/procps-3.2.7-top-sorthigh.path b/packaging/procps-3.2.7-top-sorthigh.path new file mode 100644 index 0000000..e17df1e --- /dev/null +++ b/packaging/procps-3.2.7-top-sorthigh.path @@ -0,0 +1,37 @@ +--- procps-3.2.7/top.c.sorthigh 2007-02-05 09:06:34.000000000 +0100 ++++ procps-3.2.7/top.c 2007-02-05 09:09:35.000000000 +0100 +@@ -3121,6 +3121,7 @@ + unsigned w = Fieldstab[i].width; + + int advance = (x==0) && !Rc.mode_altscr; ++ f += advance; + + switch (i) { + case P_CMD: +@@ -3179,7 +3180,7 @@ + break; + case P_PRI: + if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) { +- f = " RT"; ++ f = advance ? " RT" : " RT"; + MKCOL(""); + } else + MKCOL((int)p->priority); +@@ -3227,7 +3228,7 @@ + break; + case P_WCH: + if (No_ksyms) { +- f = " %08lx "; ++ f = advance ? "%08lx " : " %08lx "; + MKCOL((long)p->wchan); + } else { + MKCOL(lookup_wchan(p->wchan, p->XXXID)); +@@ -3236,7 +3237,7 @@ + + } /* end: switch 'procflag' */ + +- rp = scat(rp, cbuf+advance); ++ rp = scat(rp, cbuf); + } /* end: for 'maxpflgs' */ + + PUFF( diff --git a/packaging/procps-3.2.7-vmstat-cpusteal.patch b/packaging/procps-3.2.7-vmstat-cpusteal.patch new file mode 100644 index 0000000..a20723f --- /dev/null +++ b/packaging/procps-3.2.7-vmstat-cpusteal.patch @@ -0,0 +1,52 @@ +--- procps-3.2.7/vmstat.c.steal 2006-07-10 10:49:49.000000000 +0200 ++++ procps-3.2.7/vmstat.c 2006-07-10 10:54:03.000000000 +0200 +@@ -150,15 +150,15 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_header(void){ +- printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n"); ++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------\n"); + printf( +- "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s\n", ++ "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n", + "r","b", + "swpd", "free", a_option?"inact":"buff", a_option?"active":"cache", + "si","so", + "bi","bo", + "in","cs", +- "us","sy","id","wa" ++ "us","sy","id","wa", "st" + ); + } + +@@ -173,7 +173,7 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_format(void) { +- const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n"; ++ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\n"; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i; + unsigned int hz = Hertz; +@@ -217,8 +217,8 @@ + (unsigned)( (100*duse + divo2) / Div ), + (unsigned)( (100*dsys + divo2) / Div ), + (unsigned)( (100*didl + divo2) / Div ), +- (unsigned)( (100*diow + divo2) / Div ) /* , +- (unsigned)( (100*dstl + divo2) / Div ) */ ++ (unsigned)( (100*diow + divo2) / Div ), ++ (unsigned)( (100*dstl + divo2) / Div ) + ); + + for(i=1;iname,((const vm_table_struct*)b)->name); + } +@@ -626,10 +629,10 @@ + unsigned long vm_nr_reverse_maps; // includes PageDirect + unsigned long vm_nr_mapped; // mapped into pagetables + unsigned long vm_nr_slab; // in slab +-unsigned long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line) +-unsigned long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line) +-unsigned long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line) +-unsigned long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line) ++unsigned long long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line) ++unsigned long long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line) ++unsigned long long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line) ++unsigned long long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line) + unsigned long vm_pgalloc; // page allocations + unsigned long vm_pgfree; // page freeings + unsigned long vm_pgactivate; // pages moved inactive -> active +@@ -668,54 +671,54 @@ + + void vminfo(void){ + char namebuf[16]; /* big enough to hold any row name */ +- vm_table_struct findme = { namebuf, NULL}; ++ vm_table_struct findme = { namebuf, NULL, 0}; + vm_table_struct *found; + char *head; + char *tail; + static const vm_table_struct vm_table[] = { +- {"allocstall", &vm_allocstall}, +- {"kswapd_inodesteal", &vm_kswapd_inodesteal}, +- {"kswapd_steal", &vm_kswapd_steal}, +- {"nr_dirty", &vm_nr_dirty}, // page version of meminfo Dirty +- {"nr_mapped", &vm_nr_mapped}, // page version of meminfo Mapped +- {"nr_page_table_pages", &vm_nr_page_table_pages},// same as meminfo PageTables +- {"nr_pagecache", &vm_nr_pagecache}, // gone in 2.5.66+ kernels +- {"nr_reverse_maps", &vm_nr_reverse_maps}, // page version of meminfo ReverseMaps GONE +- {"nr_slab", &vm_nr_slab}, // page version of meminfo Slab +- {"nr_unstable", &vm_nr_unstable}, +- {"nr_writeback", &vm_nr_writeback}, // page version of meminfo Writeback +- {"pageoutrun", &vm_pageoutrun}, +- {"pgactivate", &vm_pgactivate}, +- {"pgalloc", &vm_pgalloc}, // GONE (now separate dma,high,normal) +- {"pgalloc_dma", &vm_pgalloc_dma}, +- {"pgalloc_high", &vm_pgalloc_high}, +- {"pgalloc_normal", &vm_pgalloc_normal}, +- {"pgdeactivate", &vm_pgdeactivate}, +- {"pgfault", &vm_pgfault}, +- {"pgfree", &vm_pgfree}, +- {"pginodesteal", &vm_pginodesteal}, +- {"pgmajfault", &vm_pgmajfault}, +- {"pgpgin", &vm_pgpgin}, // important +- {"pgpgout", &vm_pgpgout}, // important +- {"pgrefill", &vm_pgrefill}, // GONE (now separate dma,high,normal) +- {"pgrefill_dma", &vm_pgrefill_dma}, +- {"pgrefill_high", &vm_pgrefill_high}, +- {"pgrefill_normal", &vm_pgrefill_normal}, +- {"pgrotated", &vm_pgrotated}, +- {"pgscan", &vm_pgscan}, // GONE (now separate direct,kswapd and dma,high,normal) +- {"pgscan_direct_dma", &vm_pgscan_direct_dma}, +- {"pgscan_direct_high", &vm_pgscan_direct_high}, +- {"pgscan_direct_normal",&vm_pgscan_direct_normal}, +- {"pgscan_kswapd_dma", &vm_pgscan_kswapd_dma}, +- {"pgscan_kswapd_high", &vm_pgscan_kswapd_high}, +- {"pgscan_kswapd_normal",&vm_pgscan_kswapd_normal}, +- {"pgsteal", &vm_pgsteal}, // GONE (now separate dma,high,normal) +- {"pgsteal_dma", &vm_pgsteal_dma}, +- {"pgsteal_high", &vm_pgsteal_high}, +- {"pgsteal_normal", &vm_pgsteal_normal}, +- {"pswpin", &vm_pswpin}, // important +- {"pswpout", &vm_pswpout}, // important +- {"slabs_scanned", &vm_slabs_scanned}, ++ VM_TABENTRY(allocstall), ++ VM_TABENTRY(kswapd_inodesteal), ++ VM_TABENTRY(kswapd_steal), ++ VM_TABENTRY(nr_dirty), ++ VM_TABENTRY(nr_mapped), ++ VM_TABENTRY(nr_page_table_pages), ++ VM_TABENTRY(nr_pagecache), ++ VM_TABENTRY(nr_reverse_maps), ++ VM_TABENTRY(nr_slab), ++ VM_TABENTRY(nr_unstable), ++ VM_TABENTRY(nr_writeback), ++ VM_TABENTRY(pageoutrun), ++ VM_TABENTRY(pgactivate), ++ VM_TABENTRY(pgalloc), ++ VM_TABENTRY(pgalloc_dma), ++ VM_TABENTRY(pgalloc_high), ++ VM_TABENTRY(pgalloc_normal), ++ VM_TABENTRY(pgdeactivate), ++ VM_TABENTRY(pgfault), ++ VM_TABENTRY(pgfree), ++ VM_TABENTRY(pginodesteal), ++ VM_TABENTRY(pgmajfault), ++ VM_TABENTRY(pgpgin), ++ VM_TABENTRY(pgpgout), ++ VM_TABENTRY(pgrefill), ++ VM_TABENTRY(pgrefill_dma), ++ VM_TABENTRY(pgrefill_high), ++ VM_TABENTRY(pgrefill_normal), ++ VM_TABENTRY(pgrotated), ++ VM_TABENTRY(pgscan), ++ VM_TABENTRY(pgscan_direct_dma), ++ VM_TABENTRY(pgscan_direct_high), ++ VM_TABENTRY(pgscan_direct_normal), ++ VM_TABENTRY(pgscan_kswapd_dma), ++ VM_TABENTRY(pgscan_kswapd_high), ++ VM_TABENTRY(pgscan_kswapd_normal), ++ VM_TABENTRY(pgsteal), ++ VM_TABENTRY(pgsteal_dma), ++ VM_TABENTRY(pgsteal_high), ++ VM_TABENTRY(pgsteal_normal), ++ VM_TABENTRY(pswpin), ++ VM_TABENTRY(pswpout), ++ VM_TABENTRY(slabs_scanned) + }; + const int vm_table_count = sizeof(vm_table)/sizeof(vm_table_struct); + +@@ -741,7 +744,10 @@ + ); + head = tail+1; + if(!found) goto nextline; +- *(found->slot) = strtoul(head,&tail,10); ++ if (found->sz == sizeof(unsigned long long)) ++ *((unsigned long long *) found->slot) = strtoull(head,&tail,10); ++ else ++ *((unsigned long *) found->slot) = strtoul(head,&tail,10); + nextline: + + //if(found) fprintf(stderr,"%s=%d\n",found->name,*(found->slot)); +--- procps-3.2.7/vmstat.c.kzak 2006-08-04 10:52:38.000000000 +0200 ++++ procps-3.2.7/vmstat.c 2006-08-04 12:05:49.000000000 +0200 +@@ -180,7 +180,7 @@ + unsigned int running,blocked,dummy_1,dummy_2; + jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2], cpu_xxx[2], cpu_yyy[2], cpu_zzz[2]; + jiff duse, dsys, didl, diow, dstl, Div, divo2; +- unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; ++ unsigned long long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; + unsigned int intr[2], ctxt[2]; + unsigned int sleep_half; + unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul; +@@ -509,7 +509,7 @@ + static void sum_format(void) { + unsigned int running, blocked, btime, processes; + jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz; +- unsigned long pgpgin, pgpgout, pswpin, pswpout; ++ unsigned long long pgpgin, pgpgout, pswpin, pswpout; + unsigned int intr, ctxt; + + meminfo(); +@@ -539,10 +539,10 @@ + printf("%13Lu IRQ cpu ticks\n", cpu_xxx); + printf("%13Lu softirq cpu ticks\n", cpu_yyy); + printf("%13Lu stolen cpu ticks\n", cpu_zzz); +- printf("%13lu pages paged in\n", pgpgin); +- printf("%13lu pages paged out\n", pgpgout); +- printf("%13lu pages swapped in\n", pswpin); +- printf("%13lu pages swapped out\n", pswpout); ++ printf("%13llu pages paged in\n", pgpgin); ++ printf("%13llu pages paged out\n", pgpgout); ++ printf("%13llu pages swapped in\n", pswpin); ++ printf("%13llu pages swapped out\n", pswpout); + printf("%13u interrupts\n", intr); + printf("%13u CPU context switches\n", ctxt); + printf("%13u boot time\n", btime); +@@ -554,7 +554,7 @@ + static void fork_format(void) { + unsigned int running, blocked, btime, processes; + jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz; +- unsigned long pgpgin, pgpgout, pswpin, pswpout; ++ unsigned long long pgpgin, pgpgout, pswpin, pswpout; + unsigned int intr, ctxt; + + getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl, diff --git a/packaging/procps-3.2.7-vmstat-timestamp-manpage.patch b/packaging/procps-3.2.7-vmstat-timestamp-manpage.patch new file mode 100644 index 0000000..37a1f48 --- /dev/null +++ b/packaging/procps-3.2.7-vmstat-timestamp-manpage.patch @@ -0,0 +1,22 @@ +diff -up procps-3.2.7/vmstat.8.timestamp procps-3.2.7/vmstat.8 +--- procps-3.2.7/vmstat.8.timestamp 2009-02-03 12:32:27.000000000 +0100 ++++ procps-3.2.7/vmstat.8 2009-02-03 12:34:37.000000000 +0100 +@@ -8,6 +8,7 @@ vmstat \- Report virtual memory statisti + .B vmstat + .RB [ "\-a" ] + .RB [ "\-n" ] ++.RB [ "\-t" ] + .RI [ delay " [ " count ]] + .br + .B vmstat +@@ -43,7 +44,9 @@ equivalent to the total number of tasks + is represented by one or more tasks, depending on thread usage. + This display does not repeat. + .PP +-The \fB-m\fP displays slabinfo. ++The \fB-t\fP switch adds timestamp to the output. ++.PP ++The \fB-m\fP switch displays slabinfo. + .PP + The \fB-n\fP switch causes the header to be displayed only once rather than periodically. + .PP diff --git a/packaging/procps-3.2.7-vmstat-timestamp.patch b/packaging/procps-3.2.7-vmstat-timestamp.patch new file mode 100644 index 0000000..d7ca068 --- /dev/null +++ b/packaging/procps-3.2.7-vmstat-timestamp.patch @@ -0,0 +1,117 @@ +--- procps-3.2.7/vmstat.c 2008-12-11 22:11:30.042532106 -0500 ++++ procps-3.2.7/vmstat.c.timestamp 2008-12-11 22:08:11.553529883 -0500 +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #include "proc/sysinfo.h" + #include "proc/version.h" +@@ -56,6 +57,7 @@ + + static unsigned int height; // window height + static unsigned int moreheaders=TRUE; ++static unsigned int showtimestamp=FALSE; + + + ///////////////////////////////////////////////////////////////////////// +@@ -71,6 +73,7 @@ + fprintf(stderr," -p prints disk partition statistics\n"); + fprintf(stderr," -s prints vm table\n"); + fprintf(stderr," -m prints slabinfo\n"); ++ fprintf(stderr," -t add timestamp to output\n"); + fprintf(stderr," -S unit size\n"); + fprintf(stderr," delay is the delay between updates in seconds. \n"); + fprintf(stderr," unit size k:1000 K:1024 m:1000000 M:1048576 (default is K)\n"); +@@ -150,7 +153,11 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_header(void){ +- printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n"); ++ if(!showtimestamp){ ++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n"); ++ } else { ++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------ ---timestamp---\n"); ++ } + printf( + "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n", + "r","b", +@@ -173,7 +180,7 @@ + //////////////////////////////////////////////////////////////////////////// + + static void new_format(void) { +- const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\n"; ++ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\t%s\n"; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i; + unsigned int hz = Hertz; +@@ -185,6 +192,10 @@ + unsigned int sleep_half; + unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul; + int debt = 0; // handle idle ticks running backwards ++ struct tm *tm_ptr; ++ time_t the_time; ++ char timebuf[32]; ++ timebuf[0] = '\0'; + + sleep_half=(sleep_time/2); + new_header(); +@@ -196,6 +207,13 @@ + &running,&blocked, + &dummy_1, &dummy_2); + ++ if (showtimestamp) ++ { ++ (void) time( &the_time ); ++ tm_ptr = localtime( &the_time ); ++ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S %Z", tm_ptr); ++ } ++ + duse= *cpu_use + *cpu_nic; + dsys= *cpu_sys + *cpu_xxx + *cpu_yyy; + didl= *cpu_idl; +@@ -218,7 +236,8 @@ + (unsigned)( (100*dsys + divo2) / Div ), + (unsigned)( (100*didl + divo2) / Div ), + (unsigned)( (100*diow + divo2) / Div ), +- (unsigned)( (100*dstl + divo2) / Div ) ++ (unsigned)( (100*dstl + divo2) / Div ), ++ timebuf + ); + + for(i=1;itgid == u->ut_pid)) { + *found_utpid = 1; +- best = tmp; ++ if (!best) ++ best = tmp; + } + if(tmp->tty != line) continue; + (*jcpu) += tmp->utime + tmp->stime; +- secondbest = tmp; + /* same time-logic here as for "best" below */ + if(! (secondbest && tmp->start_time <= secondbest->start_time) ){ + secondbest = tmp; diff --git a/packaging/procps-3.2.7-watch-unicode.patch b/packaging/procps-3.2.7-watch-unicode.patch new file mode 100644 index 0000000..1196b9c --- /dev/null +++ b/packaging/procps-3.2.7-watch-unicode.patch @@ -0,0 +1,148 @@ +diff -u procps-3.2.7/Makefile procps/Makefile +--- procps-3.2.7/Makefile 2007-01-16 17:24:49.000000000 +0100 ++++ procps/Makefile 2007-01-16 17:29:27.000000000 +0100 +@@ -67,7 +67,7 @@ + # plus the top-level Makefile to make it work stand-alone. + _TARFILES := Makefile + +-CURSES := -lncurses ++CURSES := -lncursesw + + # This seems about right for the dynamic library stuff. + # Something like this is probably needed to make the SE Linux +diff -u procps-3.2.7/watch.c procps/watch.c +--- procps-3.2.7/watch.c 2007-01-16 17:24:49.000000000 +0100 ++++ procps/watch.c 2007-01-16 18:06:57.000000000 +0100 +@@ -15,7 +15,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -28,6 +28,8 @@ + #include + #include + #include ++#include ++#include + + #ifdef FORCE_8BIT + #undef isprint +@@ -137,6 +139,27 @@ + } + } + ++static wint_t ++readwc(FILE *stream, mbstate_t *mbs) ++{ ++ for (;;) { ++ int chr; ++ char c; ++ wchar_t wc; ++ size_t len; ++ ++ chr = getc(stream); ++ if (chr == EOF) ++ return WEOF; ++ c = chr; ++ len = mbrtowc(&wc, &c, 1, mbs); ++ if (len == (size_t)-1) ++ memset(mbs, 0, sizeof(*mbs)); ++ else if (len != (size_t)-2) ++ return wc; ++ } ++} ++ + int + main(int argc, char *argv[]) + { +@@ -243,6 +266,7 @@ + FILE *p; + int x, y; + int oldeolseen = 1; ++ mbstate_t mbs; + + if (screen_size_changed) { + get_terminal_size(); +@@ -276,49 +300,63 @@ + do_exit(2); + } + ++ memset(&mbs, 0, sizeof(mbs)); + for (y = show_title; y < height; y++) { + int eolseen = 0, tabpending = 0; + for (x = 0; x < width; x++) { +- int c = ' '; +- int attr = 0; ++ wint_t c = L' '; ++ int attr = 0, c_width; ++ cchar_t cc; ++ wchar_t wstr[2]; + + if (!eolseen) { + /* if there is a tab pending, just spit spaces until the + next stop instead of reading characters */ + if (!tabpending) + do +- c = getc(p); +- while (c != EOF && !isprint(c) +- && c != '\n' +- && c != '\t'); +- if (c == '\n') ++ c = readwc(p, &mbs); ++ while (c != WEOF && !iswprint(c) ++ && c != L'\n' ++ && c != L'\t'); ++ if (c == L'\n') + if (!oldeolseen && x == 0) { + x = -1; + continue; + } else + eolseen = 1; +- else if (c == '\t') ++ else if (c == L'\t') + tabpending = 1; +- if (c == EOF || c == '\n' || c == '\t') +- c = ' '; ++ if (c == WEOF || c == L'\n' || c == L'\t') ++ c = L' '; + if (tabpending && (((x + 1) % 8) == 0)) + tabpending = 0; + } ++ wstr[0] = c; ++ wstr[1] = 0; ++ setcchar (&cc, wstr, 0, 0, NULL); + move(y, x); + if (option_differences) { +- chtype oldch = inch(); +- char oldc = oldch & A_CHARTEXT; ++ cchar_t oldc; ++ wchar_t oldwstr[2]; ++ attr_t attrs; ++ short colors; ++ ++ in_wch(&oldc); ++ getcchar(&oldc, oldwstr, &attrs, &colors, NULL); + attr = !first_screen +- && ((char)c != oldc ++ && (wstr[0] != oldwstr[0] + || + (option_differences_cumulative +- && (oldch & A_ATTRIBUTES))); ++ && attrs)); + } + if (attr) + standout(); +- addch(c); ++ add_wch(&cc); + if (attr) + standend(); ++ c_width = wcwidth(c); ++ if (c_width > 1) ++ x += c_width - 1; + } + oldeolseen = eolseen; + } diff --git a/packaging/procps-enable-core.patch b/packaging/procps-enable-core.patch new file mode 100644 index 0000000..b7310c1 --- /dev/null +++ b/packaging/procps-enable-core.patch @@ -0,0 +1,11 @@ +--- procps-3.2.7/ps/display.c.enable-core 2009-02-27 11:57:21.000000000 +0000 ++++ procps-3.2.7/ps/display.c 2009-02-27 11:57:48.000000000 +0000 +@@ -562,6 +562,8 @@ + default: + sigaction(i,&sa,NULL); + case 0: ++ case SIGABRT: ++ case SIGSEGV: + case SIGINT: /* ^C */ + case SIGTSTP: /* ^Z */ + case SIGTTOU: /* see stty(1) man page */ diff --git a/packaging/procps.changes b/packaging/procps.changes new file mode 100644 index 0000000..fcd27ed --- /dev/null +++ b/packaging/procps.changes @@ -0,0 +1,474 @@ +* Wed Jul 28 2010 Ameya Palande 3.2.8 +- Fix for "Unknown HZ value! (??) Assume 100." problem + +* Mon Dec 14 2009 Yi Yang 3.2.7 +- Fix Makefile error when LD_AS_NEEDED is set + +* Mon Dec 14 2009 Yi Yang 3.2.7 +- Remove "unset LD_AS_NEEDED" + +* Thu May 7 2009 Arjan van de Ven 3.2.7 +- not ready for LD_AS_NEEDED yet + +* Wed Jul 23 2008 Zhang Xin +- Mark man file as %doc + +* Tue Feb 12 2008 Tomas Smetana 3.2.7-20 +- rebuild (gcc-4.3) + +* Thu Jan 24 2008 Tomas Smetana 3.2.7-19.2 +- install slabtop again: kernel was fixed + +* Fri Jan 18 2008 Tomas Smetana 3.2.7-19.1 +- rebuild because of errors on x86_64 + +* Fri Jan 18 2008 Tomas Smetana 3.2.7-19 +- fix #296471 - update top man page +- fix #226319 - merge review + +* Fri Jan 11 2008 Tomas Smetana 3.2.7-18 +- fix displaying the CPU column as integer (related #354001) +- don't install slabtop -- there's no /proc/slabinfo + +* Wed Sep 12 2007 Tomas Smetana 3.2.7-17 +- fix #185994 - top "Cpu0" line never updates when using + "Single Cpu = Off" option on single processor machine + +* Mon Aug 27 2007 Tomas Smetana 3.2.7-16.1 +- bump release + +* Mon Aug 27 2007 Tomas Smetana 3.2.7-16 +- fix #255441 - ps requires libselinux-devel to display security contexts + +* Thu Aug 23 2007 Tomas Smetana 3.2.7-15.1 +- rebuild + +* Mon Aug 20 2007 Tomas Smetana 3.2.7-15 +- fix #244960 - ps manpage formatted incorrectly +- update license tag + +* Mon Jun 18 2007 Tomas Smetana 3.2.7-14 +- fix #244152 ps truncates eip and esp to 32-bit values on 64-bit systems + +* Tue May 22 2007 Tomas Smetana 3.2.7-13 +- fix #208217 - ps does not accept '+' in sort specifier + +* Wed Apr 25 2007 Tomas Smetana 3.2.7-12 +- fix #183029 - watch ignores multibyte characters +- fix #222251 - STIME column can jitter +- fix array overflow in sysctl + +* Tue Apr 3 2007 Karel Zak 3.2.7-11 +- fix #234546 - 'w' doesn't give correct information about what's being run. +- fix #228870 - process `sysctl' is using deprecated sysctl +- cleanup spec file + +* Mon Feb 5 2007 Karel Zak 3.2.7-10 +- fix #212637 - sysctl using deprecated syscall +- fix #140975 - top corrupts screen when sorting on first column + +* Tue Jan 30 2007 Karel Zak 3.2.7-9 +- fix procps_version in FAQ patch (thanks to Ian Kent) + +* Wed Sep 27 2006 Karel Zak 3.2.7-8 +- remove zombie patch (needs more investigation) +- fix #208100 - top command with '-c' option become not to display list of tasks +- fix #199174 - top returns with exit code 1 even if no error occurs + +* Tue Sep 19 2006 Karel Zak 3.2.7-7 +- fix #206551 - top fails to convert to cpu single mode when hit '1' + +* Tue Sep 5 2006 Karel Zak 3.2.7-6 +- fix minor bug in procps-3.2.6-top-env-cpuloop.patch + +* Fri Aug 7 2006 Karel Zak - 3.2.7-5 +- fix #189349 - 32bit vmstat on 64bit kernel + +* Thu Aug 3 2006 Karel Zak - 3.2.7-4 +- fix #139827 - ps(1) outputs a multi-threads process as a defunct process. + +* Wed Jul 19 2006 Karel Zak - 3.2.7-3 +- spec file cleanup + +* Wed Jul 12 2006 Jesse Keating - 3.2.7-2.1 +- rebuild + +* Mon Jul 10 2006 Karel Zak 3.2.7-2 +- fix #134516 - ps ignores /proc/#/cmdline if contents 2047 bytes + +* Mon Jul 10 2006 Karel Zak 3.2.7-1 +- upgrade to 3.2.7 (and sync patches) + +* Thu Jun 1 2006 Karel Zak 3.2.6-4 +- fix #191493 - watch -n doesn't handle large integers properly +- fix #186017 - top "Cpu0" line never updates on single processor machine + (bugfix added to the 'remcpu' patch) +- fix #168444 - memory usage conflicts with /proc/meminfo +- fix #174619 - top reports wrong values for CPU(s) in batch mode +- fix #185299 - cpu steal time support + +* Fri Feb 10 2006 Jesse Keating - 3.2.6-3.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 3.2.6-3.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Tue Dec 13 2005 Dan Walsh 3.2.6-3 +- Translate context + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Tue Nov 8 2005 Karel Zak 3.2.6-2 +- fix #157725 - sysctl -A returns an error + +* Mon Oct 31 2005 Karel Zak 3.2.6-1 +- update to new upstream release + +* Wed Oct 12 2005 Karel Zak 3.2.5-8 +- fix #170083 - Top showing bad cpu usage numbers + +* Tue Sep 6 2005 Karel Zak 3.2.5-7 +- improve procps-3.2.5-sysctl-writeonly.patch +- fix #161449 - "top" ignores user and system toprc +- fix #161559 - top segfaults when resizing console +- fix #160796 - vmstat crashes when accessing LVM partition +- fix #161303 - 'top' failed when remove cpus + +* Tue May 10 2005 Karel Zak 3.2.5-6 +- fix permissions in the spec install section + +* Tue May 10 2005 Karel Zak 3.2.5-5 +- fix debuginfo + +* Tue Apr 26 2005 Karel Zak 3.2.5-4 +- fix #144459 - sysctl reports error: unknown error <...> reading key '' + (now sysctl doesn't read data from write-only /proc/sys files) + +* Thu Mar 17 2005 Karel Zak 3.2.5-3 +- fix top crashes when terminal window is resized (#149319) + +* Sat Mar 5 2005 Karel Zak 3.2.5-2 +- rebuilt + +* Tue Feb 1 2005 Karel Zak 3.2.5-1 +- update to new upstream 3.2.5 +- recreate selinux patch +- remove errno, slabinfo21 and fullpath patches -- all fixed by upstream + +* Mon Jan 25 2005 Karel Zak 3.2.4-4 +- pmap truncates filenames of mappings (#142751) + +* Mon Jan 24 2005 Karel Zak 3.2.4-3 +- add support for /proc/slabinfo 2.1 (#145369) + +* Fri Jan 7 2005 Karel Zak 3.2.4-2 +- fix sysctl errno usage (#144459) + +* Wed Dec 1 2004 Karel Zak 3.2.4-1 +- update to new upstream release + +* Mon Nov 1 2004 Karel Zak 3.2.3-6 +- update FAQ +- update spec description +- fix text in .noproc patch +- fix segv fault if cpu number exceeding 38 (#137159) + +* Tue Sep 28 2004 Dan Walsh 3.2.3-5 +- Fix terminal handling when /proc is not available. +- Patch provided by Karel Zak + +* Thu Sep 16 2004 Dan Walsh 3.2.3-4 +- Fix spec file to use makefile + +* Mon Aug 30 2004 Dan Walsh 3.2.3-3 +- Fix batch mode to use dumb terminal + +* Tue Aug 17 2004 Florian La Roche +- fix building as non-root, patch from Steve G + +* Tue Aug 10 2004 Dan Walsh 3.2.3-1 +- Latest from Upstream + +* Tue Jul 20 2004 Dan Walsh 3.2.2-2 +- Reformat ps man page + +* Mon Jul 19 2004 Dan Walsh 3.2.2-1 +- Update to upstream version. + +* Sat Jun 26 2004 Dan Walsh 3.2.1-7 +- Add patch to display vm_size when STATSIZE env set + +* Tue Jun 15 2004 Alan Cox 3.2.1-6 +- Removed broken SELinux patch to w +- Added a better alternative whereby we get less data but don't fall for + vanished processes when SELinux is running + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Mon Jun 14 2004 Dan Walsh 3.2.1-5 +- Fix FAQ Line + +* Mon Apr 09 2004 Colin Walters 3.2.1-4 +- Add little patch to make w/who work when getattr access + to /proc/ for the user's login process is denied + +* Sun Mar 28 2004 Dan Walsh 3.2.1-3 +- bump for rhel3 + +* Sun Mar 28 2004 Dan Walsh 3.2.1-2 +- Removed addtask patch, very buggy, +- Added FAQ to docdir + +* Sun Mar 28 2004 Dan Walsh 3.2.1-1 +- Update to latest from upstream + +* Thu Mar 25 2004 Dan Walsh 3.2.0-3 +- Add addtask patch to total all threads times. + +* Wed Mar 17 2004 Dan Walsh +- Clean up spec file. + +* Tue Mar 02 2004 Elliot Lee +- rebuilt + +* Tue Feb 24 2004 Dan Walsh 3.2.0-1 +- New version from upstream + +* Fri Feb 13 2004 Elliot Lee +- rebuilt + +* Thu Jan 22 2004 Dan Walsh 3.1.15-3 +- Match -Z to --context + +* Wed Jan 21 2004 Dan Walsh 3.1.15-2 +- Add back in -Z support + +* Wed Jan 21 2004 Alexander Larsson 3.1.15-1 +- upgrade to procps3 +- Some regressions, see bug #114012 + +* Tue Jan 20 2004 Dan Walsh 2.0.17-7 +- Remove LIBCURSES from skill and sysctl + +* Wed Dec 10 2003 Dan Walsh 2.0.17-6 +- Turn on SELinux + +* Mon Dec 8 2003 Alexander Larsson 2.0.17-5 +- Fix top total percentages (#109484) + +* Wed Oct 15 2003 Dan Walsh 2.0.17-4 +- Turn off selinux + +* Wed Oct 15 2003 Dan Walsh 2.0.17-3.sel +- Fix help message + +* Thu Oct 9 2003 Dan Walsh 2.0.17-2.sel +- Turn on selinux + +* Fri Oct 3 2003 Alexander Larsson 2.0.17-1 +- Update to 2.0.17, drop upstream patches, forward port remaining patches + +* Fri Sep 5 2003 Dan Walsh 2.0.13-11 +- Turn off selinux + +* Thu Aug 28 2003 Dan Walsh 2.0.13-10.sel +- Add -Z switch for SELinux + +* Sun Aug 17 2003 Doug Ledford 2.0.13-9E +- Add patch to recognize irq and softirq time accounting in kernels that + support this feature + +* Mon Aug 11 2003 Alexander Larsson 2.0.13-8 +- rebuild + +* Mon Aug 11 2003 Alexander Larsson 2.0.13-7E +- Add swapped patch from rik van riel + +* Wed Aug 6 2003 Alexander Larsson 2.0.13-6 +- rebuild + +* Wed Aug 6 2003 Alexander Larsson 2.0.13-5E +- Update iowait patch (#101657) +- Add wchan 64bit patch from Mark DeWandel + +* Mon Jul 28 2003 Dan Walsh 2.0.13-4E +- Add SELinux patch + +* Wed Jul 16 2003 Matt Wilson 2.0.13-3E +- display iowait with procps-2.0.13-iowait.patch (#99061) + +* Fri Jul 11 2003 Alexander Larsson 2.0.13-2E +- Disable linuxthreads thread hack + +* Mon Jul 7 2003 Alexander Larsson 2.0.13-1E +- rebuild + +* Fri Jul 4 2003 Alexander Larsson 2.0.13-1 +- update to 2.0.13 +- Re-merged ntpl patch +- Add hertz fix from Ernie Petrides + +* Wed Jun 04 2003 Elliot Lee +- rebuilt + +* Fri May 23 2003 Alexander Larsson 2.0.12-1 +- Update to 2.0.12 +- Add patch to fix segfault on ps axl (#91453) + +* Fri Mar 14 2003 Alexander Larsson 2.0.11-7 +- Add patch that fixes negative priorities in top. + +* Thu Feb 20 2003 Alexander Larsson 2.0.11-6 +- New NPTL patch: +- Added skipthreads optimization to top +- Don't read threads in 'w' + +* Thu Feb 20 2003 Alexander Larsson 2.0.11-5 +- Update the NPTL patch since the kernel /proc was fixed +- For kernels >= 2.4.20-2.50 + +* Mon Feb 17 2003 Alexander Larsson 2.0.11-4 +- Update nptl patch to new /proc layout. + +* Wed Jan 22 2003 Tim Powers 2.0.11-3 +- rebuilt + +* Wed Jan 22 2003 Alexander Larsson 2.0.11-2 +- Created nptl patch after discussion with ingo and arjan + +* Tue Jan 21 2003 Alexander Larsson 2.0.11-1 +- Update to 2.0.11 + +* Mon Dec 16 2002 Elliot Lee 2.0.10-4 +- Fix %%install in changelog + +* Tue Nov 19 2002 Jakub Jelinek 2.0.10-3 +- Fix for Hammer + +* Wed Oct 23 2002 Alexander Larsson 2.0.10-2 +- Remove uninstalled files in %%install. Add pmap to %%files + +* Tue Oct 8 2002 Alexander Larsson 2.0.10-1 +- Update to 2.0.10 +- Removed applied patches. + +* Mon Aug 12 2002 Alexander Larsson 2.0.7-25 +- Add patch to protect against idle ticks going backwards. Fixes #71237 + +* Thu Aug 8 2002 Alexander Larsson 2.0.7-24 +- Fix saving of sort, fixes #32757 +- Fix printing size, fixes #48224 +- Fix float decimal point input #58163 + +* Thu Aug 8 2002 Alexander Larsson 2.0.7-23 +- Fix unsigned/signed bug. Closes #60998. +- Update threadbadhack to correctly propagate process time to the main thread. + +* Wed Aug 7 2002 Alexander Larsson 2.0.7-22 +- Don't strip binaries + +* Fri Jul 12 2002 Alexander Larsson 2.0.7-21 +- Remove the X11 subpackage + +* Mon Jul 1 2002 Alexander Larsson 2.0.7-19 +- Added patch that fixes #35174 + +* Wed Jun 26 2002 Alexander Larsson 2.0.7-18 +- New thread badhack patch. Fixes a segfault. + +* Mon Jun 24 2002 Alexander Larsson 2.0.7-16 +- New thread badhack. Now enabled by default. + +* Fri Jun 21 2002 Tim Powers +- automated rebuild + +* Thu Jun 20 2002 Alexander Larsson 2.0.7-14 +- Added badhack to support hiding threads + +* Thu May 23 2002 Tim Powers +- automated rebuild + +* Mon Apr 15 2002 Bill Nottingham 2.0.7-12 +- add ldconfig in postun section + +* Mon Aug 27 2001 Trond Eivind Glomsrod 2.0.7-11 +- Add ncurses-devel as a build dependency (#49562) + +* Sat Jul 21 2001 Tim Powers +- removed applnk entry, one of the things that's cluttering our menus + +* Sun Jun 24 2001 Elliot Lee +- Bump release + rebuild. + +* Thu Apr 5 2001 Jakub Jelinek +- fix AIX style user defined formats (#34833) + +* Thu Mar 22 2001 Bill Nottingham +- add a '-e' to sysctl to ignore errors (#31852) + +* Mon Mar 5 2001 Preston Brown +- bigger buffer for reading /proc/stat fixes segfault (#27840) + +* Thu Feb 1 2001 Preston Brown +- make sysctl return a value when errors occur (#18820). +- support big UIDs (#22683) + +* Mon Jan 22 2001 Helge Deller +- work-around for negative CPU output (Bug #18380) + +* Thu Aug 17 2000 Than Ngo +- fix failing in RPM post script (Bug #16226) + +* Wed Jul 26 2000 Michael K. Johnson +- Added Jakub's locale patch + +* Fri Jul 14 2000 Michael K. Johnson +- procps-2.0.7 +- integrated all patches except for signames patch, which is broken + and unnecessary +- See NEWS for changes between 2.0.6 and 2.0.7 +- Added patch to correctly install desktop file. Oops. + +* Wed Jul 12 2000 Prospector +- automatic rebuild + +* Tue Jul 03 2000 Preston Brown +- larger buffers for reading /proc/stat + +* Tue Jun 13 2000 Preston Brown +- FHS paths + +* Tue May 30 2000 Preston Brown +- add smp, signal name patches from VA Linux. Thanks guys. + +* Mon May 22 2000 Harald Hoyer +- added sysctl.conf (5) man page + +* Wed May 10 2000 Bill Nottingham +- fix PAGE_SIZE mismatch on ia64 + +* Sun May 7 2000 Bill Nottingham +- rebuild with different optimizations for ia64 + +* Fri Mar 24 2000 Bernhard Rosenkraenzer +- rebuild with current ncurses + +* Tue Mar 7 2000 Bill Nottingham +- fix end-of-file behavior in sysctl + +* Mon Feb 07 2000 Preston Brown +- wmconfig -> desktop + +* Mon Feb 7 2000 Jakub Jelinek +- don't try to load System.map (and spit error messages if it does not + exist) if ps or top are not going to use it, both to speed things up + and remove the ugly messages when they don't make sense. +- in top, print the possible error messages using standard top SHOWMESSAGE + (because it will be now printed out when already in terminal mode). + +* Thu Feb 3 2000 Matt Wilson +- added patch to prevent divide by zero on UltraSparc +- gzip man pages + diff --git a/packaging/procps.spec b/packaging/procps.spec new file mode 100644 index 0000000..ca2920e --- /dev/null +++ b/packaging/procps.spec @@ -0,0 +1,188 @@ +#specfile originally created for Fedora, modified for Moblin Linux +Summary: System and process monitoring utilities +Name: procps +Version: 3.2.8 +Release: 1 +License: GPLv2+ and LGPLv2+ +Group: Applications/System +URL: http://procps.sourceforge.net +Source: http://procps.sourceforge.net/procps-%{version}.tar.gz +Source1: FAQ +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +Patch1: procps-3.2.7-selinux.patch +Patch2: procps-3.1.15-misc.patch +Patch3: procps-3.2.3-FAQ.patch +Patch4: procps-3.2.1-selinux-workaround.patch +Patch6: procps-3.2.3-noproc.patch +Patch7: procps-3.2.3-pseudo.patch +Patch8: procps-3.2.4-0x9b.patch +# 157725 - sysctl -A returns an error +Patch9: procps-3.2.5-sysctl-writeonly.patch +# 161449 - "top" ignores user and system toprc +Patch10: procps-3.2.5-top-rc.patch +# 161303 - 'top' failed when remove cpus +# 186017 - Top "Cpu0" line never updates on single processor machine +Patch11: procps-3.2.7-top-remcpu.patch +# Selinux +Patch12: procps-3.2.6-libselinux.patch +# 177453 - for VIRT use proc->vm_size rather then proc->size (workaround) +Patch13: procps-3.2.6-top-env-vmsize.patch +# 174619 - workaround for reliable Cpu(s) data in the first loop +Patch14: procps-3.2.6-top-env-cpuloop.patch +# 185299 - cpu steal time support +Patch15: procps-3.2.7-vmstat-cpusteal.patch +# 134516 - ps ignores /proc/#/cmdline if contents 2047 bytes +Patch16: procps-3.2.7-longcmd.patch +# 189349 - 32bit vmstat on 64bit kernel +Patch17: procps-3.2.7-vmstat-pgpg.patch +# 212637 - sysctl using deprecated syscall +# 228870 - process `sysctl' is using deprecated sysctl ... +Patch18: procps-3.2.7-sysctl-ignore.patch +# 140975 - top corrupts screen when sorting on first column +Patch19: procps-3.2.7-top-sorthigh.path +# 234546 - 'w' doesn't give correct information about what's being run. +Patch20: procps-3.2.7-w-best.patch +# 183029 - watch ignores unicode characters +#Patch21: procps-3.2.7-watch-unicode.patch +# 222251 - STIME can jitter by one second +Patch22: procps-3.2.7-ps-stime.patch +#244152 - ps truncates eip and esp to 32-bit values on 64-bit systems +Patch23: procps-3.2.7-ps-eip64.patch +#244960 - ps manpage formatted incorrectly +Patch24: procps-3.2.7-psman.patch +#255441 - ldopen libselinux.so.1 instead of libselinux.so +Patch25: procps-3.2.7-ps-libselinux.patch +#185994 - error when using "Single Cpu = Off" option +Patch26: procps-3.2.7-top-cpu0.patch +#354001 - CPU value in top is reported as an integer +Patch27: procps-3.2.7-top-cpuint.patch +#296471 - update top man page +Patch28: procps-3.2.7-top-manpage.patch +#440694 - strange text after selecting few field +Patch30: procps-3.2.7-top-clrscr.patch +#435453 - errors with man -t formatting of ps man page +Patch31: procps-3.2.7-ps-man-fmt.patch +#472783 - 'vmstat -p ', +# the detailed statistics of the partition name is not output. +Patch32: procps-3.2.7-vmstat-partstats-long.patch +# Fix vmstat header to be 80 chars not 81 +Patch33: procps-3.2.7-vmstat-header.patch +# rhel bug #475963: slabtop -o should display the info once +Patch34: procps-3.2.7-slabtop-once.patch +#476134 - added timestamp to vmstat with new option -t +Patch35: procps-3.2.7-vmstat-timestamp.patch +#manual page updated to document the -t functionality +Patch36: procps-3.2.7-vmstat-timestamp-manpage.patch +# 'requested writes' display in partition statistics +Patch37: procps-3.2.7-vmstat-partstats-reqwrites.patch +# '-l' option of 'free' documented +Patch38: procps-3.2.7-free-hlmem.patch +# enable core dump generation +Patch39: procps-enable-core.patch +# fix "Unknown HZ value! (??) Assume 100." issue +Patch40: unknown-hz-value-fix.patch + +BuildRequires: ncurses-devel + +%description +The procps package contains a set of system utilities that provide +system information. Procps includes ps, free, skill, pkill, pgrep, +snice, tload, top, uptime, vmstat, w, watch and pdwx. The ps command +displays a snapshot of running processes. The top command provides +a repetitive update of the statuses of running processes. The free +command displays the amounts of free and used memory on your +system. The skill command sends a terminate command (or another +specified signal) to a specified set of processes. The snice +command is used to change the scheduling priority of specified +processes. The tload command prints a graph of the current system +load average to a specified tty. The uptime command displays the +current time, how long the system has been running, how many users +are logged on, and system load averages for the past one, five, +and fifteen minutes. The w command displays a list of the users +who are currently logged on and what they are running. The watch +program watches a running program. The vmstat command displays +virtual memory statistics about processes, memory, paging, block +I/O, traps, and CPU activity. The pwdx command reports the current +working directory of a process or processes. + +%prep +%setup -q + +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +#%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 +%patch38 -p1 +%patch39 -p1 +%patch40 -p1 + +cp %SOURCE1 . + +%build +make SHARED=1 CFLAGS="$RPM_OPT_FLAGS" W_SHOWFROM=-DW_SHOWFROM lib64=%{_lib} + +%install +rm -rf %{buildroot} +make ldconfig=true DESTDIR=%{buildroot} lib64=%{_lib} install="install -D" \ + SKIP="/bin/kill /usr/share/man/man1/kill.1" install +mkdir -p %{buildroot}/%{_docdir}/procps-%{version} +# keep 'rpm' happy... +chmod -R u+w %{buildroot}/sbin +chmod -R u+w %{buildroot}/bin +chmod -R u+w %{buildroot}/usr/bin +chmod -R u+w %{buildroot}/lib* + +%clean +rm -rf %{buildroot} + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%defattr(0644,root,root,755) +%doc NEWS BUGS TODO FAQ +%attr(755,root,root) /%{_lib}/* +%attr(755,root,root) /bin/ps +%attr(755,root,root) /sbin/sysctl +%attr(755,root,root) /usr/bin/* + +%doc %attr(0644,root,root) %{_mandir}/man1/* +%doc %attr(0644,root,root) %{_mandir}/man8/* +%doc %attr(0644,root,root) %{_mandir}/man5/* + diff --git a/packaging/unknown-hz-value-fix.patch b/packaging/unknown-hz-value-fix.patch new file mode 100644 index 0000000..63f1f89 --- /dev/null +++ b/packaging/unknown-hz-value-fix.patch @@ -0,0 +1,63 @@ +From 8e8a824533643de4a4a221358a8c5ae7d4e9e474 Mon Sep 17 00:00:00 2001 +From: Ameya Palande +Date: Wed, 28 Jul 2010 14:41:24 +0300 +Subject: [PATCH] unknown hz value fix + +Fixes linux version detection. This relied on a side-effect of the elf loader +and previously hoped that constructors would be called in a certain order. +That behavior was undefined and hence unreliable. For newer kernels, an elf +note is now checked to extract HZ tick count. + +Original author: +Backported to meego by: Ameya Palande +--- + proc/sysinfo.c | 1 + + proc/version.c | 5 ++--- + proc/version.h | 1 + + 3 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/proc/sysinfo.c b/proc/sysinfo.c +index 0ad7fba..793829f 100644 +--- a/proc/sysinfo.c ++++ b/proc/sysinfo.c +@@ -212,6 +212,7 @@ static int check_for_privs(void){ + static void init_libproc(void) __attribute__((constructor)); + static void init_libproc(void){ + have_privs = check_for_privs(); ++ init_Linux_version(); // make sure we have version before continuing... + // ought to count CPUs in /proc/stat instead of relying + // on glibc, which foolishly tries to parse /proc/cpuinfo + // +diff --git a/proc/version.c b/proc/version.c +index 391b9bb..2049e15 100644 +--- a/proc/version.c ++++ b/proc/version.c +@@ -35,11 +35,10 @@ void display_version(void) { + + int linux_version_code; + +-static void init_Linux_version(void) __attribute__((constructor)); +-static void init_Linux_version(void) { ++void init_Linux_version(void) { + static struct utsname uts; + int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */ +- ++ + if (uname(&uts) == -1) /* failure implies impending death */ + exit(1); + if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3) +diff --git a/proc/version.h b/proc/version.h +index 492d251..28c9b49 100644 +--- a/proc/version.h ++++ b/proc/version.h +@@ -14,6 +14,7 @@ + + EXTERN_C_BEGIN + ++extern void init_Linux_version(void); /* initialize linux version */ + extern void display_version(void); /* display suite version */ + extern const char procps_version[]; /* global buf for suite version */ + extern const char procps_number_version[]; /* global buf for suite number version */ +-- +1.7.0.4 + diff --git a/pgrep.1 b/pgrep.1 new file mode 100644 index 0000000..ae2edc2 --- /dev/null +++ b/pgrep.1 @@ -0,0 +1,162 @@ +.\" Manual page for pgrep / pkill. +.\" Licensed under version 2 of the GNU General Public License. +.\" Copyright 2000 Kjetil Torgrim Homme +.\" +.TH PGREP 1 "June 25, 2000" "Linux" "Linux User's Manual" +.SH NAME +pgrep, pkill \- look up or signal processes based on name and other attributes + +.SH SYNOPSIS +pgrep [\-flvx] [\-d \fIdelimiter\fP] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...] +.br + [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...] +.br + [\-t \fIterm\fP,...] [\fIpattern\fP] + +pkill [\-\fIsignal\fP] [\-fvx] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...] +.br + [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...] +.br + [\-t \fIterm\fP,...] [\fIpattern\fP] + +.SH DESCRIPTION +\fBpgrep\fP looks through the currently running processes and lists the +process IDs which matches the selection criteria to stdout. All +the criteria have to match. For example, + +pgrep -u root sshd + +will only list the processes called \fBsshd\fP AND owned by \fBroot\fP. +On the other hand, + +pgrep -u root,daemon + +will list the processes owned by \fBroot\fP OR \fBdaemon\fP. + +\fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP) +to each process instead of listing them on stdout. + +.SH OPTIONS +.TP +\-d \fIdelimiter\fP +Sets the string used to delimit each process ID in the output (by +default a newline). (\fBpgrep\fP only.) +.TP +\-f +The \fIpattern\fP is normally only matched against the process name. +When \-f is set, the full command line is used. +.TP +\-g \fIpgrp\fP,... +Only match processes in the process group IDs listed. Process group 0 +is translated into \fBpgrep\fP's or \fBpkill\fP's own process group. +.TP +\-G \fIgid\fP,... +Only match processes whose real group ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-l +List the process name as well as the process ID. (\fBpgrep\fP only.) +.TP +\-n +Select only the newest (most recently started) of the matching +processes. +.TP +\-o +Select only the oldest (least recently started) of the matching +processes. +.TP +\-P \fIppid\fP,... +Only match processes whose parent process ID is listed. +.TP +\-s \fIsid\fP,... +Only match processes whose process session ID is listed. Session ID 0 +is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID. +.TP +\-t \fIterm\fP,... +Only match processes whose controlling terminal is listed. The +terminal name should be specified without the "/dev/" prefix. +.TP +\-u \fIeuid\fP,... +Only match processes whose effective user ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-U \fIuid\fP,... +Only match processes whose real user ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-v +Negates the matching. +.TP +\-x +Only match processes whose name (or command line if \-f is specified) +\fBexactly\fP match the \fIpattern\fP. +.TP +\-\fIsignal\fP +Defines the signal to send to each matched process. Either the +numeric or the symbolic signal name can be used. (\fBpkill\fP only.) + +.SH OPERANDS +.TP +\fIpattern\fP +Specifies an Extended Regular Expression for matching against the +process names or command lines. + +.SH EXAMPLES +Example 1: Find the process ID of the \fBnamed\fP daemon: + +unix$ pgrep \-u root named + +Example 2: Make \fBsyslog\fP reread its configuration file: + +unix$ pkill \-HUP syslogd + +Example 3: Give detailed information on all \fBxterm\fP processes: + +unix$ ps \-fp $(pgrep \-d, \-x xterm) + +Example 4: Make all \fBnetscape\fP processes run nicer: + +unix$ renice +4 `pgrep netscape` + +.SH "EXIT STATUS" +.TP +.I "0" +One or more processes matched the criteria. +.TP +.I "1" +No processes matched. +.TP +.I "2" +Syntax error in the command line. +.TP +.I "3" +Fatal error: out of memory etc. + +.SH NOTES +The process name used for matching is limited to the 15 characters +present in the output of /proc/\fIpid\fP/stat. Use the \-f option to +match against the complete command line, /proc/\fIpid\fP/cmdline. + +The running \fBpgrep\fP or \fBpkill\fP process will never report +itself as a match. + +.SH BUGS +The options \-n and \-o and \-v can not be combined. Let me know if +you need to do this. + +Defunct processes are reported. + +.SH "SEE ALSO" +ps(1) regex(7) signal(7) killall(1) skill(1) kill(1) kill(2) + +.SH STANDARDS +\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This +implementation is fully compatible. + +.SH AUTHOR +Kjetil Torgrim Homme + +Albert Cahalan is the current maintainer of +the procps package. + +Please send bug reports to diff --git a/pgrep.c b/pgrep.c new file mode 100644 index 0000000..bbb7cd1 --- /dev/null +++ b/pgrep.c @@ -0,0 +1,732 @@ +/* emacs settings: -*- c-basic-offset: 8 tab-width: 8 -*- + * + * pgrep/pkill -- utilities to filter the process table + * + * Copyright 2000 Kjetil Torgrim Homme + * + * May be distributed under the conditions of the + * GNU General Public License; a copy is in COPYING + * + * Changes by Albert Cahalan, 2002,2006. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/readproc.h" +#include "proc/sig.h" +#include "proc/devname.h" +#include "proc/sysinfo.h" +#include "proc/version.h" /* procps_version */ + +// EXIT_SUCCESS is 0 +// EXIT_FAILURE is 1 +#define EXIT_USAGE 2 +#define EXIT_FATAL 3 + +static int i_am_pkill = 0; +static const char *progname = "pgrep"; + +union el { + long num; + char * str; +}; + +/* User supplied arguments */ + +static int opt_full = 0; +static int opt_long = 0; +static int opt_oldest = 0; +static int opt_newest = 0; +static int opt_negate = 0; +static int opt_exact = 0; +static int opt_signal = SIGTERM; +static int opt_lock = 0; +static int opt_case = 0; + +static const char *opt_delim = "\n"; +static union el *opt_pgrp = NULL; +static union el *opt_rgid = NULL; +static union el *opt_pid = NULL; +static union el *opt_ppid = NULL; +static union el *opt_sid = NULL; +static union el *opt_term = NULL; +static union el *opt_euid = NULL; +static union el *opt_ruid = NULL; +static char *opt_pattern = NULL; +static char *opt_pidfile = NULL; + +static int usage (int opt) NORETURN; +static int usage (int opt) +{ + int err = (opt=='?'); /* getopt() uses '?' to mark an error */ + FILE *fp = err ? stderr : stdout; + + if (i_am_pkill) + fprintf (fp, "Usage: pkill [-SIGNAL] [-fvx] "); + else + fprintf (fp, "Usage: pgrep [-flvx] [-d DELIM] "); + fprintf (fp, "[-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n" + "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] " + "[PATTERN]\n"); + + exit(err ? EXIT_USAGE : EXIT_SUCCESS); +} + + +static union el *split_list (const char *restrict str, int (*convert)(const char *, union el *)) +{ + char *copy = strdup (str); + char *ptr = copy; + char *sep_pos; + int i = 0; + int size = 0; + union el *list = NULL; + + do { + if (i == size) { + size = size * 5 / 4 + 4; + // add 1 because slot zero is a count + list = realloc (list, 1 + size * sizeof *list); + if (list == NULL) + exit (EXIT_FATAL); + } + sep_pos = strchr (ptr, ','); + if (sep_pos) + *sep_pos = 0; + // Use ++i instead of i++ because slot zero is a count + if (!convert (ptr, &list[++i])) + exit (EXIT_USAGE); + if (sep_pos) + ptr = sep_pos + 1; + } while (sep_pos); + + free (copy); + if (!i) { + free (list); + list = NULL; + } else { + list[0].num = i; + } + return list; +} + +// strict_atol returns a Boolean: TRUE if the input string +// contains a plain number, FALSE if there are any non-digits. +static int strict_atol (const char *restrict str, long *restrict value) +{ + int res = 0; + int sign = 1; + + if (*str == '+') + ++str; + else if (*str == '-') { + ++str; + sign = -1; + } + + for ( ; *str; ++str) { + if (! isdigit (*str)) + return (0); + res *= 10; + res += *str - '0'; + } + *value = sign * res; + return 1; +} + +#include + +// Seen non-BSD code do this: +// +//if (fcntl_lock(pid_fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1) +// return -1; +int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len) +{ + struct flock lock[1]; + + lock->l_type = type; + lock->l_whence = whence; + lock->l_start = start; + lock->l_len = len; + + return fcntl(fd, cmd, lock); +} + + +// We try a read lock. The daemon should have a write lock. +// Seen using flock: FreeBSD code +static int has_flock(int fd) +{ + return flock(fd, LOCK_SH|LOCK_NB)==-1 && errno==EWOULDBLOCK; +} + +// We try a read lock. The daemon should have a write lock. +// Seen using fcntl: libslack +static int has_fcntl(int fd) +{ + struct flock f; // seriously, struct flock is for a fnctl lock! + f.l_type = F_RDLCK; + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; + return fcntl(fd,F_SETLK,&f)==-1 && (errno==EACCES || errno==EAGAIN); +} + +static union el *read_pidfile(void) +{ + char buf[12]; + int fd; + struct stat sbuf; + char *endp; + int pid; + union el *list = NULL; + + fd = open(opt_pidfile, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0) + goto out; + if(fstat(fd,&sbuf) || !S_ISREG(sbuf.st_mode) || sbuf.st_size<1) + goto out; + // type of lock, if any, is not standardized on Linux + if(opt_lock && !has_flock(fd) && !has_fcntl(fd)) + goto out; + memset(buf,'\0',sizeof buf); + buf[read(fd,buf+1,sizeof buf-2)] = '\0'; + pid = strtoul(buf+1,&endp,10); + if(endp<=buf+1 || pid<1 || pid>0x7fffffff) + goto out; + if(*endp && !isspace(*endp)) + goto out; + list = malloc(2 * sizeof *list); + list[0].num = 1; + list[1].num = pid; +out: + close(fd); + return list; +} + +static int conv_uid (const char *restrict name, union el *restrict e) +{ + struct passwd *pwd; + + if (strict_atol (name, &e->num)) + return (1); + + pwd = getpwnam (name); + if (pwd == NULL) { + fprintf (stderr, "%s: invalid user name: %s\n", + progname, name); + return 0; + } + e->num = pwd->pw_uid; + return 1; +} + + +static int conv_gid (const char *restrict name, union el *restrict e) +{ + struct group *grp; + + if (strict_atol (name, &e->num)) + return 1; + + grp = getgrnam (name); + if (grp == NULL) { + fprintf (stderr, "%s: invalid group name: %s\n", + progname, name); + return 0; + } + e->num = grp->gr_gid; + return 1; +} + + +static int conv_pgrp (const char *restrict name, union el *restrict e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: invalid process group: %s\n", + progname, name); + return 0; + } + if (e->num == 0) + e->num = getpgrp (); + return 1; +} + + +static int conv_sid (const char *restrict name, union el *restrict e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: invalid session id: %s\n", + progname, name); + return 0; + } + if (e->num == 0) + e->num = getsid (0); + return 1; +} + + +static int conv_num (const char *restrict name, union el *restrict e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: not a number: %s\n", + progname, name); + return 0; + } + return 1; +} + + +static int conv_str (const char *restrict name, union el *restrict e) +{ + e->str = strdup (name); + return 1; +} + + +static int match_numlist (long value, const union el *restrict list) +{ + int found = 0; + if (list == NULL) + found = 0; + else { + int i; + for (i = list[0].num; i > 0; i--) { + if (list[i].num == value) + found = 1; + } + } + return found; +} + +static int match_strlist (const char *restrict value, const union el *restrict list) +{ + int found = 0; + if (list == NULL) + found = 0; + else { + int i; + for (i = list[0].num; i > 0; i--) { + if (! strcmp (list[i].str, value)) + found = 1; + } + } + return found; +} + +static void output_numlist (const union el *restrict list, int num) +{ + int i; + const char *delim = opt_delim; + for (i = 0; i < num; i++) { + if(i+1==num) + delim = "\n"; + printf ("%ld%s", list[i].num, delim); + } +} + +static void output_strlist (const union el *restrict list, int num) +{ +// FIXME: escape codes + int i; + const char *delim = opt_delim; + for (i = 0; i < num; i++) { + if(i+1==num) + delim = "\n"; + printf ("%s%s", list[i].str, delim); + } +} + +static PROCTAB *do_openproc (void) +{ + PROCTAB *ptp; + int flags = 0; + + if (opt_pattern || opt_full) + flags |= PROC_FILLCOM; + if (opt_ruid || opt_rgid) + flags |= PROC_FILLSTATUS; + if (opt_oldest || opt_newest || opt_pgrp || opt_sid || opt_term) + flags |= PROC_FILLSTAT; + if (!(flags & PROC_FILLSTAT)) + flags |= PROC_FILLSTATUS; // FIXME: need one, and PROC_FILLANY broken + if (opt_euid && !opt_negate) { + int num = opt_euid[0].num; + int i = num; + uid_t *uids = malloc (num * sizeof (uid_t)); + if (uids == NULL) + exit (EXIT_FATAL); + while (i-- > 0) { + uids[i] = opt_euid[i+1].num; + } + flags |= PROC_UID; + ptp = openproc (flags, uids, num); + } else { + ptp = openproc (flags); + } + return ptp; +} + +static regex_t * do_regcomp (void) +{ + regex_t *preg = NULL; + + if (opt_pattern) { + char *re; + char errbuf[256]; + int re_err; + + preg = malloc (sizeof (regex_t)); + if (preg == NULL) + exit (EXIT_FATAL); + if (opt_exact) { + re = malloc (strlen (opt_pattern) + 5); + if (re == NULL) + exit (EXIT_FATAL); + sprintf (re, "^(%s)$", opt_pattern); + } else { + re = opt_pattern; + } + + re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB | opt_case); + if (re_err) { + regerror (re_err, preg, errbuf, sizeof(errbuf)); + fputs(errbuf,stderr); + exit (EXIT_USAGE); + } + } + return preg; +} + +static union el * select_procs (int *num) +{ + PROCTAB *ptp; + proc_t task; + unsigned long long saved_start_time; // for new/old support + pid_t saved_pid = 0; // for new/old support + int matches = 0; + int size = 0; + regex_t *preg; + pid_t myself = getpid(); + union el *list = NULL; + char cmd[4096]; + + ptp = do_openproc(); + preg = do_regcomp(); + + if (opt_newest) saved_start_time = 0ULL; + if (opt_oldest) saved_start_time = ~0ULL; + if (opt_newest) saved_pid = 0; + if (opt_oldest) saved_pid = INT_MAX; + + memset(&task, 0, sizeof (task)); + while(readproc(ptp, &task)) { + int match = 1; + + if (task.XXXID == myself) + continue; + else if (opt_newest && task.start_time < saved_start_time) + match = 0; + else if (opt_oldest && task.start_time > saved_start_time) + match = 0; + else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid)) + match = 0; + else if (opt_pid && ! match_numlist (task.tgid, opt_pid)) + match = 0; + else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp)) + match = 0; + else if (opt_euid && ! match_numlist (task.euid, opt_euid)) + match = 0; + else if (opt_ruid && ! match_numlist (task.ruid, opt_ruid)) + match = 0; + else if (opt_rgid && ! match_numlist (task.rgid, opt_rgid)) + match = 0; + else if (opt_sid && ! match_numlist (task.session, opt_sid)) + match = 0; + else if (opt_term) { + if (task.tty == 0) { + match = 0; + } else { + char tty[256]; + dev_to_tty (tty, sizeof(tty) - 1, + task.tty, task.XXXID, ABBREV_DEV); + match = match_strlist (tty, opt_term); + } + } + if (opt_long || (match && opt_pattern)) { + if (opt_full && task.cmdline) { + int i = 0; + int bytes = sizeof (cmd) - 1; + + /* make sure it is always NUL-terminated */ + cmd[bytes] = 0; + /* make room for SPC in loop below */ + --bytes; + + strncpy (cmd, task.cmdline[i], bytes); + bytes -= strlen (task.cmdline[i++]); + while (task.cmdline[i] && bytes > 0) { + strncat (cmd, " ", bytes); + strncat (cmd, task.cmdline[i], bytes); + bytes -= strlen (task.cmdline[i++]) + 1; + } + } else { + strcpy (cmd, task.cmd); + } + } + + if (match && opt_pattern) { + if (regexec (preg, cmd, 0, NULL, 0) != 0) + match = 0; + } + + if (match ^ opt_negate) { /* Exclusive OR is neat */ + if (opt_newest) { + if (saved_start_time == task.start_time && + saved_pid > task.XXXID) + continue; + saved_start_time = task.start_time; + saved_pid = task.XXXID; + matches = 0; + } + if (opt_oldest) { + if (saved_start_time == task.start_time && + saved_pid < task.XXXID) + continue; + saved_start_time = task.start_time; + saved_pid = task.XXXID; + matches = 0; + } + if (matches == size) { + size = size * 5 / 4 + 4; + list = realloc(list, size * sizeof *list); + if (list == NULL) + exit (EXIT_FATAL); + } + if (opt_long) { + char buff[5096]; // FIXME + sprintf (buff, "%d %s", task.XXXID, cmd); + list[matches++].str = strdup (buff); + } else { + list[matches++].num = task.XXXID; + } + } + + memset (&task, 0, sizeof (task)); + } + closeproc (ptp); + + *num = matches; + return list; +} + + +static void parse_opts (int argc, char **argv) +{ + char opts[32] = ""; + int opt; + int criteria_count = 0; + + if (strstr (argv[0], "pkill")) { + i_am_pkill = 1; + progname = "pkill"; + /* Look for a signal name or number as first argument */ + if (argc > 1 && argv[1][0] == '-') { + int sig; + sig = signal_name_to_number (argv[1] + 1); + if (sig == -1 && isdigit (argv[1][1])) + sig = atoi (argv[1] + 1); + if (sig != -1) { + int i; + for (i = 2; i < argc; i++) + argv[i-1] = argv[i]; + --argc; + opt_signal = sig; + } + } + } else { + /* These options are for pgrep only */ + strcat (opts, "ld:"); + } + + strcat (opts, "LF:fnovxP:g:s:u:U:G:t:?V"); + + while ((opt = getopt (argc, argv, opts)) != -1) { + switch (opt) { +// case 'D': // FreeBSD: print info about non-matches for debugging +// break; + case 'F': // FreeBSD: the arg is a file containing a PID to match + opt_pidfile = strdup (optarg); + ++criteria_count; + break; + case 'G': // Solaris: match rgid/rgroup + opt_rgid = split_list (optarg, conv_gid); + if (opt_rgid == NULL) + usage (opt); + ++criteria_count; + break; +// case 'I': // FreeBSD: require confirmation before killing +// break; +// case 'J': // Solaris: match by project ID (name or number) +// break; + case 'L': // FreeBSD: fail if pidfile (see -F) not locked + opt_lock++; + break; +// case 'M': // FreeBSD: specify core (OS crash dump) file +// break; +// case 'N': // FreeBSD: specify alternate namelist file (for us, System.map -- but we don't need it) +// break; + case 'P': // Solaris: match by PPID + opt_ppid = split_list (optarg, conv_num); + if (opt_ppid == NULL) + usage (opt); + ++criteria_count; + break; +// case 'S': // FreeBSD: don't ignore the built-in kernel tasks +// break; +// case 'T': // Solaris: match by "task ID" (probably not a Linux task) +// break; + case 'U': // Solaris: match by ruid/rgroup + opt_ruid = split_list (optarg, conv_uid); + if (opt_ruid == NULL) + usage (opt); + ++criteria_count; + break; + case 'V': + fprintf(stdout, "%s (%s)\n", progname, procps_version); + exit(EXIT_SUCCESS); +// case 'c': // Solaris: match by contract ID +// break; + case 'd': // Solaris: change the delimiter + opt_delim = strdup (optarg); + break; + case 'f': // Solaris: match full process name (as in "ps -f") + opt_full = 1; + break; + case 'g': // Solaris: match pgrp + opt_pgrp = split_list (optarg, conv_pgrp); + if (opt_pgrp == NULL) + usage (opt); + ++criteria_count; + break; +// case 'i': // FreeBSD: ignore case. OpenBSD: withdrawn. See -I. This sucks. +// if (opt_case) +// usage (opt); +// opt_case = REG_ICASE; +// break; +// case 'j': // FreeBSD: restricted to the given jail ID +// break; + case 'l': // Solaris: long output format (pgrep only) Should require -f for beyond argv[0] maybe? + opt_long = 1; + break; + case 'n': // Solaris: match only the newest + if (opt_oldest|opt_negate|opt_newest) + usage (opt); + opt_newest = 1; + ++criteria_count; + break; + case 'o': // Solaris: match only the oldest + if (opt_oldest|opt_negate|opt_newest) + usage (opt); + opt_oldest = 1; + ++criteria_count; + break; + case 's': // Solaris: match by session ID -- zero means self + opt_sid = split_list (optarg, conv_sid); + if (opt_sid == NULL) + usage (opt); + ++criteria_count; + break; + case 't': // Solaris: match by tty + opt_term = split_list (optarg, conv_str); + if (opt_term == NULL) + usage (opt); + ++criteria_count; + break; + case 'u': // Solaris: match by euid/egroup + opt_euid = split_list (optarg, conv_uid); + if (opt_euid == NULL) + usage (opt); + ++criteria_count; + break; + case 'v': // Solaris: as in grep, invert the matching (uh... applied after selection I think) + if (opt_oldest|opt_negate|opt_newest) + usage (opt); + opt_negate = 1; + break; + // OpenBSD -x, being broken, does a plain string + case 'x': // Solaris: use ^(regexp)$ in place of regexp (FreeBSD too) + opt_exact = 1; + break; +// case 'z': // Solaris: match by zone ID +// break; + case '?': + usage (opt); + break; + } + } + + if(opt_lock && !opt_pidfile){ + fprintf(stderr, "%s: -L without -F makes no sense\n",progname); + usage(0); + } + + if(opt_pidfile){ + opt_pid = read_pidfile(); + if(!opt_pid){ + fprintf(stderr, "%s: pidfile not valid\n",progname); + usage(0); + } + } + + if (argc - optind == 1) + opt_pattern = argv[optind]; + else if (argc - optind > 1) + usage (0); + else if (criteria_count == 0) { + fprintf (stderr, "%s: No matching criteria specified\n", + progname); + usage (0); + } +} + + +int main (int argc, char *argv[]) +{ + union el *procs; + int num; + + parse_opts (argc, argv); + + procs = select_procs (&num); + if (i_am_pkill) { + int i; + for (i = 0; i < num; i++) { + if (kill (procs[i].num, opt_signal) != -1) continue; + if (errno==ESRCH) continue; // gone now, which is OK + fprintf (stderr, "pkill: %ld - %s\n", + procs[i].num, strerror (errno)); + } + } else { + if (opt_long) + output_strlist(procs,num); + else + output_numlist(procs,num); + } + return !num; // exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE) +} diff --git a/pkill.1 b/pkill.1 new file mode 100644 index 0000000..0e94b52 --- /dev/null +++ b/pkill.1 @@ -0,0 +1 @@ +.so man1/pgrep.1 diff --git a/pmap.1 b/pmap.1 new file mode 100644 index 0000000..a91d0e8 --- /dev/null +++ b/pmap.1 @@ -0,0 +1,39 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for pmap. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan. +.\" +.TH PMAP 1 "October 26, 2002" "Linux" "Linux User's Manual" +.SH NAME +pmap \- report memory map of a process + +.SH SYNOPSIS +.nf +pmap [ -x | -d ] [ -q ] pids... +pmap -V +.fi + +.SH DESCRIPTION +The pmap command reports the memory map of a process or processes. + +.SH "GENERAL OPTIONS" +.TS +l l l. +-x extended Show the extended format. +-d device Show the device format. +-q quiet Do not display some header/footer lines. +-V show version Displays version of program. +.TE + +.SH "SEE ALSO" +ps(1) pgrep(1) + +.SH STANDARDS +No standards apply, but pmap looks an awful lot like a SunOS command. + +.SH AUTHOR +Albert Cahalan wrote pmap in 2002, and is the current +maintainer of the procps collection. Please send bug reports +to . diff --git a/pmap.c b/pmap.c new file mode 100644 index 0000000..6b865fc --- /dev/null +++ b/pmap.c @@ -0,0 +1,372 @@ +/* + * Copyright 2002 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "proc/readproc.h" +#include "proc/version.h" +#include "proc/escape.h" + +static void usage(void) NORETURN; +static void usage(void){ + fprintf(stderr, + "Usage: pmap [-x | -d] [-q] [-A low,high] pid...\n" + "-x show details\n" + "-d show offset and device number\n" + "-q quiet; less header/footer info\n" + "-V show the version number\n" + "-A limit results to the given range\n" + ); + exit(1); +} + + +static unsigned KLONG range_low; +static unsigned KLONG range_high = ~0ull; + +static int V_option; +static int r_option; // ignored -- for SunOS compatibility +static int x_option; +static int d_option; +static int q_option; + +static unsigned shm_minor = ~0u; + +static void discover_shm_minor(void){ + void *addr; + int shmid; + char mapbuf[256]; + + if(!freopen("/proc/self/maps", "r", stdin)) return; + + // create + shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666); + if(shmid==-1) return; // failed; oh well + // attach + addr = shmat(shmid, NULL, SHM_RDONLY); + if(addr==(void*)-1) goto out_destroy; + + while(fgets(mapbuf, sizeof mapbuf, stdin)){ + char flags[32]; + char *tmp; // to clean up unprintables + unsigned KLONG start, end; + unsigned long long file_offset, inode; + unsigned dev_major, dev_minor; + sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode); + tmp = strchr(mapbuf,'\n'); + if(tmp) *tmp='\0'; + tmp = mapbuf; + while(*tmp){ + if(!isprint(*tmp)) *tmp='?'; + tmp++; + } + if(start > (unsigned long)addr) continue; + if(dev_major) continue; + if(flags[3] != 's') continue; + if(strstr(mapbuf,"/SYSV")){ + shm_minor = dev_minor; + break; + } + } + + if(shmdt(addr)) perror("shmdt"); + +out_destroy: + if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID"); + + return; +} + + +static const char *mapping_name(proc_t *p, unsigned KLONG addr, unsigned KLONG len, const char *mapbuf, unsigned showpath, unsigned dev_major, unsigned dev_minor, unsigned long long inode){ + const char *cp; + + if(!dev_major && dev_minor==shm_minor && strstr(mapbuf,"/SYSV")){ + static char shmbuf[64]; + snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode); + return shmbuf; + } + + cp = strrchr(mapbuf,'/'); + if(cp){ + if(showpath) return strchr(mapbuf,'/'); + return cp[1] ? cp+1 : cp; + } + + cp = strchr(mapbuf,'/'); + if(cp){ + if(showpath) return cp; + return strrchr(cp,'/') + 1; // it WILL succeed + } + + cp = " [ anon ]"; + if( (p->start_stack >= addr) && (p->start_stack <= addr+len) ) cp = " [ stack ]"; + return cp; +} + +static int one_proc(proc_t *p){ + char buf[32]; + char mapbuf[9600]; + char cmdbuf[512]; + unsigned long total_shared = 0ul; + unsigned long total_private_readonly = 0ul; + unsigned long total_private_writeable = 0ul; + + // Overkill, but who knows what is proper? The "w" prog + // uses the tty width to determine this. + int maxcmd = 0xfffff; + + sprintf(buf,"/proc/%u/maps",p->tgid); + if(!freopen(buf, "r", stdin)) return 1; + + escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS); + printf("%u: %s\n", p->tgid, cmdbuf); + + if(!q_option && (x_option|d_option)){ + if(x_option){ + if(sizeof(KLONG)==4) printf("Address Kbytes RSS Anon Locked Mode Mapping\n"); + else printf("Address Kbytes RSS Anon Locked Mode Mapping\n"); + } + if(d_option){ + if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n"); + else printf("Address Kbytes Mode Offset Device Mapping\n"); + } + } + + while(fgets(mapbuf,sizeof mapbuf,stdin)){ + char flags[32]; + char *tmp; // to clean up unprintables + unsigned KLONG start, end, diff; + unsigned long long file_offset, inode; + unsigned dev_major, dev_minor; + sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode); + + if(start > range_high) + break; + if(end < range_low) + continue; + + tmp = strchr(mapbuf,'\n'); + if(tmp) *tmp='\0'; + tmp = mapbuf; + while(*tmp){ + if(!isprint(*tmp)) *tmp='?'; + tmp++; + } + + diff = end-start; + if(flags[3]=='s') total_shared += diff; + if(flags[3]=='p'){ + flags[3] = '-'; + if(flags[1]=='w') total_private_writeable += diff; + else total_private_readonly += diff; + } + + // format used by Solaris 9 and procps-3.2.0+ + // an 'R' if swap not reserved (MAP_NORESERVE, SysV ISM shared mem, etc.) + flags[4] = '-'; + flags[5] = '\0'; + + if(x_option){ + const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode); + printf( + (sizeof(KLONG)==8) + ? "%016"KLF"x %7lu - - - %s %s\n" + : "%08lx %7lu - - - %s %s\n", + start, + (unsigned long)(diff>>10), + flags, + cp + ); + } + if(d_option){ + const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode); + printf( + (sizeof(KLONG)==8) + ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n" + : "%08lx %7lu %s %016Lx %03x:%05x %s\n", + start, + (unsigned long)(diff>>10), + flags, + file_offset, + dev_major, dev_minor, + cp + ); + } + if(!x_option && !d_option){ + const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode); + printf( + (sizeof(KLONG)==8) + ? "%016"KLF"x %6luK %s %s\n" + : "%08lx %6luK %s %s\n", + start, + (unsigned long)(diff>>10), + flags, + cp + ); + } + + } + + + + + if(!q_option){ + if(x_option){ + if(sizeof(KLONG)==8){ + printf("---------------- ------ ------ ------ ------\n"); + printf( + "total kB %15ld - - -\n", + (total_shared + total_private_writeable + total_private_readonly) >> 10 + ); + }else{ + printf("-------- ------- ------- ------- -------\n"); + printf( + "total kB %7ld - - -\n", + (total_shared + total_private_writeable + total_private_readonly) >> 10 + ); + } + } + if(d_option){ + printf( + "mapped: %ldK writeable/private: %ldK shared: %ldK\n", + (total_shared + total_private_writeable + total_private_readonly) >> 10, + total_private_writeable >> 10, + total_shared >> 10 + ); + } + if(!x_option && !d_option){ + if(sizeof(KLONG)==8) printf(" total %16ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10); + else printf(" total %8ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10); + } + } + + return 0; +} + + +int main(int argc, char *argv[]){ + unsigned *pidlist; + unsigned count = 0; + PROCTAB* PT; + proc_t p; + int ret = 0; + + if(argc<2) usage(); + pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps + + while(*++argv){ + if(!strcmp("--version",*argv)){ + V_option++; + continue; + } + if(**argv=='-'){ + char *walk = *argv; + if(!walk[1]) usage(); + while(*++walk){ + switch(*walk){ + case 'V': + V_option++; + break; + case 'x': + x_option++; + break; + case 'r': + r_option++; + break; + case 'd': + d_option++; + break; + case 'q': + q_option++; + break; + case 'A':{ + char *arg1; + if(walk[1]){ + arg1 = walk+1; + walk += strlen(walk)-1; + }else{ + arg1 = *++argv; + if(!arg1) + usage(); + } + char *arg2 = strchr(arg1,','); + if(arg2) + *arg2 = '\0'; + arg2 = arg2 ? arg2++ : arg1; + + if(*arg1) + range_low = STRTOUKL(arg1,&arg1,16); + if(*arg2) + range_high = STRTOUKL(arg2,&arg2,16); + if(*arg1 || *arg2) + usage(); + } + break; + case 'a': // Sun prints anon/swap reservations + case 'F': // Sun forces hostile ptrace-like grab + case 'l': // Sun shows unresolved dynamic names + case 'L': // Sun shows lgroup info + case 's': // Sun shows page sizes + case 'S': // Sun shows swap reservations + default: + usage(); + } + } + }else{ + char *walk = *argv; + char *endp; + unsigned long pid; + if(!strncmp("/proc/",walk,6)){ + walk += 6; + // user allowed to do: pmap /proc/* + if(*walk<'0' || *walk>'9') continue; + } + if(*walk<'0' || *walk>'9') usage(); + pid = strtoul(walk, &endp, 0); + if(pid<1ul || pid>0x7ffffffful || *endp) usage(); + pidlist[count++] = pid; + } + } + + if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes + if(V_option){ + if(count|x_option|r_option|d_option|q_option) usage(); + fprintf(stdout, "pmap (%s)\n", procps_version); + return 0; + } + if(count<1) usage(); // no processes + if(d_option && x_option) usage(); + + discover_shm_minor(); + + pidlist[count] = 0; // old libproc interface is zero-terminated + PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist); + while(readproc(PT, &p)){ + ret |= one_proc(&p); + if(p.cmdline) free((void*)*p.cmdline); + count--; + } + closeproc(PT); + + if(count) ret |= 42; // didn't find all processes asked for + return ret; +} diff --git a/proc/COPYING b/proc/COPYING new file mode 100644 index 0000000..92b8903 --- /dev/null +++ b/proc/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/proc/alloc.c b/proc/alloc.c new file mode 100644 index 0000000..4a0aca7 --- /dev/null +++ b/proc/alloc.c @@ -0,0 +1,49 @@ +// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com +// Copyright 2002 Albert Cahalan +// +// This file is placed under the conditions of the GNU Library +// General Public License, version 2, or any later version. +// See file COPYING for information on distribution conditions. + +#include +#include +#include "alloc.h" + +void *xcalloc(void *pointer, int size) { + void * ret; + if (pointer) + free(pointer); + if (!(ret = calloc(1, size))) { + fprintf(stderr, "xcalloc: allocation error, size = %d\n", size); + exit(1); + } + return ret; +} + +void *xmalloc(unsigned int size) { + void *p; + + if (size == 0) + ++size; + p = malloc(size); + if (!p) { + fprintf(stderr, "xmalloc: malloc(%d) failed", size); + perror(NULL); + exit(1); + } + return(p); +} + +void *xrealloc(void *oldp, unsigned int size) { + void *p; + + if (size == 0) + ++size; + p = realloc(oldp, size); + if (!p) { + fprintf(stderr, "xrealloc: realloc(%d) failed", size); + perror(NULL); + exit(1); + } + return(p); +} diff --git a/proc/alloc.h b/proc/alloc.h new file mode 100644 index 0000000..8c5016d --- /dev/null +++ b/proc/alloc.h @@ -0,0 +1,14 @@ +#ifndef PROCPS_PROC_ALLOC_H +#define PROCPS_PROC_ALLOC_H + +#include "procps.h" + +EXTERN_C_BEGIN + +extern void *xrealloc(void *oldp, unsigned int size) MALLOC; +extern void *xmalloc(unsigned int size) MALLOC; +extern void *xcalloc(void *pointer, int size) MALLOC; + +EXTERN_C_END + +#endif diff --git a/proc/devname.c b/proc/devname.c new file mode 100644 index 0000000..32ad954 --- /dev/null +++ b/proc/devname.c @@ -0,0 +1,329 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "version.h" +#include "devname.h" + +// This is the buffer size for a tty name. Any path is legal, +// which makes PAGE_SIZE appropriate (see kernel source), but +// that is only 99% portable and utmp only holds 32 anyway. +// We need at least 20 for guess_name(). +#define TTY_NAME_SIZE 128 + +/* Who uses what: + * + * tty_to_dev w (there is a fancy version in ps) + * dev_to_tty top, ps + */ + +#if 0 +#include +#define MAJOR_OF(d) ((unsigned)major(d)) +#define MINOR_OF(d) ((unsigned)minor(d)) +#else +#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu ) +#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) ) +#undef major +#undef minor +#define major <-- do not use --> +#define minor <-- do not use --> +#endif + +typedef struct tty_map_node { + struct tty_map_node *next; + unsigned short devfs_type; // bool + unsigned short major_number; + unsigned minor_first; + unsigned minor_last; + char name[16]; +} tty_map_node; + +static tty_map_node *tty_map = NULL; + +/* Load /proc/tty/drivers for device name mapping use. */ +static void load_drivers(void){ + char buf[10000]; + char *p; + int fd; + int bytes; + fd = open("/proc/tty/drivers",O_RDONLY); + if(fd == -1) goto fail; + bytes = read(fd, buf, sizeof(buf) - 1); + if(bytes == -1) goto fail; + buf[bytes] = '\0'; + p = buf; + while(( p = strstr(p, " /dev/") )){ // " /dev/" is the second column + tty_map_node *tmn; + int len; + char *end; + p += 6; + end = strchr(p, ' '); + if(!end) continue; + len = end - p; + tmn = calloc(1, sizeof(tty_map_node)); + tmn->next = tty_map; + tty_map = tmn; + /* if we have a devfs type name such as /dev/tts/%d then strip the %d but + keep a flag. */ + if(len >= 3 && !strncmp(end - 2, "%d", 2)){ + len -= 2; + tmn->devfs_type = 1; + } + if(len >= sizeof tmn->name) + len = sizeof tmn->name - 1; // mangle it to avoid overflow + memcpy(tmn->name, p, len); + p = end; /* set p to point past the %d as well if there is one */ + while(*p == ' ') p++; + tmn->major_number = atoi(p); + p += strspn(p, "0123456789"); + while(*p == ' ') p++; + switch(sscanf(p, "%u-%u", &tmn->minor_first, &tmn->minor_last)){ + default: + /* Can't finish parsing this line so we remove it from the list */ + tty_map = tty_map->next; + free(tmn); + break; + case 1: + tmn->minor_last = tmn->minor_first; + break; + case 2: + break; + } + } +fail: + if(fd != -1) close(fd); + if(!tty_map) tty_map = (tty_map_node *)-1; +} + +/* Try to guess the device name from /proc/tty/drivers info. */ +static int driver_name(char *restrict const buf, unsigned maj, unsigned min){ + struct stat sbuf; + tty_map_node *tmn; + if(!tty_map) load_drivers(); + if(tty_map == (tty_map_node *)-1) return 0; + tmn = tty_map; + for(;;){ + if(!tmn) return 0; + if(tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) break; + tmn = tmn->next; + } + sprintf(buf, "/dev/%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */ + if(stat(buf, &sbuf) < 0){ + if(tmn->devfs_type) return 0; + sprintf(buf, "/dev/%s", tmn->name); /* like "/dev/ttyZZ255" */ + if(stat(buf, &sbuf) < 0) return 0; + } + if(min != MINOR_OF(sbuf.st_rdev)) return 0; + if(maj != MAJOR_OF(sbuf.st_rdev)) return 0; + return 1; +} + +// major 204 is a mess -- "Low-density serial ports" +static const char low_density_names[][6] = { +"LU0", "LU1", "LU2", "LU3", +"FB0", +"SA0", "SA1", "SA2", +"SC0", "SC1", "SC2", "SC3", +"FW0", "FW1", "FW2", "FW3", +"AM0", "AM1", "AM2", "AM3", "AM4", "AM5", "AM6", "AM7", +"AM8", "AM9", "AM10", "AM11", "AM12", "AM13", "AM14", "AM15", +"DB0", "DB1", "DB2", "DB3", "DB4", "DB5", "DB6", "DB7", +"SG0", +"SMX0", "SMX1", "SMX2", +"MM0", "MM1", +"CPM0", "CPM1", "CPM2", "CPM3", /* "CPM4", "CPM5", */ // bad allocation? +"IOC0", "IOC1", "IOC2", "IOC3", "IOC4", "IOC5", "IOC6", "IOC7", +"IOC8", "IOC9", "IOC10", "IOC11", "IOC12", "IOC13", "IOC14", "IOC15", +"IOC16", "IOC17", "IOC18", "IOC19", "IOC20", "IOC21", "IOC22", "IOC23", +"IOC24", "IOC25", "IOC26", "IOC27", "IOC28", "IOC29", "IOC30", "IOC31", +"VR0", "VR1", +"IOC84", "IOC85", "IOC86", "IOC87", "IOC88", "IOC89", "IOC90", "IOC91", +"IOC92", "IOC93", "IOC94", "IOC95", "IOC96", "IOC97", "IOC98", "IOC99", +"IOC100", "IOC101", "IOC102", "IOC103", "IOC104", "IOC105", "IOC106", "IOC107", +"IOC108", "IOC109", "IOC110", "IOC111", "IOC112", "IOC113", "IOC114", "IOC115", +"SIOC0", "SIOC1", "SIOC2", "SIOC3", "SIOC4", "SIOC5", "SIOC6", "SIOC7", +"SIOC8", "SIOC9", "SIOC10", "SIOC11", "SIOC12", "SIOC13", "SIOC14", "SIOC15", +"SIOC16", "SIOC17", "SIOC18", "SIOC19", "SIOC20", "SIOC21", "SIOC22", "SIOC23", +"SIOC24", "SIOC25", "SIOC26", "SIOC27", "SIOC28", "SIOC29", "SIOC30", "SIOC31", +"PSC0", "PSC1", "PSC2", "PSC3", "PSC4", "PSC5", +"AT0", "AT1", "AT2", "AT3", "AT4", "AT5", "AT6", "AT7", +"AT8", "AT9", "AT10", "AT11", "AT12", "AT13", "AT14", "AT15", +"NX0", "NX1", "NX2", "NX3", "NX4", "NX5", "NX6", "NX7", +"NX8", "NX9", "NX10", "NX11", "NX12", "NX13", "NX14", "NX15", +"J0", // minor is 186 +"UL0","UL1","UL2","UL3", +"xvc0", // FAIL -- "/dev/xvc0" lacks "tty" prefix +"PZ0","PZ1","PZ2","PZ3", +"TX0","TX1","TX2","TX3","TX4","TX5","TX6","TX7", +"SC0","SC1","SC2","SC3", +"MAX0","MAX1","MAX2","MAX3", +}; + +#if 0 +// test code +#include +#define AS(x) (sizeof(x)/sizeof((x)[0])) +int main(int argc, char *argv[]){ + int i = 0; + while(i 255) return 0; // should never happen; array index protection + t0 = "pqrstuvwxyzabcde"[tmpmin>>4]; + t1 = "0123456789abcdef"[tmpmin&0x0f]; + sprintf(buf, "/dev/tty%c%c", t0, t1); + break; + case 4: + if(min<64){ + sprintf(buf, "/dev/tty%d", min); + break; + } + sprintf(buf, "/dev/ttyS%d", min-64); + break; + case 11: sprintf(buf, "/dev/ttyB%d", min); break; + case 17: sprintf(buf, "/dev/ttyH%d", min); break; + case 19: sprintf(buf, "/dev/ttyC%d", min); break; + case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */ + case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */ + case 24: sprintf(buf, "/dev/ttyE%d", min); break; + case 32: sprintf(buf, "/dev/ttyX%d", min); break; + case 43: sprintf(buf, "/dev/ttyI%d", min); break; + case 46: sprintf(buf, "/dev/ttyR%d", min); break; + case 48: sprintf(buf, "/dev/ttyL%d", min); break; + case 57: sprintf(buf, "/dev/ttyP%d", min); break; + case 71: sprintf(buf, "/dev/ttyF%d", min); break; + case 75: sprintf(buf, "/dev/ttyW%d", min); break; + case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */ + case 105: sprintf(buf, "/dev/ttyV%d", min); break; + case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */ + /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */ + case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break; + case 148: sprintf(buf, "/dev/ttyT%d", min); break; + case 154: sprintf(buf, "/dev/ttySR%d", min); break; + case 156: sprintf(buf, "/dev/ttySR%d", min+256); break; + case 164: sprintf(buf, "/dev/ttyCH%d", min); break; + case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */ + case 172: sprintf(buf, "/dev/ttyMX%d", min); break; + case 174: sprintf(buf, "/dev/ttySI%d", min); break; + case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */ + case 204: + if(min >= sizeof low_density_names / sizeof low_density_names[0]) return 0; + memcpy(buf,"/dev/tty",8); + memcpy(buf+8, low_density_names[min], sizeof low_density_names[0]); + buf[8 + sizeof low_density_names[0]] = '\0'; +// snprintf(buf, 9 + sizeof low_density_names[0], "/dev/tty%.*s", sizeof low_density_names[0], low_density_names[min]); + break; + case 208: sprintf(buf, "/dev/ttyU%d", min); break; + case 216: sprintf(buf, "/dev/ttyUB%d", min); break; // "/dev/rfcomm%d" now? + case 224: sprintf(buf, "/dev/ttyY%d", min); break; + case 227: sprintf(buf, "/dev/3270/tty%d", min); break; /* bummer, HUGE */ + case 229: sprintf(buf, "/dev/iseries/vtty%d", min); break; /* bummer, HUGE */ + case 256: sprintf(buf, "/dev/ttyEQ%d", min); break; + default: return 0; + } + if(stat(buf, &sbuf) < 0) return 0; + if(min != MINOR_OF(sbuf.st_rdev)) return 0; + if(maj != MAJOR_OF(sbuf.st_rdev)) return 0; + return 1; +} + +/* Linux 2.2 can give us filenames that might be correct. + * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected) + * and in /proc/PID/fd/255 (used by bash to remember the tty). + */ +static int link_name(char *restrict const buf, unsigned maj, unsigned min, int pid, const char *restrict name){ + struct stat sbuf; + char path[32]; + int count; + sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */ + count = readlink(path,buf,TTY_NAME_SIZE-1); + if(count == -1) return 0; + buf[count] = '\0'; + if(stat(buf, &sbuf) < 0) return 0; + if(min != MINOR_OF(sbuf.st_rdev)) return 0; + if(maj != MAJOR_OF(sbuf.st_rdev)) return 0; + return 1; +} + +/* number --> name */ +unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) { + static char buf[TTY_NAME_SIZE]; + char *restrict tmp = buf; + unsigned dev = dev_t_dev; + unsigned i = 0; + int c; + if(dev == 0u) goto no_tty; + if(linux_version_code > LINUX_VERSION(2, 7, 0)){ // not likely to make 2.6.xx + if(link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "tty" )) goto abbrev; + } + if(driver_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev; + if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/2" )) goto abbrev; + if( guess_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev; + if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/255")) goto abbrev; + // fall through if unable to find a device file +no_tty: + strcpy(ret, "?"); + return 1; +abbrev: + if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5; + if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3; + if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4; + /* gotta check before we chop or we may chop someone else's memory */ + if(chop + (unsigned long)(tmp-buf) <= sizeof buf) + tmp[chop] = '\0'; + /* replace non-ASCII characters with '?' and return the number of chars */ + for(;;){ + c = *tmp; + tmp++; + if(!c) break; + i++; + if(c<=' ') c = '?'; + if(c>126) c = '?'; + *ret = c; + ret++; + } + *ret = '\0'; + return i; +} + +/* name --> number */ +int tty_to_dev(const char *restrict const name) { + struct stat sbuf; + static char buf[32]; + if(name[0]=='/' && stat(name, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/tty%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/pts/%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + return -1; +} diff --git a/proc/devname.h b/proc/devname.h new file mode 100644 index 0000000..10c2cb6 --- /dev/null +++ b/proc/devname.h @@ -0,0 +1,17 @@ +#ifndef PROC_DEVNAME_H +#define PROC_DEVNAME_H + +#include "procps.h" + +EXTERN_C_BEGIN + +#define ABBREV_DEV 1 /* remove /dev/ */ +#define ABBREV_TTY 2 /* remove tty */ +#define ABBREV_PTS 4 /* remove pts/ */ + +extern unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags); + +extern int tty_to_dev(const char *restrict const name); + +EXTERN_C_END +#endif diff --git a/proc/escape.c b/proc/escape.c new file mode 100644 index 0000000..0eb1418 --- /dev/null +++ b/proc/escape.c @@ -0,0 +1,216 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include "procps.h" +#include "escape.h" +#include "readproc.h" + +#if (__GNU_LIBRARY__ >= 6) +# include +# include +# include /* MB_CUR_MAX */ +# include +# include +#endif + +#if (__GNU_LIBRARY__ >= 6) +static int escape_str_utf8(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){ + int my_cells = 0; + int my_bytes = 0; + mbstate_t s; + + memset(&s, 0, sizeof (s)); + + for(;;) { + wchar_t wc; + int len = 0; + + if(my_cells >= *maxcells || my_bytes+1 >= bufsize) + break; + + if (!(len = mbrtowc (&wc, src, MB_CUR_MAX, &s))) + /* 'str' contains \0 */ + break; + + if (len < 0) { + /* invalid multibyte sequence -- zeroize state */ + memset (&s, 0, sizeof (s)); + *(dst++) = '?'; + src++; + my_cells++; + my_bytes++; + + } else if (len==1) { + /* non-multibyte */ + *(dst++) = isprint(*src) ? *src : '?'; + src++; + my_cells++; + my_bytes++; + + } else if (!iswprint(wc)) { + /* multibyte - no printable */ + *(dst++) = '?'; + src+=len; + my_cells++; + my_bytes++; + + } else { + /* multibyte - printable */ + int wlen = wcwidth(wc); + + if (wlen==0) { + // invisible multibyte -- we don't ignore it, because some terminal + // interpret it wrong and more safe is replace it with '?' + *(dst++) = '?'; + src+=len; + my_cells++; + my_bytes++; + } else { + // multibyte - printable + // Got space? + if (my_cells+wlen > *maxcells || my_bytes+1+len >= bufsize) break; + // 0x9b is control byte for some terminals + if (memchr(src, 0x9B, len)) { + // unsafe multibyte + *(dst++) = '?'; + src+=len; + my_cells++; + my_bytes++; + } else { + // safe multibyte + memcpy(dst, src, len); + my_cells += wlen; + dst += len; + my_bytes += len; + src += len; + } + } + } + //fprintf(stdout, "cells: %d\n", my_cells); + } + *(dst++) = '\0'; + + // fprintf(stderr, "maxcells: %d, my_cells; %d\n", *maxcells, my_cells); + + *maxcells -= my_cells; + return my_bytes; // bytes of text, excluding the NUL +} + +#endif /* __GNU_LIBRARY__ */ + +/* sanitize a string via one-way mangle */ +int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){ + unsigned char c; + int my_cells = 0; + int my_bytes = 0; + const char codes[] = + "Z-------------------------------" + "********************************" + "********************************" + "*******************************-" + "--------------------------------" + "********************************" + "********************************" + "********************************"; + +#if (__GNU_LIBRARY__ >= 6) + static int utf_init=0; + + if(utf_init==0){ + /* first call -- check if UTF stuff is usable */ + char *enc = nl_langinfo(CODESET); + utf_init = enc && strcasecmp(enc, "UTF-8")==0 ? 1 : -1; + } + if (utf_init==1) + /* UTF8 locales */ + return escape_str_utf8(dst, src, bufsize, maxcells); +#endif + + if(bufsize > *maxcells+1) bufsize=*maxcells+1; // FIXME: assumes 8-bit locale + + for(;;){ + if(my_cells >= *maxcells || my_bytes+1 >= bufsize) + break; + c = (unsigned char) *(src++); + if(!c) break; + if(codes[c]=='-') c='?'; + my_cells++; + my_bytes++; + *(dst++) = c; + } + *(dst++) = '\0'; + + *maxcells -= my_cells; + return my_bytes; // bytes of text, excluding the NUL +} + +///////////////////////////////////////////////// + +// escape an argv or environment string array +// +// bytes arg means sizeof(buf) +int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t bytes, int *cells){ + size_t i = 0; + + for(;;){ + i += escape_str(dst+i, *src, bytes-i, cells); + if(bytes-i < 3) break; // need room for space, a character, and the NUL + src++; + if(!*src) break; // need something to print + if (*cells<=1) break; // need room for printed size of text + dst[i++] = ' '; + --*cells; + } + return i; // bytes, excluding the NUL +} + +/////////////////////////////////////////////////// + +int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags){ + int overhead = 0; + int end = 0; + + if(flags & ESC_ARGS){ + const char **lc = (const char**)pp->cmdline; + if(lc && *lc) return escape_strlist(outbuf, lc, bytes, cells); + } + if(flags & ESC_BRACKETS){ + overhead += 2; + } + if(flags & ESC_DEFUNCT){ + if(pp->state=='Z') overhead += 10; // chars in " " + else flags &= ~ESC_DEFUNCT; + } + if(overhead + 1 >= *cells){ // if no room for even one byte of the command name + // you'd damn well better have _some_ space +// outbuf[0] = '-'; // Oct23 + outbuf[1] = '\0'; + return 1; + } + if(flags & ESC_BRACKETS){ + outbuf[end++] = '['; + } + *cells -= overhead; + end += escape_str(outbuf+end, pp->cmd, bytes-overhead, cells); + + // Hmmm, do we want "[foo] " or "[foo ]"? + if(flags & ESC_BRACKETS){ + outbuf[end++] = ']'; + } + if(flags & ESC_DEFUNCT){ + memcpy(outbuf+end, " ", 10); + end += 10; + } + outbuf[end] = '\0'; + return end; // bytes, not including the NUL +} diff --git a/proc/escape.h b/proc/escape.h new file mode 100644 index 0000000..172960f --- /dev/null +++ b/proc/escape.h @@ -0,0 +1,22 @@ +#ifndef PROCPS_PROC_ESCAPE_H +#define PROCPS_PROC_ESCAPE_H + +//#include +#include +#include "procps.h" +#include "readproc.h" + +EXTERN_C_BEGIN + +#define ESC_STRETCH 1 // since we mangle to '?' this is 1 (would be 4 for octal escapes) + +#define ESC_ARGS 0x1 // try to use cmdline instead of cmd +#define ESC_BRACKETS 0x2 // if using cmd, put '[' and ']' around it +#define ESC_DEFUNCT 0x4 // mark zombies with " " + +extern int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t n, int *cells); +extern int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells); +extern int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags); + +EXTERN_C_END +#endif diff --git a/proc/ksym.c b/proc/ksym.c new file mode 100644 index 0000000..2e5379f --- /dev/null +++ b/proc/ksym.c @@ -0,0 +1,631 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "procps.h" +#include "version.h" +#include "sysinfo.h" /* smp_num_cpus */ +#include "wchan.h" // to verify prototypes + +#define KSYMS_FILENAME "/proc/ksyms" + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/would/be/nice/to/have/this/file" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked" +#define linux_version_code 131598 /* ? */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.12" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12" +#define linux_version_code 131852 /* 2.3.12 */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS" +#define linux_version_code 131858 /* 2.3.18ac8 */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS" +#define linux_version_code 131858 /* 2.3.18ac8 */ +#define smp_num_cpus 2 +#endif + +/* These are the symbol types, with relative popularity: + * ? w machine type junk for Alpha -- odd syntax + * ? S not for i386 + * 4 W not for i386 + * 60 R + * 100 A + * 125 r + * 363 s not for i386 + * 858 B + * 905 g generated by modutils? + * 929 G generated by modutils? + * 1301 b + * 2750 D + * 4481 d + * 11417 ? + * 13666 t + * 15442 T + * + * For i386, that is: "RArBbDd?tT" + */ + +#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS" + +/* + * '?' is a symbol type + * '.' is part of a name (versioning?) + * "\t[]" are for the module name in /proc/ksyms + */ +#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +/* System.map lines look like: + * hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n + * + * Alpha systems can start with a few lines that have the address replaced + * by space padding and a 'w' for the type. For those lines, the last space + * is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv + * (just one of those, always with a "_mv", then the newline) + * + * The /proc/ksyms lines are like System.map lines w/o the symbol type char. + * When odd features are used, the name part contains: + * "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}" + * It is likely that more crap will be added... + */ + +typedef struct symb { + unsigned KLONG addr; + const char *name; +} symb; + +/* These mostly rely on POSIX to make them zero. */ + +static symb hashtable[256]; + +static char *sysmap_data; +static unsigned sysmap_room; +static symb *sysmap_index; +static unsigned sysmap_count; + +static char *ksyms_data; +static unsigned ksyms_room = 4096; +static symb *ksyms_index; +static unsigned ksyms_count; +static unsigned idx_room; + +/*********************************/ + +/* Kill this: _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$ + * We kill: (_R[^A-Z]*[0-9a-f]{8,})+$ + * + * The loop should almost never be taken, but it has to be there. + * It gets rid of anything that _looks_ like a version code, even + * if a real version code has already been found. This is because + * the inability to perfectly recognize a version code may lead to + * symbol mangling, which in turn leads to mismatches between the + * /proc/ksyms and System.map data files. + */ +#if 0 +static char *chop_version(char *arg){ + char *cp; + cp = strchr(arg,'\t'); + if(cp) *cp = '\0'; /* kill trailing module name first */ + for(;;){ + char *p; + int len = 0; + cp = strrchr(arg, 'R'); + if(!cp || cp<=arg+1 || cp[-1]!='_') break; + for(p=cp; *++p; ){ + switch(*p){ + default: + goto out; + case '0' ... '9': + case 'a' ... 'f': + len++; + continue; + case 'g' ... 'z': + case '_': + len=0; + continue; + } + } + if(len<8) break; + cp[-1] = '\0'; + } +out: + if(*arg=='G'){ + int len = strlen(arg); + while( len>8 && !memcmp(arg,"GPLONLY_",8) ){ + arg += 8; + len -= 8; + } + } + return arg; +} +#endif +static char *chop_version(char *arg){ + char *cp; + cp = strchr(arg,'\t'); + if(cp) *cp = '\0'; /* kill trailing module name first */ + for(;;){ + int len; + cp = strrchr(arg, 'R'); + if(!cp || cp<=arg+1 || cp[-1]!='_') break; + len=strlen(cp); + if(len<9) break; + if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break; + if(strspn(cp+len-8,"0123456789abcdef")!=8) break; + cp[-1] = '\0'; + } + if(*arg=='G'){ + int len = strlen(arg); + while( len>8 && !memcmp(arg,"GPLONLY_",8) ){ + arg += 8; + len -= 8; + } + } + return arg; +} + +/***********************************/ + +static const symb *search(unsigned KLONG address, symb *idx, unsigned count){ + unsigned left; + unsigned mid; + unsigned right; + if(!idx) return NULL; /* maybe not allocated */ + if(address < idx[0].addr) return NULL; + if(address >= idx[count-1].addr) return idx+count-1; + left = 0; + right = count-1; + for(;;){ + mid = (left + right) / 2; + if(address >= idx[mid].addr) left = mid; + if(address <= idx[mid].addr) right = mid; + if(right-left <= 1) break; + } + if(address == idx[right].addr) return idx+right; + return idx+left; +} + +/*********************************/ + +/* allocate if needed, read, and return buffer size */ +static void read_file(const char *restrict filename, char **bufp, unsigned *restrict roomp) { + int fd = 0; + ssize_t done; + char *buf = *bufp; + ssize_t total = 0; + unsigned room = *roomp; + + if(!room) goto hell; /* failed before */ + if(!buf) buf = malloc(room); + if(!buf) goto hell; +open_again: + fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0){ + switch(errno){ + case EINTR: goto open_again; + default: _exit(101); + case EACCES: /* somebody screwing around? */ + /* FIXME: set a flag to disable symbol lookup? */ + case ENOENT:; /* no module support */ + } + goto hell; + } + for(;;){ + done = read(fd, buf+total, room-total-1); + if(done==0) break; /* nothing left */ + if(done==-1){ + if(errno==EINTR) continue; /* try again */ + perror(""); + goto hell; + } + if(done==(ssize_t)room-total-1){ + char *tmp; + total += done; + /* more to go, but no room in buffer */ + room *= 2; + tmp = realloc(buf, room); + if(!tmp) goto hell; + buf = tmp; + continue; + } + if(done>0 && done<(ssize_t)room-total-1){ + total += done; + continue; /* OK, we read some. Go do more. */ + } + fprintf(stderr,"%ld can't happen\n", (long)done); + /* FIXME: memory leak */ + _exit(42); + } + buf[total] = '\0'; // parse_ksyms() expects NUL-terminated file + *bufp = buf; + *roomp = room; + close(fd); + return; +hell: + if(buf) free(buf); + *bufp = NULL; + *roomp = 0; /* this function will never work again */ + total = 0; + if(fd>0) close(fd); + return; +} + +/*********************************/ + +static int parse_ksyms(void) { + char *endp; + if(!ksyms_room || !ksyms_data) goto quiet_goodbye; + endp = ksyms_data; + ksyms_count = 0; + if(idx_room) goto bypass; /* some space already allocated */ + idx_room = 512; + for(;;){ + void *vp; + idx_room *= 2; + vp = realloc(ksyms_index, sizeof(symb)*idx_room); + if(!vp) goto bad_alloc; + ksyms_index = vp; +bypass: + for(;;){ + char *saved; + if(!*endp) return 1; + saved = endp; + ksyms_index[ksyms_count].addr = STRTOUKL(endp, &endp, 16); + if(endp==saved || *endp != ' ') goto bad_parse; + endp++; + saved = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + ksyms_index[ksyms_count].name = chop_version(saved); + ++endp; + if(++ksyms_count >= idx_room) break; /* need more space */ + } + } + + if(0){ +bad_alloc: + fprintf(stderr, "Warning: not enough memory available\n"); + } + if(0){ +bad_parse: + fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n"); + } +quiet_goodbye: + idx_room = 0; + if(ksyms_data) free(ksyms_data) , ksyms_data = NULL; + ksyms_room = 0; + if(ksyms_index) free(ksyms_index) , ksyms_index = NULL; + ksyms_count = 0; + return 0; +} + +/*********************************/ + +#define VCNT 16 + +static int sysmap_mmap(const char *restrict const filename, message_fn message) { + struct stat sbuf; + char *endp; + int fd; + char Version[32]; + fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0) return 0; + if(fstat(fd, &sbuf) < 0) goto bad_open; + if(!S_ISREG(sbuf.st_mode)) goto bad_open; + if(sbuf.st_size < 5000) goto bad_open; /* if way too small */ + /* Would be shared read-only, but we want '\0' after each name. */ + endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + sysmap_data = endp; + while(*endp==' '){ /* damn Alpha machine types */ + if(strncmp(endp," w ", 19)) goto bad_parse; + endp += 19; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse; + endp++; + } + if(sysmap_data == (caddr_t) -1) goto bad_open; + close(fd); + fd = -1; + sprintf(Version, "Version_%d", linux_version_code); + sysmap_room = 512; + for(;;){ + void *vp; + sysmap_room *= 2; + vp = realloc(sysmap_index, sizeof(symb)*sysmap_room); + if(!vp) goto bad_alloc; + sysmap_index = vp; + for(;;){ + char *vstart; + if(endp - sysmap_data >= sbuf.st_size){ /* if we reached the end */ + int i = VCNT; /* check VCNT times to verify this file */ + if(*Version) goto bad_version; + if(!ksyms_index) return 1; /* if can not verify, assume success */ + while(i--){ +#if 1 + const symb *findme; + const symb *map_symb; + /* Choose VCNT entries from /proc/ksyms to test */ + findme = ksyms_index + (ksyms_count*i/VCNT); + /* Search for them in the System.map */ + map_symb = search(findme->addr, sysmap_index, sysmap_count); + if(map_symb){ + if(map_symb->addr != findme->addr) continue; + /* backup to first matching address */ + while (map_symb != sysmap_index){ + if (map_symb->addr != (map_symb-1)->addr) break; + map_symb--; + } + /* search for name in symbols with same address */ + while (map_symb != (sysmap_index+sysmap_count)){ + if (map_symb->addr != findme->addr) break; + if (!strcmp(map_symb->name,findme->name)) goto good_match; + map_symb++; + } + map_symb--; /* backup to last symbol with matching address */ + message("{%s} {%s}\n",map_symb->name,findme->name); + goto bad_match; + } +good_match:; +#endif + } + return 1; /* success */ + } + sysmap_index[sysmap_count].addr = STRTOUKL(endp, &endp, 16); + if(*endp != ' ') goto bad_parse; + endp++; + if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse; + endp++; + if(*endp != ' ') goto bad_parse; + endp++; + vstart = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + ++endp; + vstart = chop_version(vstart); + sysmap_index[sysmap_count].name = vstart; + if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0'; + if(++sysmap_count >= sysmap_room) break; /* need more space */ + } + } + + if(0){ +bad_match: + message("Warning: %s does not match kernel data.\n", filename); + } + if(0){ +bad_version: + message("Warning: %s has an incorrect kernel version.\n", filename); + } + if(0){ +bad_alloc: + message("Warning: not enough memory available\n"); + } + if(0){ +bad_parse: + message("Warning: %s not parseable as a System.map\n", filename); + } + if(0){ +bad_open: + message("Warning: %s could not be opened as a System.map\n", filename); + } + + sysmap_room=0; + sysmap_count=0; + if(sysmap_index) free(sysmap_index); + sysmap_index = NULL; + if(fd>=0) close(fd); + if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1); + sysmap_data = NULL; + return 0; +} + +/*********************************/ + +static void read_and_parse(void){ + static time_t stamp; /* after data gets old, load /proc/ksyms again */ + if(time(NULL) != stamp){ + read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room); + parse_ksyms(); + memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */ + stamp = time(NULL); + } +} + +/*********************************/ + +static void default_message(const char *restrict format, ...) __attribute__((format(printf,1,2))); +static void default_message(const char *restrict format, ...) { + va_list arg; + + va_start (arg, format); + vfprintf (stderr, format, arg); + va_end (arg); +} + +/*********************************/ + +static int use_wchan_file; + +int open_psdb_message(const char *restrict override, message_fn message) { + static const char *sysmap_paths[] = { + "/boot/System.map-%s", + "/boot/System.map", + "/lib/modules/%s/System.map", + "/usr/src/linux/System.map", + "/System.map", + NULL + }; + struct stat sbuf; + struct utsname uts; + char path[128]; + const char **fmt = sysmap_paths; + const char *sm; + +#ifdef SYSMAP_FILENAME /* debug feature */ + override = SYSMAP_FILENAME; +#endif + + // first allow for a user-selected System.map file + if( + (sm=override) + || + (sm=getenv("PS_SYSMAP")) + || + (sm=getenv("PS_SYSTEM_MAP")) + ){ + if(!have_privs){ + read_and_parse(); + if(sysmap_mmap(sm, message)) return 0; + } + /* failure is better than ignoring the user & using bad data */ + return -1; /* ought to return "Namelist not found." */ + } + + // next try the Linux 2.5.xx method + if(!stat("/proc/self/wchan", &sbuf)){ + use_wchan_file = 1; // hack + return 0; + } + + // finally, search for the System.map file + uname(&uts); + path[sizeof path - 1] = '\0'; + do{ + int did_ksyms = 0; + snprintf(path, sizeof path - 1, *fmt, uts.release); + if(!stat(path, &sbuf)){ + if (did_ksyms++) read_and_parse(); + if (sysmap_mmap(path, message)) return 0; + } + }while(*++fmt); + /* TODO: Without System.map, no need to keep ksyms loaded. */ + return -1; +} + +/***************************************/ + +int open_psdb(const char *restrict override) { + return open_psdb_message(override, default_message); +} + +/***************************************/ + +static const char * read_wchan_file(unsigned pid){ + static char buf[64]; + const char *ret = buf; + ssize_t num; + int fd; + + snprintf(buf, sizeof buf, "/proc/%d/wchan", pid); + fd = open(buf, O_RDONLY); + if(fd==-1) return "?"; + num = read(fd, buf, sizeof buf - 1); + close(fd); + if(num<1) return "?"; // allow for "0" + buf[num] = '\0'; + + if(buf[0]=='0' && buf[1]=='\0') return "-"; + + // would skip over numbers if they existed -- but no + + // lame ppc64 has a '.' in front of every name + if(*ret=='.') ret++; + switch(*ret){ + case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; + case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; + case '_': while(*ret=='_') ret++; break; + } + return ret; +} + +/***************************************/ + +static const symb fail = { .name = "?" }; +static const char dash[] = "-"; +static const char star[] = "*"; + +#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */ + +/* return pointer to temporary static buffer with function name */ +const char * lookup_wchan(unsigned KLONG address, unsigned pid) { + const symb *mod_symb; + const symb *map_symb; + const symb *good_symb; + const char *ret; + unsigned hash; + + // can't cache it due to a race condition :-( + if(use_wchan_file) return read_wchan_file(pid); + + if(!address) return dash; + if(!~address) return star; + + read_and_parse(); + hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */ + if(hashtable[hash].addr == address) return hashtable[hash].name; + mod_symb = search(address, ksyms_index, ksyms_count); + if(!mod_symb) mod_symb = &fail; + map_symb = search(address, sysmap_index, sysmap_count); + if(!map_symb) map_symb = &fail; + + /* which result is closest? */ + good_symb = (mod_symb->addr > map_symb->addr) + ? mod_symb + : map_symb + ; + if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail; + + /* good_symb->name has the data, but needs to be trimmed */ + ret = good_symb->name; + // lame ppc64 has a '.' in front of every name + if(*ret=='.') ret++; + switch(*ret){ + case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; + case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; + case '_': while(*ret=='_') ret++; break; + } + /* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */ + + /* cache name after abbreviation */ + hashtable[hash].addr = address; + hashtable[hash].name = ret; + + return ret; +} diff --git a/proc/library.map b/proc/library.map new file mode 100644 index 0000000..a38627b --- /dev/null +++ b/proc/library.map @@ -0,0 +1,24 @@ +# for --version-script +# WTF is the syntax for this file? +# Give me a BNF, man! +_3_2_5 { +global: + __cyg_profile_func_enter; __cyg_profile_func_exit; main; + + readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command; + escape_str; escape_strlist; + openproc; closeproc; + tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan; + display_version; procps_version; linux_version_code; + Hertz; smp_num_cpus; have_privs; + sprint_uptime; uptime; user_from_uid; print_uptime; loadavg; + pretty_print_signals; print_given_signals; unix_print_signals; signal_name_to_number; signal_number_to_name; + meminfo; vminfo; getstat; getdiskstat; getpartitions_num; getslabinfo; get_pid_digits; + kb_active; kb_inactive; kb_main_buffers; kb_main_cached; + kb_main_free; kb_main_total; kb_main_used; kb_swap_free; + kb_swap_total; kb_swap_used; kb_main_shared; + kb_low_total; kb_low_free; kb_high_total; kb_high_free; + vm_pgpgin; vm_pgpgout; vm_pswpin; vm_pswpout; + free_slabinfo; put_slabinfo; get_slabinfo; get_proc_stats; +local: *; +}; diff --git a/proc/module.mk b/proc/module.mk new file mode 100644 index 0000000..af20334 --- /dev/null +++ b/proc/module.mk @@ -0,0 +1,130 @@ +# This file gets included into the main Makefile, in the top directory. + +# Ideally, we want something like this: +# +# /lib/libproc.so.w ELF soname ('w' is a digit, starting from 1) +# /lib/procps-x.y.z.so file itself (x.y.z is the procps version) +# /lib/libproc.so for linking, UNSUPPORTED +# /usr/lib/libproc.a for linking, UNSUPPORTED +# proc/libproc.so.w as above, if testing with LD_LIBRARY_PATH +# proc/whatever if testing with LD_PRELOAD +# proc/libproc.a for static build +# +# Without a stable ABI, there's no point in having any of that. +# Without a stable API, there's no point in having the *.a file. +# +# A new ELF soname is required for every big ABI change. To conserve +# numbers for future use, the ELF soname can be set equal to the +# file name until some future date when a stable ABI is declared. + +SHARED := 1 + +# for lib$(NAME).so and /usr/include/($NAME) and such +NAME := proc + +LIBVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION) +ABIVERSION := 0 + +SOFILE := lib$(NAME)-$(LIBVERSION).so +ifneq ($(ABIVERSION),0) +SOLINK := lib$(NAME).so +SONAME := lib$(NAME).so.$(ABIVERSION) +else +SONAME := $(SOFILE) +SOLINK := $(SOFILE) +endif + +ANAME := lib$(NAME).a + +############ + +FPIC := -fpic + +ifeq ($(SHARED),1) +ALL += proc/$(SONAME) +INSTALL += ldconfig +LIBFLAGS := -DSHARED=1 $(FPIC) +# This is in gcc 3.5, but exported functions must be marked. +#LIBFLAGS += $(call check_gcc,-fvisibility=hidden,) +LIBPROC := proc/$(SONAME) +else +ALL += proc/$(ANAME) +#INSTALL += $(usr/lib)$(ANAME) +LIBFLAGS := -DSHARED=0 +LIBPROC := proc/$(ANAME) +endif + +LIBSRC := $(wildcard proc/*.c) +LIBHDR := $(wildcard proc/*.h) +LIBOBJ := $(LIBSRC:.c=.o) + +# Separate rule for this directory, to use -fpic or -fPIC +$(filter-out proc/version.o,$(LIBOBJ)): proc/%.o: proc/%.c + $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) $< -o $@ + +LIB_X := COPYING module.mk library.map +TARFILES += $(LIBSRC) $(LIBHDR) $(addprefix proc/,$(LIB_X)) + + +# Clean away all output files, .depend, and symlinks. +# Use wildcards in case the version has changed. +CLEAN += proc/.depend proc/lib*.so* proc/lib*.a $(LIBOBJ) +DIRS += proc/ + +proc/$(ANAME): $(LIBOBJ) + $(AR) rcs $@ $^ + +#proc/$(SONAME): proc/library.map +proc/$(SONAME): $(LIBOBJ) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -shared -Wl,-soname,$(SONAME) -Wl,--version-script=proc/library.map -o $@ $^ -lc + + +# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT +proc/.depend: $(LIBSRC) $(LIBHDR) + $(strip $(CC) $(ALL_CPPFLAGS) $(LIB_CFLAGS) -MM -MG $(LIBSRC) > $@) + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),tar) +ifneq ($(MAKECMDGOALS),extratar) +ifneq ($(MAKECMDGOALS),beta) +-include proc/.depend +endif +endif +endif +endif + +#################### install rules ########################### + +$(lib)$(SOFILE) : proc/$(SONAME) + $(install) --mode a=rx $< $@ + +ifneq ($(SOLINK),$(SOFILE)) +.PHONY: $(lib)$(SOLINK) +$(lib)$(SOLINK) : $(lib)$(SOFILE) + cd $(lib) && $(ln_sf) $(SOFILE) $(SOLINK) +endif + +ifneq ($(SONAME),$(SOFILE)) +.PHONY: $(lib)$(SONAME) +$(lib)$(SONAME) : $(lib)$(SOFILE) + cd $(lib) && $(ln_sf) $(SOFILE) $(SONAME) +endif + +.PHONY: ldconfig +ldconfig : $(lib)$(SONAME) $(lib)$(SOLINK) + $(ldconfig) + +$(usr/lib)$(ANAME) : proc/$(ANAME) + $(install) --mode a=r $< $@ + +# Junk anyway... supposed to go in /usr/include/$(NAME) +#INSTALL += $(addprefix $(include),$(HDRFILES)) +# +#$(addprefix $(include),$(HDRFILES)): $(include)% : proc/% +#$(include)% : proc/% +# $(install) --mode a=r $< $@ + +################################################################## + +proc/version.o: proc/version.c proc/version.h + $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c -o $@ $< diff --git a/proc/procps.h b/proc/procps.h new file mode 100644 index 0000000..a70e925 --- /dev/null +++ b/proc/procps.h @@ -0,0 +1,112 @@ +#ifndef PROCPS_PROC_PROCPS_H +#define PROCPS_PROC_PROCPS_H + +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif + +// Some ports make the mistake of running a 32-bit userspace +// on a 64-bit kernel. Shame on them. It's not at all OK to +// make everything "long long", since that causes unneeded +// slowness on 32-bit hardware. +// +// SPARC: The 32-bit kernel was looking like an ex-penguin, +// but it lives! ("I'm not dead yet.") So, 64-bit users will +// just have to compile for 64-bit. Aw, the suffering. +// +// MIPS: Used 32-bit for embedded systems and obsolete hardware. +// The 64-bit systems use an n32 format executable, defining +// _ABIN32 to indicate this. Since n32 doesn't currently run on +// any 32-bit system, nobody get hurt if it's bloated. Not that +// this is sane of course, but it won't hurt the 32-bit users. +// __mips_eabi means eabi, which comes in both sizes, but isn't used. +// +// PowerPC: Big ugly problem! 32-bit Macs are still popular. :-/ +// +// x86-64: So far, nobody has been dumb enough to go 32-bit. +// +// Unknown: PA-RISC and zSeries +// +#if defined(k64test) || (defined(_ABIN32) && _MIPS_SIM == _ABIN32) +#define KLONG long long // not typedef; want "unsigned KLONG" to work +#define KLF "L" +#define STRTOUKL strtoull +#else +#define KLONG long +#define KLF "l" +#define STRTOUKL strtoul +#endif + +// since gcc-2.5 +#define NORETURN __attribute__((__noreturn__)) +#define FUNCTION __attribute__((__const__)) // no access to global mem, even via ptr, and no side effect + +#if !defined(restrict) && __STDC_VERSION__ < 199901 +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 92 +#define restrict __restrict__ +#else +#warning No restrict keyword? +#define restrict +#endif +#endif + +#if __GNUC__ > 2 || __GNUC_MINOR__ >= 96 +// won't alias anything, and aligned enough for anything +#define MALLOC __attribute__ ((__malloc__)) +// no side effect, may read globals +#define PURE __attribute__ ((__pure__)) +// tell gcc what to expect: if(unlikely(err)) die(err); +#define likely(x) __builtin_expect(!!(x),1) +#define unlikely(x) __builtin_expect(!!(x),0) +#define expected(x,y) __builtin_expect((x),(y)) +#else +#define MALLOC +#define PURE +#define likely(x) (x) +#define unlikely(x) (x) +#define expected(x,y) (x) +#endif + +#if SHARED==1 && (__GNUC__ > 2 || __GNUC_MINOR__ >= 96) +#define LABEL_OFFSET +#endif + +#define STRINGIFY_ARG(a) #a +#define STRINGIFY(a) STRINGIFY_ARG(a) + +// marks old junk, to warn non-procps library users +#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3 +#define OBSOLETE __attribute__((deprecated)) +#else +#define OBSOLETE +#endif + +#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 1 ) || __GNUC__ > 3 +// Tells gcc that function is library-internal; +// so no need to do dynamic linking at run-time. +// This might work with slightly older compilers too. +#define HIDDEN __attribute__((visibility("hidden"))) +// The opposite, in case -fvisibility=hidden used +#define EXPORT __attribute__((visibility("default"))) +// Tell g++ that a function won't throw exceptions. +#define NOTHROW __attribute__((__nothrow__)) +#else +#define HIDDEN +#define EXPORT +#define NOTHROW +#endif + +// Like HIDDEN, but for an alias that gets created. +// In gcc-3.2 there is an alias+hidden conflict. +// Many will have patched this bug, but oh well. +#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 2 ) || __GNUC__ > 3 +#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x),visibility("hidden"))) +#else +#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x))) +#endif + +#endif diff --git a/proc/pwcache.c b/proc/pwcache.c new file mode 100644 index 0000000..ab7e528 --- /dev/null +++ b/proc/pwcache.c @@ -0,0 +1,77 @@ +// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com +// Note: most likely none of his code remains +// +// Copyright 2002, Albert Cahalan +// +// This file is placed under the conditions of the GNU Library +// General Public License, version 2, or any later version. +// See file COPYING for information on distribution conditions. + +#include +#include +#include +#include +#include +#include "alloc.h" +#include "pwcache.h" +#include + +// might as well fill cache lines... else we waste memory anyway + +#define HASHSIZE 64 /* power of 2 */ +#define HASH(x) ((x) & (HASHSIZE - 1)) + +static struct pwbuf { + struct pwbuf *next; + uid_t uid; + char name[P_G_SZ]; +} *pwhash[HASHSIZE]; + +char *user_from_uid(uid_t uid) { + struct pwbuf **p; + struct passwd *pw; + + p = &pwhash[HASH(uid)]; + while (*p) { + if ((*p)->uid == uid) + return((*p)->name); + p = &(*p)->next; + } + *p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf)); + (*p)->uid = uid; + pw = getpwuid(uid); + if(!pw || strlen(pw->pw_name) >= P_G_SZ) + sprintf((*p)->name, "%u", uid); + else + strcpy((*p)->name, pw->pw_name); + + (*p)->next = NULL; + return((*p)->name); +} + +static struct grpbuf { + struct grpbuf *next; + gid_t gid; + char name[P_G_SZ]; +} *grphash[HASHSIZE]; + +char *group_from_gid(gid_t gid) { + struct grpbuf **g; + struct group *gr; + + g = &grphash[HASH(gid)]; + while (*g) { + if ((*g)->gid == gid) + return((*g)->name); + g = &(*g)->next; + } + *g = (struct grpbuf *) malloc(sizeof(struct grpbuf)); + (*g)->gid = gid; + gr = getgrgid(gid); + if (!gr || strlen(gr->gr_name) >= P_G_SZ) + sprintf((*g)->name, "%u", gid); + else + strcpy((*g)->name, gr->gr_name); + (*g)->next = NULL; + return((*g)->name); +} diff --git a/proc/pwcache.h b/proc/pwcache.h new file mode 100644 index 0000000..678554d --- /dev/null +++ b/proc/pwcache.h @@ -0,0 +1,17 @@ +#ifndef PROCPS_PROC_PWCACHE_H +#define PROCPS_PROC_PWCACHE_H + +#include +#include "procps.h" + +EXTERN_C_BEGIN + +// used in pwcache and in readproc to set size of username or groupname +#define P_G_SZ 20 + +extern char *user_from_uid(uid_t uid); +extern char *group_from_gid(gid_t gid); + +EXTERN_C_END + +#endif diff --git a/proc/readproc.c b/proc/readproc.c new file mode 100644 index 0000000..4fad11d --- /dev/null +++ b/proc/readproc.c @@ -0,0 +1,1054 @@ +/* + * New Interface to Process Table -- PROCTAB Stream (a la Directory streams) + * Copyright (C) 1996 Charles L. Blake. + * Copyright (C) 1998 Michael K. Johnson + * Copyright 1998-2003 Albert Cahalan + * May be distributed under the conditions of the + * GNU Library General Public License; a copy is in COPYING + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "version.h" +#include "readproc.h" +#include "alloc.h" +#include "pwcache.h" +#include "devname.h" +#include "procps.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// sometimes it's easier to do this manually, w/o gcc helping +#ifdef PROF +extern void __cyg_profile_func_enter(void*,void*); +#define ENTER(x) __cyg_profile_func_enter((void*)x,(void*)x) +#define LEAVE(x) __cyg_profile_func_exit((void*)x,(void*)x) +#else +#define ENTER(x) +#define LEAVE(x) +#endif + +// convert hex string to unsigned long long +static unsigned long long unhex(const char *restrict cp){ + unsigned long long ull = 0; + for(;;){ + char c = *cp++; + if(unlikely(c<0x30)) break; + ull = (ull<<4) | (c - (c>0x57) ? 0x57 : 0x30) ; + } + return ull; +} + +static int task_dir_missing; + +/////////////////////////////////////////////////////////////////////////// + +typedef struct status_table_struct { + unsigned char name[7]; // /proc/*/status field name + unsigned char len; // name length +#ifdef LABEL_OFFSET + long offset; // jump address offset +#else + void *addr; +#endif +} status_table_struct; + +#ifdef LABEL_OFFSET +#define F(x) {#x, sizeof(#x)-1, (long)(&&case_##x-&&base)}, +#else +#define F(x) {#x, sizeof(#x)-1, &&case_##x}, +#endif +#define NUL {"", 0, 0}, + +// Derived from: +// gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf +// +// Suggested method: +// Grep this file for "case_", then strip those down to the name. +// (leave the colon and newline) So "Pid:\n" and "Threads:\n" +// would be lines in the file. (no quote, no escape, etc.) +// +// Watch out for name size in the status_table_struct (grrr, expanding) +// and the number of entries (we mask with 63 for now). The table +// must be padded out to 64 entries, maybe 128 in the future. + +static void status2proc(char *S, proc_t *restrict P, int is_proc){ + long Threads = 0; + long Tgid = 0; + long Pid = 0; + + static const unsigned char asso[] = + { + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 15, 61, + 61, 61, 61, 61, 61, 61, 30, 3, 5, 5, + 61, 5, 61, 8, 61, 61, 3, 61, 10, 61, + 6, 61, 13, 0, 30, 25, 0, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 3, 61, 13, + 0, 0, 61, 30, 61, 25, 61, 61, 61, 0, + 61, 61, 61, 61, 5, 61, 0, 61, 61, 61, + 0, 61, 61, 61, 61, 61, 61, 61 + }; + + static const status_table_struct table[] = { + F(VmStk) + NUL NUL + F(State) + NUL + F(VmExe) + F(ShdPnd) + NUL + F(VmData) + NUL + F(Name) + NUL NUL + F(VmRSS) + NUL NUL + F(VmLck) + NUL NUL NUL + F(Gid) + F(Pid) + NUL NUL NUL + F(VmSize) + NUL NUL + F(VmLib) + NUL NUL + F(PPid) + NUL + F(SigCgt) + NUL + F(Threads) + F(SigPnd) + NUL + F(SigIgn) + NUL + F(Uid) + NUL NUL NUL NUL NUL NUL NUL NUL NUL + NUL NUL NUL NUL NUL + F(Tgid) + NUL NUL NUL NUL + F(SigBlk) + NUL NUL NUL + }; + +#undef F +#undef NUL + +ENTER(0x220); + + P->vm_size = 0; + P->vm_lock = 0; + P->vm_rss = 0; + P->vm_data = 0; + P->vm_stack= 0; + P->vm_exe = 0; + P->vm_lib = 0; + P->nlwp = 0; + P->signal[0] = '\0'; // so we can detect it as missing for very old kernels + + goto base; + + for(;;){ + char *colon; + status_table_struct entry; + + // advance to next line + S = strchr(S, '\n'); + if(unlikely(!S)) break; // if no newline + S++; + + // examine a field name (hash and compare) + base: + if(unlikely(!*S)) break; + entry = table[63 & (asso[S[3]] + asso[S[2]] + asso[S[0]])]; + colon = strchr(S, ':'); + if(unlikely(!colon)) break; + if(unlikely(colon[1]!='\t')) break; + if(unlikely(colon-S != entry.len)) continue; + if(unlikely(memcmp(entry.name,S,colon-S))) continue; + + S = colon+2; // past the '\t' + +#ifdef LABEL_OFFSET + goto *(&&base + entry.offset); +#else + goto *entry.addr; +#endif + + case_Name:{ + unsigned u = 0; + while(u < sizeof P->cmd - 1u){ + int c = *S++; + if(unlikely(c=='\n')) break; + if(unlikely(c=='\0')) break; // should never happen + if(unlikely(c=='\\')){ + c = *S++; + if(c=='\n') break; // should never happen + if(!c) break; // should never happen + if(c=='n') c='\n'; // else we assume it is '\\' + } + P->cmd[u++] = c; + } + P->cmd[u] = '\0'; + S--; // put back the '\n' or '\0' + continue; + } +#ifdef SIGNAL_STRING + case_ShdPnd: + memcpy(P->signal, S, 16); + P->signal[16] = '\0'; + continue; + case_SigBlk: + memcpy(P->blocked, S, 16); + P->blocked[16] = '\0'; + continue; + case_SigCgt: + memcpy(P->sigcatch, S, 16); + P->sigcatch[16] = '\0'; + continue; + case_SigIgn: + memcpy(P->sigignore, S, 16); + P->sigignore[16] = '\0'; + continue; + case_SigPnd: + memcpy(P->_sigpnd, S, 16); + P->_sigpnd[16] = '\0'; + continue; +#else + case_ShdPnd: + P->signal = unhex(S); + continue; + case_SigBlk: + P->blocked = unhex(S); + continue; + case_SigCgt: + P->sigcatch = unhex(S); + continue; + case_SigIgn: + P->sigignore = unhex(S); + continue; + case_SigPnd: + P->_sigpnd = unhex(S); + continue; +#endif + case_State: + P->state = *S; + continue; + case_Tgid: + Tgid = strtol(S,&S,10); + continue; + case_Pid: + Pid = strtol(S,&S,10); + continue; + case_PPid: + P->ppid = strtol(S,&S,10); + continue; + case_Threads: + Threads = strtol(S,&S,10); + continue; + case_Uid: + P->ruid = strtol(S,&S,10); + P->euid = strtol(S,&S,10); + P->suid = strtol(S,&S,10); + P->fuid = strtol(S,&S,10); + continue; + case_Gid: + P->rgid = strtol(S,&S,10); + P->egid = strtol(S,&S,10); + P->sgid = strtol(S,&S,10); + P->fgid = strtol(S,&S,10); + continue; + case_VmData: + P->vm_data = strtol(S,&S,10); + continue; + case_VmExe: + P->vm_exe = strtol(S,&S,10); + continue; + case_VmLck: + P->vm_lock = strtol(S,&S,10); + continue; + case_VmLib: + P->vm_lib = strtol(S,&S,10); + continue; + case_VmRSS: + P->vm_rss = strtol(S,&S,10); + continue; + case_VmSize: + P->vm_size = strtol(S,&S,10); + continue; + case_VmStk: + P->vm_stack = strtol(S,&S,10); + continue; + } + +#if 0 + // recent kernels supply per-tgid pending signals + if(is_proc && *ShdPnd){ + memcpy(P->signal, ShdPnd, 16); + P->signal[16] = '\0'; + } +#endif + + // recent kernels supply per-tgid pending signals +#ifdef SIGNAL_STRING + if(!is_proc || !P->signal[0]){ + memcpy(P->signal, P->_sigpnd, 16); + P->signal[16] = '\0'; + } +#else + if(!is_proc || !have_process_pending){ + P->signal = P->_sigpnd; + } +#endif + + // Linux 2.4.13-pre1 to max 2.4.xx have a useless "Tgid" + // that is not initialized for built-in kernel tasks. + // Only 2.6.0 and above have "Threads" (nlwp) info. + + if(Threads){ + P->nlwp = Threads; + P->tgid = Tgid; // the POSIX PID value + P->tid = Pid; // the thread ID + }else{ + P->nlwp = 1; + P->tgid = Pid; + P->tid = Pid; + } + +LEAVE(0x220); +} + +/////////////////////////////////////////////////////////////////////// + +// Reads /proc/*/stat files, being careful not to trip over processes with +// names like ":-) 1 2 3 4 5 6". +static void stat2proc(const char* S, proc_t *restrict P) { + unsigned num; + char* tmp; + +ENTER(0x160); + + /* fill in default values for older kernels */ + P->processor = 0; + P->rtprio = -1; + P->sched = -1; + P->nlwp = 0; + + S = strchr(S, '(') + 1; + tmp = strrchr(S, ')'); + num = tmp - S; + if(unlikely(num >= sizeof P->cmd)) num = sizeof P->cmd - 1; + memcpy(P->cmd, S, num); + P->cmd[num] = '\0'; + S = tmp + 2; // skip ") " + + num = sscanf(S, + "%c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu " + "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */ + "%ld %ld " + "%d " + "%ld " + "%Lu " /* start_time */ + "%lu " + "%ld " + "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u " + "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ + "%"KLF"u %*lu %*lu " + "%d %d " + "%lu %lu", + &P->state, + &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid, + &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, + &P->utime, &P->stime, &P->cutime, &P->cstime, + &P->priority, &P->nice, + &P->nlwp, + &P->alarm, + &P->start_time, + &P->vsize, + &P->rss, + &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip, +/* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */ + &P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */ +/* -- Linux 2.0.35 ends here -- */ + &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */ +/* -- Linux 2.2.8 to 2.5.17 end here -- */ + &P->rtprio, &P->sched /* both added to 2.5.18 */ + ); + + if(!P->nlwp){ + P->nlwp = 1; + } + +LEAVE(0x160); +} + +///////////////////////////////////////////////////////////////////////// + +static void statm2proc(const char* s, proc_t *restrict P) { + int num; + num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld", + &P->size, &P->resident, &P->share, + &P->trs, &P->lrs, &P->drs, &P->dt); +/* fprintf(stderr, "statm2proc converted %d fields.\n",num); */ +} + +static int file2str(const char *directory, const char *what, char *ret, int cap) { + static char filename[80]; + int fd, num_read; + + sprintf(filename, "%s/%s", directory, what); + fd = open(filename, O_RDONLY, 0); + if(unlikely(fd==-1)) return -1; + num_read = read(fd, ret, cap - 1); + close(fd); + if(unlikely(num_read<=0)) return -1; + ret[num_read] = '\0'; + return num_read; +} + +static char** file2strvec(const char* directory, const char* what) { + char buf[2048]; /* read buf bytes at a time */ + char *p, *rbuf = 0, *endbuf, **q, **ret; + int fd, tot = 0, n, c, end_of_file = 0; + int align; + + sprintf(buf, "%s/%s", directory, what); + fd = open(buf, O_RDONLY, 0); + if(fd==-1) return NULL; + + /* read whole file into a memory buffer, allocating as we go */ + while ((n = read(fd, buf, sizeof buf - 1)) > 0) { + if (n < (int)(sizeof buf - 1)) + end_of_file = 1; + if (n == 0 && rbuf == 0) + return NULL; /* process died between our open and read */ + if (n < 0) { + if (rbuf) + free(rbuf); + return NULL; /* read error */ + } + if (end_of_file && buf[n-1]) /* last read char not null */ + buf[n++] = '\0'; /* so append null-terminator */ + rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */ + memcpy(rbuf + tot, buf, n); /* copy buffer into it */ + tot += n; /* increment total byte ctr */ + if (end_of_file) + break; + } + close(fd); + if (n <= 0 && !end_of_file) { + if (rbuf) free(rbuf); + return NULL; /* read error */ + } + endbuf = rbuf + tot; /* count space for pointers */ + align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1)); + for (c = 0, p = rbuf; p < endbuf; p++) + if (!*p) + c += sizeof(char*); + c += sizeof(char*); /* one extra for NULL term */ + + rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */ + endbuf = rbuf + tot; /* addr just past data buf */ + q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */ + *q++ = p = rbuf; /* point ptrs to the strings */ + endbuf--; /* do not traverse final NUL */ + while (++p < endbuf) + if (!*p) /* NUL char implies that */ + *q++ = p+1; /* next string -> next char */ + + *q = 0; /* null ptr list terminator */ + return ret; +} + +// warning: interface may change +int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){ + char name[32]; + int fd; + unsigned n = 0; + dst[0] = '\0'; + snprintf(name, sizeof name, "/proc/%u/cmdline", pid); + fd = open(name, O_RDONLY); + if(fd==-1) return 0; + for(;;){ + ssize_t r = read(fd,dst+n,sz-n); + if(r==-1){ + if(errno==EINTR) continue; + break; + } + n += r; + if(n==sz) break; // filled the buffer + if(r==0) break; // EOF + } + close(fd); + if(n){ + int i; + if(n==sz) n--; + dst[n] = '\0'; + i=n; + while(i--){ + int c = dst[i]; + if(c<' ' || c>'~') dst[i]=' '; + } + } + return n; +} + +/* These are some nice GNU C expression subscope "inline" functions. + * The can be used with arbitrary types and evaluate their arguments + * exactly once. + */ + +/* Test if item X of type T is present in the 0 terminated list L */ +# define XinL(T, X, L) ( { \ + T x = (X), *l = (L); \ + while (*l && *l != x) l++; \ + *l == x; \ + } ) + +/* Test if item X of type T is present in the list L of length N */ +# define XinLN(T, X, L, N) ( { \ + T x = (X), *l = (L); \ + int i = 0, n = (N); \ + while (i < n && l[i] != x) i++; \ + i < n && l[i] == x; \ + } ) + +////////////////////////////////////////////////////////////////////////////////// +// This reads process info from /proc in the traditional way, for one process. +// The pid (tgid? tid?) is already in p, and a path to it in path, with some +// room to spare. +static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) { + static struct stat sb; // stat() buffer + static char sbuf[1024]; // buffer for stat,statm + char *restrict const path = PT->path; + unsigned flags = PT->flags; + + if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ + goto next_proc; + + if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) + goto next_proc; /* not one of the requested uids */ + + p->euid = sb.st_uid; /* need a way to get real uid */ + p->egid = sb.st_gid; /* need a way to get real gid */ + + if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */ + if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 )) + goto next_proc; /* error reading /proc/#/stat */ + stat2proc(sbuf, p); /* parse /proc/#/stat */ + } + + if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */ + if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 )) + statm2proc(sbuf, p); /* ignore statm errors here */ + } /* statm fields just zero */ + + if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */ + if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){ + status2proc(sbuf, p, 1); + } + } + + // if multithreaded, some values are crap + if(p->nlwp > 1){ + p->wchan = (KLONG)~0ull; + } + + /* some number->text resolving which is time consuming and kind of insane */ + if (flags & PROC_FILLUSR){ + memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser); + if(flags & PROC_FILLSTATUS) { + memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser); + memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser); + memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser); + } + } + + /* some number->text resolving which is time consuming and kind of insane */ + if (flags & PROC_FILLGRP){ + memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup); + if(flags & PROC_FILLSTATUS) { + memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup); + memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup); + memcpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup); + } + } + + if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */ + p->cmdline = file2strvec(path, "cmdline"); + else + p->cmdline = NULL; + + if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */ + p->environ = file2strvec(path, "environ"); + else + p->environ = NULL; + + return p; +next_proc: + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// +// This reads /proc/*/task/* data, for one task. +// p is the POSIX process (task group summary) (not needed by THIS implementation) +// t is the POSIX thread (task group member, generally not the leader) +// path is a path to the task, with some room to spare. +static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) { + static struct stat sb; // stat() buffer + static char sbuf[1024]; // buffer for stat,statm + unsigned flags = PT->flags; + +//printf("hhh\n"); + if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */ + goto next_task; + +// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) +// goto next_task; /* not one of the requested uids */ + + t->euid = sb.st_uid; /* need a way to get real uid */ + t->egid = sb.st_gid; /* need a way to get real gid */ + +//printf("iii\n"); + if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */ + if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 )) + goto next_task; /* error reading /proc/#/stat */ + stat2proc(sbuf, t); /* parse /proc/#/stat */ + } + + if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */ +#if 0 + if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 )) + statm2proc(sbuf, t); /* ignore statm errors here */ +#else + t->size = p->size; + t->resident = p->resident; + t->share = p->share; + t->trs = p->trs; + t->lrs = p->lrs; + t->drs = p->drs; + t->dt = p->dt; +#endif + } /* statm fields just zero */ + + if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */ + if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){ + status2proc(sbuf, t, 0); + } + } + + /* some number->text resolving which is time consuming */ + if (flags & PROC_FILLUSR){ + memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser); + if(flags & PROC_FILLSTATUS) { + memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser); + memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser); + memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser); + } + } + + /* some number->text resolving which is time consuming */ + if (flags & PROC_FILLGRP){ + memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup); + if(flags & PROC_FILLSTATUS) { + memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup); + memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup); + memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup); + } + } + +#if 0 + if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */ + t->cmdline = file2strvec(path, "cmdline"); + else + t->cmdline = NULL; + + if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */ + t->environ = file2strvec(path, "environ"); + else + t->environ = NULL; +#else + t->cmdline = p->cmdline; // better not free these until done with all threads! + t->environ = p->environ; +#endif + + t->ppid = p->ppid; // ought to put the per-task ppid somewhere + + return t; +next_task: + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// +// This finds processes in /proc in the traditional way. +// Return non-zero on success. +static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) { + static struct direct *ent; /* dirent handle */ + char *restrict const path = PT->path; + for (;;) { + ent = readdir(PT->procfs); + if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; + if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + } + p->tgid = strtoul(ent->d_name, NULL, 10); + p->tid = p->tgid; + memcpy(path, "/proc/", 6); + strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries + return 1; +} + +////////////////////////////////////////////////////////////////////////////////// +// This finds tasks in /proc/*/task/ in the traditional way. +// Return non-zero on success. +static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) { + static struct direct *ent; /* dirent handle */ + if(PT->taskdir_user != p->tgid){ + if(PT->taskdir){ + closedir(PT->taskdir); + } + // use "path" as some tmp space + snprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid); + PT->taskdir = opendir(path); + if(!PT->taskdir) return 0; + PT->taskdir_user = p->tgid; + } + for (;;) { + ent = readdir(PT->taskdir); + if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0; + if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break; + } + t->tid = strtoul(ent->d_name, NULL, 10); + t->tgid = p->tgid; + t->ppid = p->ppid; // cover for kernel behavior? we want both actually...? + snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name); + return 1; +} + +////////////////////////////////////////////////////////////////////////////////// +// This "finds" processes in a list that was given to openproc(). +// Return non-zero on success. (tgid was handy) +static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) { + char *restrict const path = PT->path; + pid_t tgid = *(PT->pids)++; + if(likely( tgid )){ + snprintf(path, PROCPATHLEN, "/proc/%d", tgid); + p->tgid = tgid; + p->tid = tgid; // they match for leaders + } + return tgid; +} + +////////////////////////////////////////////////////////////////////////////////// +/* readproc: return a pointer to a proc_t filled with requested info about the + * next process available matching the restriction set. If no more such + * processes are available, return a null pointer (boolean false). Use the + * passed buffer instead of allocating space if it is non-NULL. */ + +/* This is optimized so that if a PID list is given, only those files are + * searched for in /proc. If other lists are given in addition to the PID list, + * the same logic can follow through as for the no-PID list case. This is + * fairly complex, but it does try to not to do any unnecessary work. + */ +proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) { + proc_t *ret; + proc_t *saved_p; + + PT->did_fake=0; +// if (PT->taskdir) { +// closedir(PT->taskdir); +// PT->taskdir = NULL; +// PT->taskdir_user = -1; +// } + + saved_p = p; + if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */ + + for(;;){ + // fills in the path, plus p->tid and p->tgid + if (unlikely(! PT->finder(PT,p) )) goto out; + + // go read the process data + ret = PT->reader(PT,p); + if(ret) return ret; + } + +out: + if(!saved_p) free(p); + // FIXME: maybe set tid to -1 here, for "-" in display? + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// +// readtask: return a pointer to a proc_t filled with requested info about the +// next task available. If no more such tasks are available, return a null +// pointer (boolean false). Use the passed buffer instead of allocating +// space if it is non-NULL. +proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) { + static char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline + proc_t *ret; + proc_t *saved_t; + + saved_t = t; + if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */ + + // 1. got to fake a thread for old kernels + // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!) + if(task_dir_missing || p->nlwp < 2){ + if(PT->did_fake) goto out; + PT->did_fake=1; + memcpy(t,p,sizeof(proc_t)); + // use the per-task pending, not per-tgid pending +#ifdef SIGNAL_STRING + memcpy(&t->signal, &t->_sigpnd, sizeof t->signal); +#else + t->signal = t->_sigpnd; +#endif + return t; + } + + for(;;){ + // fills in the path, plus t->tid and t->tgid + if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out; // simple_nexttid + + // go read the task data + ret = PT->taskreader(PT,p,t,path); // simple_readtask + if(ret) return ret; + } + +out: + if(!saved_t) free(t); + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////// + +// initiate a process table scan +PROCTAB* openproc(int flags, ...) { + va_list ap; + struct stat sbuf; + static int did_stat; + PROCTAB* PT = xmalloc(sizeof(PROCTAB)); + + if(!did_stat){ + task_dir_missing = stat("/proc/self/task", &sbuf); + did_stat = 1; + } + PT->taskdir = NULL; + PT->taskdir_user = -1; + PT->taskfinder = simple_nexttid; + PT->taskreader = simple_readtask; + + PT->reader = simple_readproc; + if (flags & PROC_PID){ + PT->procfs = NULL; + PT->finder = listed_nextpid; + }else{ + PT->procfs = opendir("/proc"); + if(!PT->procfs) return NULL; + PT->finder = simple_nextpid; + } + PT->flags = flags; + + va_start(ap, flags); /* Init args list */ + if (flags & PROC_PID) + PT->pids = va_arg(ap, pid_t*); + else if (flags & PROC_UID) { + PT->uids = va_arg(ap, uid_t*); + PT->nuid = va_arg(ap, int); + } + va_end(ap); /* Clean up args list */ + + return PT; +} + +// terminate a process table scan +void closeproc(PROCTAB* PT) { + if (PT){ + if (PT->procfs) closedir(PT->procfs); + if (PT->taskdir) closedir(PT->taskdir); + memset(PT,'#',sizeof(PROCTAB)); + free(PT); + } +} + +// deallocate the space allocated by readproc if the passed rbuf was NULL +void freeproc(proc_t* p) { + if (!p) /* in case p is NULL */ + return; + /* ptrs are after strings to avoid copying memory when building them. */ + /* so free is called on the address of the address of strvec[0]. */ + if (p->cmdline) + free((void*)*p->cmdline); + if (p->environ) + free((void*)*p->environ); + free(p); +} + + +////////////////////////////////////////////////////////////////////////////////// +void look_up_our_self(proc_t *p) { + char sbuf[1024]; + + if(file2str("/proc/self", "stat", sbuf, sizeof sbuf) == -1){ + fprintf(stderr, "Error, do this: mount -t proc none /proc\n"); + _exit(47); + } + stat2proc(sbuf, p); // parse /proc/self/stat +} + +HIDDEN_ALIAS(readproc); +HIDDEN_ALIAS(readtask); + +/* Convenient wrapper around openproc and readproc to slurp in the whole process + * table subset satisfying the constraints of flags and the optional PID list. + * Free allocated memory with exit(). Access via tab[N]->member. The pointer + * list is NULL terminated. + */ +proc_t** readproctab(int flags, ...) { + PROCTAB* PT = NULL; + proc_t** tab = NULL; + int n = 0; + va_list ap; + + va_start(ap, flags); /* pass through args to openproc */ + if (flags & PROC_UID) { + /* temporary variables to ensure that va_arg() instances + * are called in the right order + */ + uid_t* u; + int i; + + u = va_arg(ap, uid_t*); + i = va_arg(ap, int); + PT = openproc(flags, u, i); + } + else if (flags & PROC_PID) + PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */ + else + PT = openproc(flags); + va_end(ap); + do { /* read table: */ + tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */ + tab[n] = readproc_direct(PT, NULL); /* final null to terminate */ + } while (tab[n++]); /* stop when NULL reached */ + closeproc(PT); + return tab; +} + +// Try again, this time with threads and selection. +proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) { + proc_t** ptab = NULL; + unsigned n_proc_alloc = 0; + unsigned n_proc = 0; + + proc_t** ttab = NULL; + unsigned n_task_alloc = 0; + unsigned n_task = 0; + + proc_t* data = NULL; + unsigned n_alloc = 0; + unsigned long n_used = 0; + + proc_data_t *pd; + + for(;;){ + proc_t *tmp; + if(n_alloc == n_used){ + //proc_t *old = data; + n_alloc = n_alloc*5/4+30; // grow by over 25% + data = realloc(data,sizeof(proc_t)*n_alloc); + //if(!data) return NULL; + } + if(n_proc_alloc == n_proc){ + //proc_t **old = ptab; + n_proc_alloc = n_proc_alloc*5/4+30; // grow by over 25% + ptab = realloc(ptab,sizeof(proc_t*)*n_proc_alloc); + //if(!ptab) return NULL; + } + tmp = readproc_direct(PT, data+n_used); + if(!tmp) break; + if(!want_proc(tmp)) continue; + ptab[n_proc++] = (proc_t*)(n_used++); + if(!( PT->flags & PROC_LOOSE_TASKS )) continue; + for(;;){ + proc_t *t; + if(n_alloc == n_used){ + proc_t *old = data; + n_alloc = n_alloc*5/4+30; // grow by over 25% + data = realloc(data,sizeof(proc_t)*n_alloc); + // have to move tmp too + tmp = data+(tmp-old); + //if(!data) return NULL; + } + if(n_task_alloc == n_task){ + //proc_t **old = ttab; + n_task_alloc = n_task_alloc*5/4+1; // grow by over 25% + ttab = realloc(ttab,sizeof(proc_t*)*n_task_alloc); + //if(!ttab) return NULL; + } + t = readtask_direct(PT, tmp, data+n_used); + if(!t) break; + if(!want_task(t)) continue; + ttab[n_task++] = (proc_t*)(n_used++); + } + } + + pd = malloc(sizeof(proc_data_t)); + pd->proc = ptab; + pd->task = ttab; + pd->nproc = n_proc; + pd->ntask = n_task; + if(PT->flags & PROC_LOOSE_TASKS){ + pd->tab = ttab; + pd->n = n_task; + }else{ + pd->tab = ptab; + pd->n = n_proc; + } + // change array indexes to pointers + while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]); + while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]); + + return pd; +} + +/* + * get_proc_stats - lookup a single tasks information and fill out a proc_t + * + * On failure, returns NULL. On success, returns 'p' and 'p' is a valid + * and filled out proc_t structure. + */ +proc_t * get_proc_stats(pid_t pid, proc_t *p) { + static char path[PATH_MAX], sbuf[1024]; + struct stat statbuf; + + sprintf(path, "/proc/%d", pid); + if (stat(path, &statbuf)) { + perror("stat"); + return NULL; + } + + if (file2str(path, "stat", sbuf, sizeof sbuf) >= 0) + stat2proc(sbuf, p); /* parse /proc/#/stat */ + if (file2str(path, "statm", sbuf, sizeof sbuf) >= 0) + statm2proc(sbuf, p); /* ignore statm errors here */ + if (file2str(path, "status", sbuf, sizeof sbuf) >= 0) + status2proc(sbuf, p, 0 /*FIXME*/); + + return p; +} diff --git a/proc/readproc.h b/proc/readproc.h new file mode 100644 index 0000000..a953b29 --- /dev/null +++ b/proc/readproc.h @@ -0,0 +1,253 @@ +#ifndef PROCPS_PROC_READPROC_H +#define PROCPS_PROC_READPROC_H + +// New Interface to Process Table -- PROCTAB Stream (a la Directory streams) +// Copyright 1996 Charles L. Blake. +// Copyright 1998 Michael K. Johnson +// Copyright 1998-2003 Albert Cahalan +// May be distributed under the terms of the +// GNU Library General Public License, a copy of which is provided +// in the file COPYING + + +#include "procps.h" +#include "pwcache.h" + +#define SIGNAL_STRING + +EXTERN_C_BEGIN + +// ld cutime, cstime, priority, nice, timeout, alarm, rss, +// c state, +// d ppid, pgrp, session, tty, tpgid, +// s signal, blocked, sigignore, sigcatch, +// lu flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime, +// lu rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip, +// lu start_time, vsize, wchan, + +// This is to help document a transition from pid to tgid/tid caused +// by the introduction of thread support. It is used in cases where +// neither tgid nor tid seemed correct. (in other words, FIXME) +#define XXXID tid + +// Basic data structure which holds all information we can get about a process. +// (unless otherwise specified, fields are read from /proc/#/stat) +// +// Most of it comes from task_struct in linux/sched.h +// +typedef struct proc_t { +// 1st 16 bytes + int + tid, // (special) task id, the POSIX thread ID (see also: tgid) + ppid; // stat,status pid of parent process + unsigned + pcpu; // stat (special) %CPU usage (is not filled in by readproc!!!) + char + state, // stat,status single-char code for process state (S=sleeping) + pad_1, // n/a padding + pad_2, // n/a padding + pad_3; // n/a padding +// 2nd 16 bytes + unsigned long long + utime, // stat user-mode CPU time accumulated by process + stime, // stat kernel-mode CPU time accumulated by process +// and so on... + cutime, // stat cumulative utime of process and reaped children + cstime, // stat cumulative stime of process and reaped children + start_time; // stat start time of process -- seconds since 1-1-70 +#ifdef SIGNAL_STRING + char + // Linux 2.1.7x and up have 64 signals. Allow 64, plus '\0' and padding. + signal[18], // status mask of pending signals, per-task for readtask() but per-proc for readproc() + blocked[18], // status mask of blocked signals + sigignore[18], // status mask of ignored signals + sigcatch[18], // status mask of caught signals + _sigpnd[18]; // status mask of PER TASK pending signals +#else + long long + // Linux 2.1.7x and up have 64 signals. + signal, // status mask of pending signals, per-task for readtask() but per-proc for readproc() + blocked, // status mask of blocked signals + sigignore, // status mask of ignored signals + sigcatch, // status mask of caught signals + _sigpnd; // status mask of PER TASK pending signals +#endif + unsigned KLONG + start_code, // stat address of beginning of code segment + end_code, // stat address of end of code segment + start_stack, // stat address of the bottom of stack for the process + kstk_esp, // stat kernel stack pointer + kstk_eip, // stat kernel instruction pointer + wchan; // stat (special) address of kernel wait channel proc is sleeping in + long + priority, // stat kernel scheduling priority + nice, // stat standard unix nice level of process + rss, // stat resident set size from /proc/#/stat (pages) + alarm, // stat ? + // the next 7 members come from /proc/#/statm + size, // statm total # of pages of memory + resident, // statm number of resident set (non-swapped) pages (4k) + share, // statm number of pages of shared (mmap'd) memory + trs, // statm text resident set size + lrs, // statm shared-lib resident set size + drs, // statm data resident set size + dt; // statm dirty pages + unsigned long + vm_size, // status same as vsize in kb + vm_lock, // status locked pages in kb + vm_rss, // status same as rss in kb + vm_data, // status data size + vm_stack, // status stack size + vm_exe, // status executable size + vm_lib, // status library size (all pages, not just used ones) + rtprio, // stat real-time priority + sched, // stat scheduling class + vsize, // stat number of pages of virtual memory ... + rss_rlim, // stat resident set size limit? + flags, // stat kernel flags for the process + min_flt, // stat number of minor page faults since process start + maj_flt, // stat number of major page faults since process start + cmin_flt, // stat cumulative min_flt of process and child processes + cmaj_flt; // stat cumulative maj_flt of process and child processes + char + **environ, // (special) environment string vector (/proc/#/environ) + **cmdline; // (special) command line string vector (/proc/#/cmdline) + char + // Be compatible: Digital allows 16 and NT allows 14 ??? + euser[P_G_SZ], // stat(),status effective user name + ruser[P_G_SZ], // status real user name + suser[P_G_SZ], // status saved user name + fuser[P_G_SZ], // status filesystem user name + rgroup[P_G_SZ], // status real group name + egroup[P_G_SZ], // status effective group name + sgroup[P_G_SZ], // status saved group name + fgroup[P_G_SZ], // status filesystem group name + cmd[16]; // stat,status basename of executable file in call to exec(2) + struct proc_t + *ring, // n/a thread group ring + *next; // n/a various library uses + int + pgrp, // stat process group id + session, // stat session id + nlwp, // stat,status number of threads, or 0 if no clue + tgid, // (special) task group ID, the POSIX PID (see also: tid) + tty, // stat full device number of controlling terminal + euid, egid, // stat(),status effective + ruid, rgid, // status real + suid, sgid, // status saved + fuid, fgid, // status fs (used for file access only) + tpgid, // stat terminal process group id + exit_signal, // stat might not be SIGCHLD + processor; // stat current (or most recent?) CPU +} proc_t; + +// PROCTAB: data structure holding the persistent information readproc needs +// from openproc(). The setup is intentionally similar to the dirent interface +// and other system table interfaces (utmp+wtmp come to mind). + +#include +#include +#include + +#define PROCPATHLEN 64 // must hold /proc/2000222000/task/2000222000/cmdline + +typedef struct PROCTAB { + DIR* procfs; +// char deBug0[64]; + DIR* taskdir; // for threads +// char deBug1[64]; + pid_t taskdir_user; // for threads + int did_fake; // used when taskdir is missing + int(*finder)(struct PROCTAB *restrict const, proc_t *restrict const); + proc_t*(*reader)(struct PROCTAB *restrict const, proc_t *restrict const); + int(*taskfinder)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const); + proc_t*(*taskreader)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const); + pid_t* pids; // pids of the procs + uid_t* uids; // uids of procs + int nuid; // cannot really sentinel-terminate unsigned short[] + int i; // generic + unsigned flags; + unsigned u; // generic + void * vp; // generic + char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline + unsigned pathlen; // length of string in the above (w/o '\0') +} PROCTAB; + +// initialize a PROCTAB structure holding needed call-to-call persistent data +extern PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ ); + +typedef struct proc_data_t { + proc_t **tab; + proc_t **proc; + proc_t **task; + int n; + int nproc; + int ntask; +} proc_data_t; + +extern proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT); + +// Convenient wrapper around openproc and readproc to slurp in the whole process +// table subset satisfying the constraints of flags and the optional PID list. +// Free allocated memory with exit(). Access via tab[N]->member. The pointer +// list is NULL terminated. + +extern proc_t** readproctab(int flags, ... /* same as openproc */ ); + +// clean-up open files, etc from the openproc() +extern void closeproc(PROCTAB* PT); + +// retrieve the next process matching the criteria set by the openproc() +extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p); +extern proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t); + +// warning: interface may change +extern int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid); + +extern void look_up_our_self(proc_t *p); + +// deallocate space allocated by readproc + +extern void freeproc(proc_t* p); + +//fill out a proc_t for a single task +extern proc_t * get_proc_stats(pid_t pid, proc_t *p); + +// openproc/readproctab: +// +// Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be +// opened.) By default readproc will consider all processes as valid to parse +// and return, but not actually fill in the cmdline, environ, and /proc/#/statm +// derived memory fields. +// +// `flags' (a bitwise-or of PROC_* below) modifies the default behavior. The +// "fill" options will cause more of the proc_t to be filled in. The "filter" +// options all use the second argument as the pointer to a list of objects: +// process status', process id's, user id's. The third +// argument is the length of the list (currently only used for lists of user +// id's since uid_t supports no convenient termination sentinel.) + +#define PROC_FILLMEM 0x0001 // read statm +#define PROC_FILLCOM 0x0002 // alloc and fill in `cmdline' +#define PROC_FILLENV 0x0004 // alloc and fill in `environ' +#define PROC_FILLUSR 0x0008 // resolve user id number -> user name +#define PROC_FILLGRP 0x0010 // resolve group id number -> group name +#define PROC_FILLSTATUS 0x0020 // read status -- currently unconditional +#define PROC_FILLSTAT 0x0040 // read stat -- currently unconditional +#define PROC_FILLWCHAN 0x0080 // look up WCHAN name +#define PROC_FILLARG 0x0100 // alloc and fill in `cmdline' + +#define PROC_LOOSE_TASKS 0x0200 // threat threads as if they were processes + +// Obsolete, consider only processes with one of the passed: +#define PROC_PID 0x1000 // process id numbers ( 0 terminated) +#define PROC_UID 0x4000 // user id numbers ( length needed ) + +// it helps to give app code a few spare bits +#define PROC_SPARE_1 0x01000000 +#define PROC_SPARE_2 0x02000000 +#define PROC_SPARE_3 0x04000000 +#define PROC_SPARE_4 0x08000000 + +EXTERN_C_END +#endif diff --git a/proc/sig.c b/proc/sig.c new file mode 100644 index 0000000..ea63397 --- /dev/null +++ b/proc/sig.c @@ -0,0 +1,240 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include "sig.h" + +/* Linux signals: + * + * SIGSYS is required by Unix98. + * SIGEMT is part of SysV, BSD, and ancient UNIX tradition. + * + * They are provided by these Linux ports: alpha, mips, sparc, and sparc64. + * You get SIGSTKFLT and SIGUNUSED instead on i386, m68k, ppc, and arm. + * (this is a Linux & libc bug -- both must be fixed) + * + * Total garbage: SIGIO SIGINFO SIGIOT SIGLOST SIGCLD + * (popular ones are handled as aliases) + * Nearly garbage: SIGSTKFLT SIGUNUSED (nothing else to fill slots) + */ + +/* Linux 2.3.29 replaces SIGUNUSED with the standard SIGSYS signal */ +#ifndef SIGSYS +# warning Standards require that define SIGSYS +# define SIGSYS SIGUNUSED +#endif + +/* If we see both, it is likely SIGSTKFLT (junk) was replaced. */ +#ifdef SIGEMT +# undef SIGSTKFLT +#endif + +#ifndef SIGRTMIN +# warning Standards require that define SIGRTMIN; assuming 32 +# define SIGRTMIN 32 +#endif + +/* It seems the SPARC libc does not know the kernel supports SIGPWR. */ +#ifndef SIGPWR +# warning Your header files lack SIGPWR. (assuming it is number 29) +# define SIGPWR 29 +#endif + +typedef struct mapstruct { + const char *name; + int num; +} mapstruct; + + +static const mapstruct sigtable[] = { + {"ABRT", SIGABRT}, /* IOT */ + {"ALRM", SIGALRM}, + {"BUS", SIGBUS}, + {"CHLD", SIGCHLD}, /* CLD */ + {"CONT", SIGCONT}, +#ifdef SIGEMT + {"EMT", SIGEMT}, +#endif + {"FPE", SIGFPE}, + {"HUP", SIGHUP}, + {"ILL", SIGILL}, + {"INT", SIGINT}, + {"KILL", SIGKILL}, + {"PIPE", SIGPIPE}, + {"POLL", SIGPOLL}, /* IO */ + {"PROF", SIGPROF}, + {"PWR", SIGPWR}, + {"QUIT", SIGQUIT}, + {"SEGV", SIGSEGV}, +#ifdef SIGSTKFLT + {"STKFLT", SIGSTKFLT}, +#endif + {"STOP", SIGSTOP}, + {"SYS", SIGSYS}, /* UNUSED */ + {"TERM", SIGTERM}, + {"TRAP", SIGTRAP}, + {"TSTP", SIGTSTP}, + {"TTIN", SIGTTIN}, + {"TTOU", SIGTTOU}, + {"URG", SIGURG}, + {"USR1", SIGUSR1}, + {"USR2", SIGUSR2}, + {"VTALRM", SIGVTALRM}, + {"WINCH", SIGWINCH}, + {"XCPU", SIGXCPU}, + {"XFSZ", SIGXFSZ} +}; + +static const int number_of_signals = sizeof(sigtable)/sizeof(mapstruct); + +static int compare_signal_names(const void *a, const void *b){ + return strcasecmp( ((const mapstruct*)a)->name, ((const mapstruct*)b)->name ); +} + +/* return -1 on failure */ +int signal_name_to_number(const char *restrict name){ + long val; + int offset; + + /* clean up name */ + if(!strncasecmp(name,"SIG",3)) name += 3; + + if(!strcasecmp(name,"CLD")) return SIGCHLD; + if(!strcasecmp(name,"IO")) return SIGPOLL; + if(!strcasecmp(name,"IOT")) return SIGABRT; + + /* search the table */ + { + const mapstruct ms = {name,0}; + const mapstruct *restrict const ptr = bsearch( + &ms, + sigtable, + number_of_signals, + sizeof(mapstruct), + compare_signal_names + ); + if(ptr) return ptr->num; + } + + if(!strcasecmp(name,"RTMIN")) return SIGRTMIN; + if(!strcasecmp(name,"EXIT")) return 0; + if(!strcasecmp(name,"NULL")) return 0; + + offset = 0; + if(!strncasecmp(name,"RTMIN+",6)){ + name += 6; + offset = SIGRTMIN; + } + + /* not found, so try as a number */ + { + char *endp; + val = strtol(name,&endp,10); + if(*endp || endp==name) return -1; /* not valid */ + } + if(val+SIGRTMIN>127) return -1; /* not valid */ + return val+offset; +} + +const char *signal_number_to_name(int signo){ + static char buf[32]; + int n = number_of_signals; + signo &= 0x7f; /* need to process exit values too */ + while(n--){ + if(sigtable[n].num==signo) return sigtable[n].name; + } + if(signo == SIGRTMIN) return "RTMIN"; + if(signo) sprintf(buf, "RTMIN+%d", signo-SIGRTMIN); + else strcpy(buf,"0"); /* AIX has NULL; Solaris has EXIT */ + return buf; +} + +int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line){ + char buf[1280]; /* 128 signals, "RTMIN+xx" is largest */ + int ret = 0; /* to be used as exit code by caller */ + int place = 0; /* position on this line */ + int amt; + if(argc > 128) return 1; + while(argc--){ + char tmpbuf[16]; + const char *restrict const txt = *argv; + if(*txt >= '0' && *txt <= '9'){ + long val; + char *endp; + val = strtol(txt,&endp,10); + if(*endp){ + fprintf(stderr, "Signal \"%s\" not known.\n", txt); + ret = 1; + goto end; + } + amt = sprintf(tmpbuf, "%s", signal_number_to_name(val)); + }else{ + int sno; + sno = signal_name_to_number(txt); + if(sno == -1){ + fprintf(stderr, "Signal \"%s\" not known.\n", txt); + ret = 1; + goto end; + } + amt = sprintf(tmpbuf, "%d", sno); + } + + if(!place){ + strcpy(buf,tmpbuf); + place = amt; + goto end; + } + if(amt+place+1 > max_line){ + printf("%s\n", buf); + strcpy(buf,tmpbuf); + place = amt; + goto end; + } + sprintf(buf+place, " %s", tmpbuf); + place += amt+1; +end: + argv++; + } + if(place) printf("%s\n", buf); + return ret; +} + +void pretty_print_signals(void){ + int i = 0; + while(++i <= number_of_signals){ + int n; + n = printf("%2d %s", i, signal_number_to_name(i)); + if(i%7) printf(" \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + n); + else printf("\n"); + } + if((i-1)%7) printf("\n"); +} + +void unix_print_signals(void){ + int pos = 0; + int i = 0; + while(++i <= number_of_signals){ + if(i-1) printf("%c", (pos>73)?(pos=0,'\n'):(pos++,' ') ); + pos += printf("%s", signal_number_to_name(i)); + } + printf("\n"); +} + +/* sanity check */ +static int init_signal_list(void) __attribute__((constructor)); +static int init_signal_list(void){ + if(number_of_signals != 31){ + fprintf(stderr, "WARNING: %d signals -- adjust and recompile.\n", number_of_signals); + } + return 0; +} diff --git a/proc/sig.h b/proc/sig.h new file mode 100644 index 0000000..ee850a2 --- /dev/null +++ b/proc/sig.h @@ -0,0 +1,30 @@ +#ifndef PROC_SIG_H +#define PROC_SIG_H +/* + * Copyright 1998-2003 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include "procps.h" + +EXTERN_C_BEGIN + +/* return -1 on failure */ +extern int signal_name_to_number(const char *restrict name); + +extern const char *signal_number_to_name(int signo); + +extern int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line); + +extern void pretty_print_signals(void); + +extern void unix_print_signals(void); + +EXTERN_C_END +#endif diff --git a/proc/slab.c b/proc/slab.c new file mode 100644 index 0000000..60c5696 --- /dev/null +++ b/proc/slab.c @@ -0,0 +1,337 @@ +/* + * slab.c - slab related functions for libproc + * + * Chris Rivera + * Robert Love + * + * This program is licensed under the GNU Library General Public License, v2 + * + * Copyright (C) 2003 Chris Rivera + * Copyright 2004, Albert Cahalan + */ + +#include +#include +#include +#include +#include + +#include "slab.h" +#include "procps.h" + +#define SLABINFO_LINE_LEN 2048 +#define SLABINFO_VER_LEN 100 +#define SLABINFO_FILE "/proc/slabinfo" + +static struct slab_info *free_index; + +/* + * get_slabnode - allocate slab_info structures using a free list + * + * In the fast path, we simply return a node off the free list. In the slow + * list, we malloc() a new node. The free list is never automatically reaped, + * both for simplicity and because the number of slab caches is fairly + * constant. + */ +static struct slab_info *get_slabnode(void) +{ + struct slab_info *node; + + if (free_index) { + node = free_index; + free_index = free_index->next; + } else { + node = malloc(sizeof(struct slab_info)); + if (!node) + perror("malloc"); + } + + return node; +} + +/* + * slab_badname_detect - return true if current slab was declared with + * whitespaces for instance + * FIXME :Other cases ? + */ + +static int slab_badname_detect(const char *restrict buffer) +{ + int numberarea=0; + while (*buffer){ + if((*buffer)==' ') + numberarea=1; + if(isalpha(*buffer)&&numberarea) + return 1; + buffer++; + } + return 0; +} + +/* + * put_slabinfo - return all allocated nodes to the free list + */ +void put_slabinfo(struct slab_info *head) +{ + free_index = head; +} + +/* + * free_slabinfo - deallocate the memory associated with each node in the + * slab_info linked list + */ +void free_slabinfo(struct slab_info *list) +{ + while (list) { + struct slab_info *temp = list->next; + free(list); + list = temp; + } +} + +// parse_slabinfo20 - actual parse routine for slabinfo 2.x (2.6 kernels) +// Note: difference between 2.0 and 2.1 is in the ": globalstat" part where version 2.1 +// has extra column . We don't use ": globalstat" part in both versions. +// +// Formats (we don't use "statistics" extensions) +// +// slabinfo - version: 2.1 +// # name \ +// : tunables \ +// : slabdata +// +// slabinfo - version: 2.1 (statistics) +// # name \ +// : tunables \ +// : slabdata \ +// : globalstat \ +// : cpustat +// +// slabinfo - version: 2.0 +// # name \ +// : tunables \ +// : slabdata +// +// slabinfo - version: 2.0 (statistics) +// # name \ +// : tunables \ +// : slabdata \ +// : globalstat \ +// : cpustat +static int parse_slabinfo20(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + struct slab_info *curr = NULL, *prev = NULL; + char buffer[SLABINFO_LINE_LEN]; + int entries = 0; + int page_size = getpagesize(); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, f)) { + int assigned; + + if (buffer[0] == '#') + continue; + + curr = get_slabnode(); + if (!curr) + break; + + if (entries++ == 0) + *list = curr; + else + prev->next = curr; + + assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d : tunables %*d %*d %*d : \ + slabdata %d %d %*d", curr->name, + &curr->nr_active_objs, &curr->nr_objs, + &curr->obj_size, &curr->objs_per_slab, + &curr->pages_per_slab, &curr->nr_active_slabs, + &curr->nr_slabs); + + if (assigned < 8) { + fprintf(stderr, "unrecognizable data in slabinfo!\n"); + curr = NULL; + break; + } + + if (curr->obj_size < stats->min_obj_size) + stats->min_obj_size = curr->obj_size; + if (curr->obj_size > stats->max_obj_size) + stats->max_obj_size = curr->obj_size; + + curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; + + if (curr->nr_objs) { + curr->use = 100 * curr->nr_active_objs / curr->nr_objs; + stats->nr_active_caches++; + } else + curr->use = 0; + + stats->nr_objs += curr->nr_objs; + stats->nr_active_objs += curr->nr_active_objs; + stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size; + stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size; + stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; + stats->nr_slabs += curr->nr_slabs; + stats->nr_active_slabs += curr->nr_active_slabs; + + prev = curr; + } + + if (!curr) { + fprintf(stderr, "\rerror reading slabinfo!\n"); + return 1; + } + + curr->next = NULL; + stats->nr_caches = entries; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; + + return 0; +} + +/* + * parse_slabinfo11 - actual parsing routine for slabinfo 1.1 (2.4 kernels) + */ +static int parse_slabinfo11(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + struct slab_info *curr = NULL, *prev = NULL; + char buffer[SLABINFO_LINE_LEN]; + int entries = 0; + int page_size = getpagesize(); + + stats->min_obj_size = INT_MAX; + stats->max_obj_size = 0; + + while (fgets(buffer, SLABINFO_LINE_LEN, f)) { + int assigned; + + curr = get_slabnode(); + if (!curr) + break; + + if (entries++ == 0) + *list = curr; + else + prev->next = curr; + + assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) + "s %d %d %d %d %d %d", + curr->name, &curr->nr_active_objs, + &curr->nr_objs, &curr->obj_size, + &curr->nr_active_slabs, &curr->nr_slabs, + &curr->pages_per_slab); + + if (assigned < 6) { + fprintf(stderr, "unrecognizable data in your slabinfo version 1.1\n\r"); + if(slab_badname_detect(buffer)) + fprintf(stderr, "Found an error in cache name at line %s\n", buffer); + curr = NULL; + break; + } + + if (curr->obj_size < stats->min_obj_size) + stats->min_obj_size = curr->obj_size; + if (curr->obj_size > stats->max_obj_size) + stats->max_obj_size = curr->obj_size; + + curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; + + if (curr->nr_objs) { + curr->use = 100 * curr->nr_active_objs / curr->nr_objs; + stats->nr_active_caches++; + } else + curr->use = 0; + + if (curr->obj_size) + curr->objs_per_slab = curr->pages_per_slab * + page_size / curr->obj_size; + + stats->nr_objs += curr->nr_objs; + stats->nr_active_objs += curr->nr_active_objs; + stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size; + stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size; + stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; + stats->nr_slabs += curr->nr_slabs; + stats->nr_active_slabs += curr->nr_active_slabs; + + prev = curr; + } + + if (!curr) { + fprintf(stderr, "\rerror reading slabinfo!\n"); + return 1; + } + + curr->next = NULL; + stats->nr_caches = entries; + if (stats->nr_objs) + stats->avg_obj_size = stats->total_size / stats->nr_objs; + + return 0; +} + +/* + * parse_slabinfo10 - actual parsing routine for slabinfo 1.0 (2.2 kernels) + * + * Not yet implemented. Please feel free. + */ +static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats, + FILE *f) +{ + (void) list, (void) stats, (void) f; + fprintf(stderr, "slabinfo version 1.0 not yet supported\n"); + return 1; +} + +/* + * slabinfo - parse the system's slabinfo and fill out both a linked list of + * slab_info structures and the slab_stat structure + * + * The function returns zero on success, in which case 'list' and 'stats' are + * valid. Nonzero is returned on failure and the state of 'list' and 'stats' + * are undefined. + */ +int get_slabinfo(struct slab_info **list, struct slab_stat *stats) +{ + FILE *slabfile; + char buffer[SLABINFO_VER_LEN]; + int major, minor, ret = 0; + + slabfile = fopen(SLABINFO_FILE, "r"); + if (!slabfile) { + perror("fopen " SLABINFO_FILE); + return 1; + } + + if (!fgets(buffer, SLABINFO_VER_LEN, slabfile)) { + fprintf(stderr, "cannot read from slabinfo\n"); + return 1; + } + + if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) { + fprintf(stderr, "not the good old slabinfo we know\n"); + return 1; + } + + if (major == 2) + ret = parse_slabinfo20(list, stats, slabfile); + else if (major == 1 && minor == 1) + ret = parse_slabinfo11(list, stats, slabfile); + else if (major == 1 && minor == 0) + ret = parse_slabinfo10(list, stats, slabfile); + else { + fprintf(stderr, "unrecognizable slabinfo version\n"); + return 1; + } + + fclose(slabfile); + + return ret; +} diff --git a/proc/slab.h b/proc/slab.h new file mode 100644 index 0000000..ea17ddc --- /dev/null +++ b/proc/slab.h @@ -0,0 +1,39 @@ +#ifndef _PROC_SLAB_H +#define _PROC_SLAB_H + +#define SLAB_INFO_NAME_LEN 64 + +struct slab_info { + char name[SLAB_INFO_NAME_LEN]; /* name of this cache */ + struct slab_info *next; + unsigned long cache_size; /* size of entire cache */ + unsigned nr_objs; /* number of objects in this cache */ + unsigned nr_active_objs; /* number of active objects */ + unsigned obj_size; /* size of each object */ + unsigned objs_per_slab; /* number of objects per slab */ + unsigned pages_per_slab; /* number of pages per slab */ + unsigned nr_slabs; /* number of slabs in this cache */ + unsigned nr_active_slabs; /* number of active slabs */ + unsigned use; /* percent full: total / active */ +}; + +struct slab_stat { + unsigned long total_size; /* size of all objects */ + unsigned long active_size; /* size of all active objects */ + unsigned nr_objs; /* number of objects, among all caches */ + unsigned nr_active_objs; /* number of active objects, among all caches */ + unsigned nr_pages; /* number of pages consumed by all objects */ + unsigned nr_slabs; /* number of slabs, among all caches */ + unsigned nr_active_slabs; /* number of active slabs, among all caches */ + unsigned nr_caches; /* number of caches */ + unsigned nr_active_caches; /* number of active caches */ + unsigned avg_obj_size; /* average object size */ + unsigned min_obj_size; /* size of smallest object */ + unsigned max_obj_size; /* size of largest object */ +}; + +extern void put_slabinfo(struct slab_info *); +extern void free_slabinfo(struct slab_info *); +extern int get_slabinfo(struct slab_info **, struct slab_stat *); + +#endif /* _PROC_SLAB_H */ diff --git a/proc/smaps.c b/proc/smaps.c new file mode 100644 index 0000000..17789a1 --- /dev/null +++ b/proc/smaps.c @@ -0,0 +1,171 @@ +#if 0 +#include "procps.h" +#include +#include +#include +#include +#include +#include +#include +#include "procps.h" + +struct smap_entry { + unsigned KLONG start; + unsigned KLONG beyond; + long long offset; + char flags[8]; + unsigned dev_major; + unsigned dev_minor; + unsigned long long inode; + + unsigned long rss; + unsigned long pss; + unsigned long sclean; + unsigned long sdirty; + unsigned long pclean; + unsigned long pdirty; + unsigned long ref; + unsigned long swap; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// This code will surely make normal programmers cry. I need speed though, +// and /proc/*/smaps should make anybody cry. (WTF kind of brain damage...?) + +struct smap_summary { + unsigned long size; + unsigned long rss; + unsigned long pss; + unsigned long sclean; + unsigned long sdirty; + unsigned long pclean; + unsigned long pdirty; + unsigned long ref; + unsigned long swap; +}; + +struct ssjt { + char str[16]; + int len; + int offset; +}; + +#define JTE(o,x) {#x,sizeof(#x)-1,o} + +void get_smap_sums(struct smap_summary *restrict ss, const char *restrict const filename){ + static struct ssjt table[] = { + JTE(-1,default), + JTE( 1,Rss), + JTE(-1,default), + JTE( 2,Pss), + JTE( 8,Swap), + JTE( 5,Private_Clean), + JTE( 6,Private_Dirty), + JTE(-1,default), + JTE( 7,Referenced), + JTE(-1,default), + JTE( 0,Size), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), // KernelPageSize would go here + JTE(-1,default), + JTE(-1,default), + JTE( 4,Shared_Dirty), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE( 3,Shared_Clean), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + JTE(-1,default), + }; + char buf[20480]; + int p1 = 0; + int p2 = 0; + memset(ss,0,sizeof *ss); + int fd = open(filename,O_RDONLY); + if(fd==-1) + return; + for(;;){ + char *nlp = memchr(buf+p1,'\n',p2-p1); + if(!nlp){ + if(p1){ + // the memmove should never do anything, because the + // kernel seems to give us the greatest number of + // complete lines of text that fit in a single page + // (and thus p2-p1 is zero) + memmove(buf,buf+p1,p2-p1); + p2 -= p1; + p1 = 0; + } + ssize_t rb = read(fd,buf+p1,sizeof buf - p1); + if(rb < 1) + break; + p2 += rb; + nlp = memchr(buf+p1,'\n',p2-p1); + if(!nlp) + break; + } + char *s = buf+p1; + int len = nlp-s; + p1 += len+1; + if(len<27) + continue; +//printf("j <%13.13s>\n",s); + if(s[0]<'A' || s[0]>'Z') + continue; + unsigned hash = ( (s[8]&15) + (s[1]&15) ) ^ (s[0]&3); + hash &= 31; +//printf("x %2d <%13.13s>\n",hash,s); + if(s[table[hash].len] != ':') + continue; +//printf("y %2d <%13.13s>\n",hash,s); + if(memcmp(table[hash].str,s,table[hash].len)) + continue; +//printf("z %2d <%13.13s>\n",hash,s); + s += table[hash].len; + while(*++s==' ') + ; + unsigned long ul = 0; + for(;;){ + char c = *s++; + if(c != ' '){ + ul *= 10; + ul += c-'0'; + continue; + } + break; + } +// if(table[hash].offset == 2) +// printf("Pss:%20lu kB\n",ul); + unsigned long *ulp = &ss->size + table[hash].offset; + *ulp += ul; +// memcpy(ss+table[hash].offset*sizeof(unsigned long), &ul, sizeof(unsigned long)); + } + close(fd); +} + +int main(int argc, char *argv[]){ + struct smap_summary ss; + get_smap_sums(&ss, argv[1]); + printf("%9lu\n",ss.size); + printf("%9lu\n",ss.rss); + printf("%9lu\n",ss.pss); + printf("%9lu\n",ss.sclean); + printf("%9lu\n",ss.sdirty); + printf("%9lu\n",ss.pclean); + printf("%9lu\n",ss.pdirty); + printf("%9lu\n",ss.ref); + printf("%9lu\n",ss.swap); + return 0; +} +#endif diff --git a/proc/sysinfo.c b/proc/sysinfo.c new file mode 100644 index 0000000..cd59534 --- /dev/null +++ b/proc/sysinfo.c @@ -0,0 +1,912 @@ +// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com +// Copyright 1998-2003 Albert Cahalan +// +// This file is placed under the conditions of the GNU Library +// General Public License, version 2, or any later version. +// See file COPYING for information on distribution conditions. +// +// File for parsing top-level /proc entities. */ +// +// June 2003, Fabian Frederick, disk and slab info + +#include +#include +#include +#include +#include + +#include +#include +#include "version.h" +#include "sysinfo.h" /* include self to verify prototypes */ + +#ifndef HZ +#include /* htons */ +#endif + +long smp_num_cpus; /* number of CPUs */ + +#define BAD_OPEN_MESSAGE \ +"Error: /proc must be mounted\n" \ +" To mount /proc at boot you need an /etc/fstab line like:\n" \ +" /proc /proc proc defaults\n" \ +" In the meantime, run \"mount /proc /proc -t proc\"\n" + +#define STAT_FILE "/proc/stat" +static int stat_fd = -1; +#define UPTIME_FILE "/proc/uptime" +static int uptime_fd = -1; +#define LOADAVG_FILE "/proc/loadavg" +static int loadavg_fd = -1; +#define MEMINFO_FILE "/proc/meminfo" +static int meminfo_fd = -1; +#define VMINFO_FILE "/proc/vmstat" +static int vminfo_fd = -1; + +// As of 2.6.24 /proc/meminfo seems to need 888 on 64-bit, +// and would need 1258 if the obsolete fields were there. +static char buf[2048]; + +/* This macro opens filename only if necessary and seeks to 0 so + * that successive calls to the functions are more efficient. + * It also reads the current contents of the file into the global buf. + */ +#define FILE_TO_BUF(filename, fd) do{ \ + static int local_n; \ + if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \ + fputs(BAD_OPEN_MESSAGE, stderr); \ + fflush(NULL); \ + _exit(102); \ + } \ + lseek(fd, 0L, SEEK_SET); \ + if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ + perror(filename); \ + fflush(NULL); \ + _exit(103); \ + } \ + buf[local_n] = '\0'; \ +}while(0) + +/* evals 'x' twice */ +#define SET_IF_DESIRED(x,y) do{ if(x) *(x) = (y); }while(0) + + +/***********************************************************************/ +int uptime(double *restrict uptime_secs, double *restrict idle_secs) { + double up=0, idle=0; + char *restrict savelocale; + + FILE_TO_BUF(UPTIME_FILE,uptime_fd); + savelocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC,"C"); + if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { + setlocale(LC_NUMERIC,savelocale); + fputs("bad data in " UPTIME_FILE "\n", stderr); + return 0; + } + setlocale(LC_NUMERIC,savelocale); + SET_IF_DESIRED(uptime_secs, up); + SET_IF_DESIRED(idle_secs, idle); + return up; /* assume never be zero seconds in practice */ +} + +/*********************************************************************** + * Some values in /proc are expressed in units of 1/HZ seconds, where HZ + * is the kernel clock tick rate. One of these units is called a jiffy. + * The HZ value used in the kernel may vary according to hacker desire. + * According to Linus Torvalds, this is not true. He considers the values + * in /proc as being in architecture-dependant units that have no relation + * to the kernel clock tick rate. Examination of the kernel source code + * reveals that opinion as wishful thinking. + * + * In any case, we need the HZ constant as used in /proc. (the real HZ value + * may differ, but we don't care) There are several ways we could get HZ: + * + * 1. Include the kernel header file. If it changes, recompile this library. + * 2. Use the sysconf() function. When HZ changes, recompile the C library! + * 3. Ask the kernel. This is obviously correct... + * + * Linus Torvalds won't let us ask the kernel, because he thinks we should + * not know the HZ value. Oh well, we don't have to listen to him. + * Someone smuggled out the HZ value. :-) + * + * This code should work fine, even if Linus fixes the kernel to match his + * stated behavior. The code only fails in case of a partial conversion. + * + * Recent update: on some architectures, the 2.4 kernel provides an + * ELF note to indicate HZ. This may be for ARM or user-mode Linux + * support. This ought to be investigated. Note that sysconf() is still + * unreliable, because it doesn't return an error code when it is + * used with a kernel that doesn't support the ELF note. On some other + * architectures there may be a system call or sysctl() that will work. + */ + +unsigned long long Hertz; + +static void old_Hertz_hack(void){ + unsigned long long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */ + double up_1, up_2, seconds; + unsigned long long jiffies; + unsigned h; + char *restrict savelocale; + + savelocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + do{ + FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_1); + /* uptime(&up_1, NULL); */ + FILE_TO_BUF(STAT_FILE,stat_fd); + sscanf(buf, "cpu %Lu %Lu %Lu %Lu", &user_j, &nice_j, &sys_j, &other_j); + FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_2); + /* uptime(&up_2, NULL); */ + } while((long long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */ + setlocale(LC_NUMERIC, savelocale); + jiffies = user_j + nice_j + sys_j + other_j; + seconds = (up_1 + up_2) / 2; + h = (unsigned)( (double)jiffies/seconds/smp_num_cpus ); + /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ + switch(h){ + case 9 ... 11 : Hertz = 10; break; /* S/390 (sometimes) */ + case 18 ... 22 : Hertz = 20; break; /* user-mode Linux */ + case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */ + case 48 ... 52 : Hertz = 50; break; + case 58 ... 61 : Hertz = 60; break; + case 62 ... 65 : Hertz = 64; break; /* StrongARM /Shark */ + case 95 ... 105 : Hertz = 100; break; /* normal Linux */ + case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */ + case 195 ... 204 : Hertz = 200; break; /* normal << 1 */ + case 247 ... 252 : Hertz = 250; break; + case 253 ... 260 : Hertz = 256; break; + case 393 ... 408 : Hertz = 400; break; /* normal << 2 */ + case 790 ... 808 : Hertz = 800; break; /* normal << 3 */ + case 990 ... 1010 : Hertz = 1000; break; /* ARM */ + case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */ + case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */ + default: +#ifdef HZ + Hertz = (unsigned long long)HZ; /* */ +#else + /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */ + Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL; +#endif + fprintf(stderr, "Unknown HZ value! (%d) Assume %Ld.\n", h, Hertz); + } +} + +// same as: euid != uid || egid != gid +#ifndef AT_SECURE +#define AT_SECURE 23 // secure mode boolean (true if setuid, etc.) +#endif + +#ifndef AT_CLKTCK +#define AT_CLKTCK 17 // frequency of times() +#endif + +#define NOTE_NOT_FOUND 42 + +//extern char** environ; + +/* for ELF executables, notes are pushed before environment and args */ +static unsigned long find_elf_note(unsigned long findme){ + unsigned long *ep = (unsigned long *)environ; + while(*ep++); + while(*ep){ + if(ep[0]==findme) return ep[1]; + ep+=2; + } + return NOTE_NOT_FOUND; +} + +int have_privs; + +static int check_for_privs(void){ + unsigned long rc = find_elf_note(AT_SECURE); + if(rc==NOTE_NOT_FOUND){ + // not valid to run this code after UID or GID change! + // (if needed, may use AT_UID and friends instead) + rc = geteuid() != getuid() || getegid() != getgid(); + } + return !!rc; +} + +static void init_libproc(void) __attribute__((constructor)); +static void init_libproc(void){ + have_privs = check_for_privs(); + // ought to count CPUs in /proc/stat instead of relying + // on glibc, which foolishly tries to parse /proc/cpuinfo + // + // SourceForge has an old Alpha running Linux 2.2.20 that + // appears to have a non-SMP kernel on a 2-way SMP box. + // _SC_NPROCESSORS_CONF returns 2, resulting in HZ=512 + // _SC_NPROCESSORS_ONLN returns 1, which should work OK + smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if(smp_num_cpus<1) smp_num_cpus=1; /* SPARC glibc is buggy */ + + if(linux_version_code > LINUX_VERSION(2, 4, 0)){ + Hertz = find_elf_note(AT_CLKTCK); + if(Hertz!=NOTE_NOT_FOUND) return; + fputs("2.4+ kernel w/o ELF notes? -- report this\n", stderr); + } + old_Hertz_hack(); +} + +#if 0 +/*********************************************************************** + * The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we + * recover jiffies by adding up the 4 or 5 numbers we are given. SMP kernels + * (as of pre-2.4 era) can report idle time going backwards, perhaps due + * to non-atomic reads and updates. There is no locking for these values. + */ +#ifndef NAN +#define NAN (-0.0) +#endif +#define JT unsigned long long +void eight_cpu_numbers(double *restrict uret, double *restrict nret, double *restrict sret, double *restrict iret, double *restrict wret, double *restrict xret, double *restrict yret, double *restrict zret){ + double tmp_u, tmp_n, tmp_s, tmp_i, tmp_w, tmp_x, tmp_y, tmp_z; + double scale; /* scale values to % */ + static JT old_u, old_n, old_s, old_i, old_w, old_x, old_y, old_z; + JT new_u, new_n, new_s, new_i, new_w, new_x, new_y, new_z; + JT ticks_past; /* avoid div-by-0 by not calling too often :-( */ + + tmp_w = 0.0; + new_w = 0; + tmp_x = 0.0; + new_x = 0; + tmp_y = 0.0; + new_y = 0; + tmp_z = 0.0; + new_z = 0; + + FILE_TO_BUF(STAT_FILE,stat_fd); + sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &new_u, &new_n, &new_s, &new_i, &new_w, &new_x, &new_y, &new_z); + ticks_past = (new_u+new_n+new_s+new_i+new_w+new_x+new_y+new_z)-(old_u+old_n+old_s+old_i+old_w+old_x+old_y+old_z); + if(ticks_past){ + scale = 100.0 / (double)ticks_past; + tmp_u = ( (double)new_u - (double)old_u ) * scale; + tmp_n = ( (double)new_n - (double)old_n ) * scale; + tmp_s = ( (double)new_s - (double)old_s ) * scale; + tmp_i = ( (double)new_i - (double)old_i ) * scale; + tmp_w = ( (double)new_w - (double)old_w ) * scale; + tmp_x = ( (double)new_x - (double)old_x ) * scale; + tmp_y = ( (double)new_y - (double)old_y ) * scale; + tmp_z = ( (double)new_z - (double)old_z ) * scale; + }else{ + tmp_u = NAN; + tmp_n = NAN; + tmp_s = NAN; + tmp_i = NAN; + tmp_w = NAN; + tmp_x = NAN; + tmp_y = NAN; + tmp_z = NAN; + } + SET_IF_DESIRED(uret, tmp_u); + SET_IF_DESIRED(nret, tmp_n); + SET_IF_DESIRED(sret, tmp_s); + SET_IF_DESIRED(iret, tmp_i); + SET_IF_DESIRED(wret, tmp_w); + SET_IF_DESIRED(xret, tmp_x); + SET_IF_DESIRED(yret, tmp_y); + SET_IF_DESIRED(zret, tmp_z); + old_u=new_u; + old_n=new_n; + old_s=new_s; + old_i=new_i; + old_w=new_w; + old_x=new_x; + old_y=new_y; + old_z=new_z; +} +#undef JT +#endif + +/***********************************************************************/ +void loadavg(double *restrict av1, double *restrict av5, double *restrict av15) { + double avg_1=0, avg_5=0, avg_15=0; + char *restrict savelocale; + + FILE_TO_BUF(LOADAVG_FILE,loadavg_fd); + savelocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) { + fputs("bad data in " LOADAVG_FILE "\n", stderr); + exit(1); + } + setlocale(LC_NUMERIC, savelocale); + SET_IF_DESIRED(av1, avg_1); + SET_IF_DESIRED(av5, avg_5); + SET_IF_DESIRED(av15, avg_15); +} + + static char buff[BUFFSIZE]; /* used in the procedures */ +/***********************************************************************/ + +static void crash(const char *filename) { + perror(filename); + exit(EXIT_FAILURE); +} + +/***********************************************************************/ + +static void getrunners(unsigned int *restrict running, unsigned int *restrict blocked) { + struct direct *ent; + DIR *proc; + + *running=0; + *blocked=0; + + if((proc=opendir("/proc"))==NULL) crash("/proc"); + + while(( ent=readdir(proc) )) { + char tbuf[32]; + char *cp; + int fd; + char c; + + if (!isdigit(ent->d_name[0])) continue; + sprintf(tbuf, "/proc/%s/stat", ent->d_name); + + fd = open(tbuf, O_RDONLY, 0); + if (fd == -1) continue; + memset(tbuf, '\0', sizeof tbuf); // didn't feel like checking read() + read(fd, tbuf, sizeof tbuf - 1); // need 32 byte buffer at most + close(fd); + + cp = strrchr(tbuf, ')'); + if(!cp) continue; + c = cp[2]; + + if (c=='R') { + (*running)++; + continue; + } + if (c=='D') { + (*blocked)++; + continue; + } + } + closedir(proc); +} + +/***********************************************************************/ + +void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz, + unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout, + unsigned *restrict intr, unsigned *restrict ctxt, + unsigned int *restrict running, unsigned int *restrict blocked, + unsigned int *restrict btime, unsigned int *restrict processes) { + static int fd; + unsigned long long llbuf = 0; + int need_vmstat_file = 0; + int need_proc_scan = 0; + const char* b; + buff[BUFFSIZE-1] = 0; /* ensure null termination in buffer */ + + if(fd){ + lseek(fd, 0L, SEEK_SET); + }else{ + fd = open("/proc/stat", O_RDONLY, 0); + if(fd == -1) crash("/proc/stat"); + } + read(fd,buff,BUFFSIZE-1); + *intr = 0; + *ciow = 0; /* not separated out until the 2.5.41 kernel */ + *cxxx = 0; /* not separated out until the 2.6.0-test4 kernel */ + *cyyy = 0; /* not separated out until the 2.6.0-test4 kernel */ + *czzz = 0; /* not separated out until the 2.6.11 kernel */ + + b = strstr(buff, "cpu "); + if(b) sscanf(b, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", cuse, cice, csys, cide, ciow, cxxx, cyyy, czzz); + + b = strstr(buff, "page "); + if(b) sscanf(b, "page %lu %lu", pin, pout); + else need_vmstat_file = 1; + + b = strstr(buff, "swap "); + if(b) sscanf(b, "swap %lu %lu", s_in, sout); + else need_vmstat_file = 1; + + b = strstr(buff, "intr "); + if(b) sscanf(b, "intr %Lu", &llbuf); + *intr = llbuf; + + b = strstr(buff, "ctxt "); + if(b) sscanf(b, "ctxt %Lu", &llbuf); + *ctxt = llbuf; + + b = strstr(buff, "btime "); + if(b) sscanf(b, "btime %u", btime); + + b = strstr(buff, "processes "); + if(b) sscanf(b, "processes %u", processes); + + b = strstr(buff, "procs_running "); + if(b) sscanf(b, "procs_running %u", running); + else need_proc_scan = 1; + + b = strstr(buff, "procs_blocked "); + if(b) sscanf(b, "procs_blocked %u", blocked); + else need_proc_scan = 1; + + if(need_proc_scan){ /* Linux 2.5.46 (approximately) and below */ + getrunners(running, blocked); + } + + (*running)--; // exclude vmstat itself + + if(need_vmstat_file){ /* Linux 2.5.40-bk4 and above */ + vminfo(); + *pin = vm_pgpgin; + *pout = vm_pgpgout; + *s_in = vm_pswpin; + *sout = vm_pswpout; + } +} + +/***********************************************************************/ +/* + * Copyright 1999 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +typedef struct mem_table_struct { + const char *name; /* memory type name */ + unsigned long *slot; /* slot in return struct */ +} mem_table_struct; + +static int compare_mem_table_structs(const void *a, const void *b){ + return strcmp(((const mem_table_struct*)a)->name,((const mem_table_struct*)b)->name); +} + +/* example data, following junk, with comments added: + * + * MemTotal: 61768 kB old + * MemFree: 1436 kB old + * MemShared: 0 kB old (now always zero; not calculated) + * Buffers: 1312 kB old + * Cached: 20932 kB old + * Active: 12464 kB new + * Inact_dirty: 7772 kB new + * Inact_clean: 2008 kB new + * Inact_target: 0 kB new + * Inact_laundry: 0 kB new, and might be missing too + * HighTotal: 0 kB + * HighFree: 0 kB + * LowTotal: 61768 kB + * LowFree: 1436 kB + * SwapTotal: 122580 kB old + * SwapFree: 60352 kB old + * Inactive: 20420 kB 2.5.41+ + * Dirty: 0 kB 2.5.41+ + * Writeback: 0 kB 2.5.41+ + * Mapped: 9792 kB 2.5.41+ + * Slab: 4564 kB 2.5.41+ + * Committed_AS: 8440 kB 2.5.41+ + * PageTables: 304 kB 2.5.41+ + * ReverseMaps: 5738 2.5.41+ + * SwapCached: 0 kB 2.5.??+ + * HugePages_Total: 220 2.5.??+ + * HugePages_Free: 138 2.5.??+ + * Hugepagesize: 4096 kB 2.5.??+ + */ + +/* obsolete */ +unsigned long kb_main_shared; +/* old but still kicking -- the important stuff */ +unsigned long kb_main_buffers; +unsigned long kb_main_cached; +unsigned long kb_main_free; +unsigned long kb_main_total; +unsigned long kb_swap_free; +unsigned long kb_swap_total; +/* recently introduced */ +unsigned long kb_high_free; +unsigned long kb_high_total; +unsigned long kb_low_free; +unsigned long kb_low_total; +/* 2.4.xx era */ +unsigned long kb_active; +unsigned long kb_inact_laundry; +unsigned long kb_inact_dirty; +unsigned long kb_inact_clean; +unsigned long kb_inact_target; +unsigned long kb_swap_cached; /* late 2.4 and 2.6+ only */ +/* derived values */ +unsigned long kb_swap_used; +unsigned long kb_main_used; +/* 2.5.41+ */ +unsigned long kb_writeback; +unsigned long kb_slab; +unsigned long nr_reversemaps; +unsigned long kb_committed_as; +unsigned long kb_dirty; +unsigned long kb_inactive; +unsigned long kb_mapped; +unsigned long kb_pagetables; +// seen on a 2.6.x kernel: +static unsigned long kb_vmalloc_chunk; +static unsigned long kb_vmalloc_total; +static unsigned long kb_vmalloc_used; +// seen on 2.6.24-rc6-git12 +static unsigned long kb_anon_pages; +static unsigned long kb_bounce; +static unsigned long kb_commit_limit; +static unsigned long kb_nfs_unstable; +static unsigned long kb_swap_reclaimable; +static unsigned long kb_swap_unreclaimable; + +void meminfo(void){ + char namebuf[16]; /* big enough to hold any row name */ + mem_table_struct findme = { namebuf, NULL}; + mem_table_struct *found; + char *head; + char *tail; + static const mem_table_struct mem_table[] = { + {"Active", &kb_active}, // important + {"AnonPages", &kb_anon_pages}, + {"Bounce", &kb_bounce}, + {"Buffers", &kb_main_buffers}, // important + {"Cached", &kb_main_cached}, // important + {"CommitLimit", &kb_commit_limit}, + {"Committed_AS", &kb_committed_as}, + {"Dirty", &kb_dirty}, // kB version of vmstat nr_dirty + {"HighFree", &kb_high_free}, + {"HighTotal", &kb_high_total}, + {"Inact_clean", &kb_inact_clean}, + {"Inact_dirty", &kb_inact_dirty}, + {"Inact_laundry",&kb_inact_laundry}, + {"Inact_target", &kb_inact_target}, + {"Inactive", &kb_inactive}, // important + {"LowFree", &kb_low_free}, + {"LowTotal", &kb_low_total}, + {"Mapped", &kb_mapped}, // kB version of vmstat nr_mapped + {"MemFree", &kb_main_free}, // important + {"MemShared", &kb_main_shared}, // important, but now gone! + {"MemTotal", &kb_main_total}, // important + {"NFS_Unstable", &kb_nfs_unstable}, + {"PageTables", &kb_pagetables}, // kB version of vmstat nr_page_table_pages + {"ReverseMaps", &nr_reversemaps}, // same as vmstat nr_page_table_pages + {"SReclaimable", &kb_swap_reclaimable}, // "swap reclaimable" (dentry and inode structures) + {"SUnreclaim", &kb_swap_unreclaimable}, + {"Slab", &kb_slab}, // kB version of vmstat nr_slab + {"SwapCached", &kb_swap_cached}, + {"SwapFree", &kb_swap_free}, // important + {"SwapTotal", &kb_swap_total}, // important + {"VmallocChunk", &kb_vmalloc_chunk}, + {"VmallocTotal", &kb_vmalloc_total}, + {"VmallocUsed", &kb_vmalloc_used}, + {"Writeback", &kb_writeback}, // kB version of vmstat nr_writeback + }; + const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct); + + FILE_TO_BUF(MEMINFO_FILE,meminfo_fd); + + kb_inactive = ~0UL; + + head = buf; + for(;;){ + tail = strchr(head, ':'); + if(!tail) break; + *tail = '\0'; + if(strlen(head) >= sizeof(namebuf)){ + head = tail+1; + goto nextline; + } + strcpy(namebuf,head); + found = bsearch(&findme, mem_table, mem_table_count, + sizeof(mem_table_struct), compare_mem_table_structs + ); + head = tail+1; + if(!found) goto nextline; + *(found->slot) = strtoul(head,&tail,10); +nextline: + tail = strchr(head, '\n'); + if(!tail) break; + head = tail+1; + } + if(!kb_low_total){ /* low==main except with large-memory support */ + kb_low_total = kb_main_total; + kb_low_free = kb_main_free; + } + if(kb_inactive==~0UL){ + kb_inactive = kb_inact_dirty + kb_inact_clean + kb_inact_laundry; + } + kb_swap_used = kb_swap_total - kb_swap_free; + kb_main_used = kb_main_total - kb_main_free; +} + +/*****************************************************************/ + +/* read /proc/vminfo only for 2.5.41 and above */ + +typedef struct vm_table_struct { + const char *name; /* VM statistic name */ + unsigned long *slot; /* slot in return struct */ +} vm_table_struct; + +static int compare_vm_table_structs(const void *a, const void *b){ + return strcmp(((const vm_table_struct*)a)->name,((const vm_table_struct*)b)->name); +} + +// see include/linux/page-flags.h and mm/page_alloc.c +unsigned long vm_nr_dirty; // dirty writable pages +unsigned long vm_nr_writeback; // pages under writeback +unsigned long vm_nr_pagecache; // pages in pagecache -- gone in 2.5.66+ kernels +unsigned long vm_nr_page_table_pages;// pages used for pagetables +unsigned long vm_nr_reverse_maps; // includes PageDirect +unsigned long vm_nr_mapped; // mapped into pagetables +unsigned long vm_nr_slab; // in slab +unsigned long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line) +unsigned long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line) +unsigned long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line) +unsigned long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line) +unsigned long vm_pgalloc; // page allocations +unsigned long vm_pgfree; // page freeings +unsigned long vm_pgactivate; // pages moved inactive -> active +unsigned long vm_pgdeactivate; // pages moved active -> inactive +unsigned long vm_pgfault; // total faults (major+minor) +unsigned long vm_pgmajfault; // major faults +unsigned long vm_pgscan; // pages scanned by page reclaim +unsigned long vm_pgrefill; // inspected by refill_inactive_zone +unsigned long vm_pgsteal; // total pages reclaimed +unsigned long vm_kswapd_steal; // pages reclaimed by kswapd +// next 3 as defined by the 2.5.52 kernel +unsigned long vm_pageoutrun; // times kswapd ran page reclaim +unsigned long vm_allocstall; // times a page allocator ran direct reclaim +unsigned long vm_pgrotated; // pages rotated to the tail of the LRU for immediate reclaim +// seen on a 2.6.8-rc1 kernel, apparently replacing old fields +static unsigned long vm_pgalloc_dma; // +static unsigned long vm_pgalloc_high; // +static unsigned long vm_pgalloc_normal; // +static unsigned long vm_pgrefill_dma; // +static unsigned long vm_pgrefill_high; // +static unsigned long vm_pgrefill_normal; // +static unsigned long vm_pgscan_direct_dma; // +static unsigned long vm_pgscan_direct_high; // +static unsigned long vm_pgscan_direct_normal; // +static unsigned long vm_pgscan_kswapd_dma; // +static unsigned long vm_pgscan_kswapd_high; // +static unsigned long vm_pgscan_kswapd_normal; // +static unsigned long vm_pgsteal_dma; // +static unsigned long vm_pgsteal_high; // +static unsigned long vm_pgsteal_normal; // +// seen on a 2.6.8-rc1 kernel +static unsigned long vm_kswapd_inodesteal; // +static unsigned long vm_nr_unstable; // +static unsigned long vm_pginodesteal; // +static unsigned long vm_slabs_scanned; // + +void vminfo(void){ + char namebuf[16]; /* big enough to hold any row name */ + vm_table_struct findme = { namebuf, NULL}; + vm_table_struct *found; + char *head; + char *tail; + static const vm_table_struct vm_table[] = { + {"allocstall", &vm_allocstall}, + {"kswapd_inodesteal", &vm_kswapd_inodesteal}, + {"kswapd_steal", &vm_kswapd_steal}, + {"nr_dirty", &vm_nr_dirty}, // page version of meminfo Dirty + {"nr_mapped", &vm_nr_mapped}, // page version of meminfo Mapped + {"nr_page_table_pages", &vm_nr_page_table_pages},// same as meminfo PageTables + {"nr_pagecache", &vm_nr_pagecache}, // gone in 2.5.66+ kernels + {"nr_reverse_maps", &vm_nr_reverse_maps}, // page version of meminfo ReverseMaps GONE + {"nr_slab", &vm_nr_slab}, // page version of meminfo Slab + {"nr_unstable", &vm_nr_unstable}, + {"nr_writeback", &vm_nr_writeback}, // page version of meminfo Writeback + {"pageoutrun", &vm_pageoutrun}, + {"pgactivate", &vm_pgactivate}, + {"pgalloc", &vm_pgalloc}, // GONE (now separate dma,high,normal) + {"pgalloc_dma", &vm_pgalloc_dma}, + {"pgalloc_high", &vm_pgalloc_high}, + {"pgalloc_normal", &vm_pgalloc_normal}, + {"pgdeactivate", &vm_pgdeactivate}, + {"pgfault", &vm_pgfault}, + {"pgfree", &vm_pgfree}, + {"pginodesteal", &vm_pginodesteal}, + {"pgmajfault", &vm_pgmajfault}, + {"pgpgin", &vm_pgpgin}, // important + {"pgpgout", &vm_pgpgout}, // important + {"pgrefill", &vm_pgrefill}, // GONE (now separate dma,high,normal) + {"pgrefill_dma", &vm_pgrefill_dma}, + {"pgrefill_high", &vm_pgrefill_high}, + {"pgrefill_normal", &vm_pgrefill_normal}, + {"pgrotated", &vm_pgrotated}, + {"pgscan", &vm_pgscan}, // GONE (now separate direct,kswapd and dma,high,normal) + {"pgscan_direct_dma", &vm_pgscan_direct_dma}, + {"pgscan_direct_high", &vm_pgscan_direct_high}, + {"pgscan_direct_normal",&vm_pgscan_direct_normal}, + {"pgscan_kswapd_dma", &vm_pgscan_kswapd_dma}, + {"pgscan_kswapd_high", &vm_pgscan_kswapd_high}, + {"pgscan_kswapd_normal",&vm_pgscan_kswapd_normal}, + {"pgsteal", &vm_pgsteal}, // GONE (now separate dma,high,normal) + {"pgsteal_dma", &vm_pgsteal_dma}, + {"pgsteal_high", &vm_pgsteal_high}, + {"pgsteal_normal", &vm_pgsteal_normal}, + {"pswpin", &vm_pswpin}, // important + {"pswpout", &vm_pswpout}, // important + {"slabs_scanned", &vm_slabs_scanned}, + }; + const int vm_table_count = sizeof(vm_table)/sizeof(vm_table_struct); + + vm_pgalloc = 0; + vm_pgrefill = 0; + vm_pgscan = 0; + vm_pgsteal = 0; + + FILE_TO_BUF(VMINFO_FILE,vminfo_fd); + + head = buf; + for(;;){ + tail = strchr(head, ' '); + if(!tail) break; + *tail = '\0'; + if(strlen(head) >= sizeof(namebuf)){ + head = tail+1; + goto nextline; + } + strcpy(namebuf,head); + found = bsearch(&findme, vm_table, vm_table_count, + sizeof(vm_table_struct), compare_vm_table_structs + ); + head = tail+1; + if(!found) goto nextline; + *(found->slot) = strtoul(head,&tail,10); +nextline: + +//if(found) fprintf(stderr,"%s=%d\n",found->name,*(found->slot)); +//else fprintf(stderr,"%s not found\n",findme.name); + + tail = strchr(head, '\n'); + if(!tail) break; + head = tail+1; + } + if(!vm_pgalloc) + vm_pgalloc = vm_pgalloc_dma + vm_pgalloc_high + vm_pgalloc_normal; + if(!vm_pgrefill) + vm_pgrefill = vm_pgrefill_dma + vm_pgrefill_high + vm_pgrefill_normal; + if(!vm_pgscan) + vm_pgscan = vm_pgscan_direct_dma + vm_pgscan_direct_high + vm_pgscan_direct_normal + + vm_pgscan_kswapd_dma + vm_pgscan_kswapd_high + vm_pgscan_kswapd_normal; + if(!vm_pgsteal) + vm_pgsteal = vm_pgsteal_dma + vm_pgsteal_high + vm_pgsteal_normal; +} + +/////////////////////////////////////////////////////////////////////// +// based on Fabian Frederick's /proc/diskstats parser + + +unsigned int getpartitions_num(struct disk_stat *disks, int ndisks){ + int i=0; + int partitions=0; + + for (i=0;i +#include +#include "procps.h" + +EXTERN_C_BEGIN + +extern unsigned long long Hertz; /* clock tick frequency */ +extern long smp_num_cpus; /* number of CPUs */ +extern int have_privs; /* boolean, true if setuid or similar */ + +#if 0 +#define JT double +extern void eight_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret, JT *wret, JT *xret, JT *yret, JT *zret); +#undef JT +#endif + +extern int uptime (double *uptime_secs, double *idle_secs); +extern void loadavg(double *av1, double *av5, double *av15); + + +/* obsolete */ +extern unsigned long kb_main_shared; +/* old but still kicking -- the important stuff */ +extern unsigned long kb_main_buffers; +extern unsigned long kb_main_cached; +extern unsigned long kb_main_free; +extern unsigned long kb_main_total; +extern unsigned long kb_swap_free; +extern unsigned long kb_swap_total; +/* recently introduced */ +extern unsigned long kb_high_free; +extern unsigned long kb_high_total; +extern unsigned long kb_low_free; +extern unsigned long kb_low_total; +/* 2.4.xx era */ +extern unsigned long kb_active; +extern unsigned long kb_inact_laundry; // grrr... +extern unsigned long kb_inact_dirty; +extern unsigned long kb_inact_clean; +extern unsigned long kb_inact_target; +extern unsigned long kb_swap_cached; /* late 2.4+ */ +/* derived values */ +extern unsigned long kb_swap_used; +extern unsigned long kb_main_used; +/* 2.5.41+ */ +extern unsigned long kb_writeback; +extern unsigned long kb_slab; +extern unsigned long nr_reversemaps; +extern unsigned long kb_committed_as; +extern unsigned long kb_dirty; +extern unsigned long kb_inactive; +extern unsigned long kb_mapped; +extern unsigned long kb_pagetables; + +#define BUFFSIZE (64*1024) +typedef unsigned long long jiff; +extern void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz, + unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout, + unsigned *restrict intr, unsigned *restrict ctxt, + unsigned int *restrict running, unsigned int *restrict blocked, + unsigned int *restrict btime, unsigned int *restrict processes); + +extern void meminfo(void); + + +extern unsigned long vm_nr_dirty; +extern unsigned long vm_nr_writeback; +extern unsigned long vm_nr_pagecache; +extern unsigned long vm_nr_page_table_pages; +extern unsigned long vm_nr_reverse_maps; +extern unsigned long vm_nr_mapped; +extern unsigned long vm_nr_slab; +extern unsigned long vm_pgpgin; +extern unsigned long vm_pgpgout; +extern unsigned long vm_pswpin; +extern unsigned long vm_pswpout; +extern unsigned long vm_pgalloc; +extern unsigned long vm_pgfree; +extern unsigned long vm_pgactivate; +extern unsigned long vm_pgdeactivate; +extern unsigned long vm_pgfault; +extern unsigned long vm_pgmajfault; +extern unsigned long vm_pgscan; +extern unsigned long vm_pgrefill; +extern unsigned long vm_pgsteal; +extern unsigned long vm_kswapd_steal; +extern unsigned long vm_pageoutrun; +extern unsigned long vm_allocstall; + +extern void vminfo(void); + +typedef struct disk_stat{ + unsigned long long reads_sectors; + unsigned long long written_sectors; + char disk_name [16]; + unsigned inprogress_IO; + unsigned merged_reads; + unsigned merged_writes; + unsigned milli_reading; + unsigned milli_spent_IO; + unsigned milli_writing; + unsigned partitions; + unsigned reads; + unsigned weighted_milli_spent_IO; + unsigned writes; +}disk_stat; + +typedef struct partition_stat{ + char partition_name [16]; + unsigned long long reads_sectors; + unsigned parent_disk; // index into a struct disk_stat array + unsigned reads; + unsigned writes; + unsigned requested_writes; +}partition_stat; + +extern unsigned int getpartitions_num(struct disk_stat *disks, int ndisks); +extern unsigned int getdiskstat (struct disk_stat**,struct partition_stat**); + +typedef struct slab_cache{ + char name[48]; + unsigned active_objs; + unsigned num_objs; + unsigned objsize; + unsigned objperslab; +}slab_cache; + +extern unsigned int getslabinfo (struct slab_cache**); + +extern unsigned get_pid_digits(void) FUNCTION; + +EXTERN_C_END +#endif /* SYSINFO_H */ diff --git a/proc/version.c b/proc/version.c new file mode 100644 index 0000000..69bae4f --- /dev/null +++ b/proc/version.c @@ -0,0 +1,49 @@ +/* Suite version information for procps utilities + * Copyright (c) 1995 Martin Schulze + * Ammended by cblake to only export the function symbol. + * + * Modified by Albert Cahalan, ????-2003 + * + * Redistributable under the terms of the + * GNU Library General Public License; see COPYING + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include "version.h" + +#ifdef MINORVERSION +const char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION; +#else +const char procps_version[] = "procps version " VERSION "." SUBVERSION; +#endif + +void display_version(void) { + fprintf(stdout, "%s\n", procps_version); +} + +/* Linux kernel version information for procps utilities + * Copyright (c) 1996 Charles Blake + */ +#include + +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) + +int linux_version_code; + +static void init_Linux_version(void) __attribute__((constructor)); +static void init_Linux_version(void) { + static struct utsname uts; + int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */ + + if (uname(&uts) == -1) /* failure implies impending death */ + exit(1); + if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3) + fprintf(stderr, /* *very* unlikely to happen by accident */ + "Non-standard uts for running kernel:\n" + "release %s=%d.%d.%d gives version code %d\n", + uts.release, x, y, z, LINUX_VERSION(x,y,z)); + linux_version_code = LINUX_VERSION(x, y, z); +} diff --git a/proc/version.h b/proc/version.h new file mode 100644 index 0000000..3769b84 --- /dev/null +++ b/proc/version.h @@ -0,0 +1,31 @@ +#ifndef PROC_VERSION_H +#define PROC_VERSION_H + +#include "procps.h" + +/* Suite version information for procps utilities + * Copyright (c) 1995 Martin Schulze + * Linux kernel version information for procps utilities + * Copyright (c) 1996 Charles Blake + * Distributable under the terms of the GNU Library General Public License + * + * Copyright 2002 Albert Cahalan + */ + +EXTERN_C_BEGIN + +extern void display_version(void); /* display suite version */ +extern const char procps_version[]; /* global buf for suite version */ + +extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE + in /usr/include/linux/version.h */ + +/* Convenience macros for composing/decomposing version codes */ +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) +#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF) +#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF) +#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF) + +EXTERN_C_END + +#endif /* PROC_VERSION_H */ diff --git a/proc/wchan.h b/proc/wchan.h new file mode 100644 index 0000000..93d4e70 --- /dev/null +++ b/proc/wchan.h @@ -0,0 +1,16 @@ +#ifndef PROCPS_PROC_WCHAN_H +#define PROCPS_PROC_WCHAN_H + +#include "procps.h" + +EXTERN_C_BEGIN + +typedef void (*message_fn)(const char *restrict, ...) __attribute__((format(printf,1,2))); + +extern const char * lookup_wchan(unsigned KLONG address, unsigned pid); +extern int open_psdb(const char *restrict override); +extern int open_psdb_message(const char *restrict override, message_fn message); + +EXTERN_C_END + +#endif diff --git a/proc/whattime.c b/proc/whattime.c new file mode 100644 index 0000000..d2785a8 --- /dev/null +++ b/proc/whattime.c @@ -0,0 +1,87 @@ +/* This is a trivial uptime program. I hereby release this program + * into the public domain. I disclaim any responsibility for this + * program --- use it at your own risk. (as if there were any.. ;-) + * -michaelkjohnson (johnsonm@sunsite.unc.edu) + * + * Modified by Larry Greenfield to give a more traditional output, + * count users, etc. (greenfie@gauss.rutgers.edu) + * + * Modified by mkj again to fix a few tiny buglies. + * + * Modified by J. Cowley to add printing the uptime message to a + * string (for top) and to optimize file handling. 19 Mar 1993. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "whattime.h" +#include "sysinfo.h" + +static char buf[128]; +static double av[3]; + +char *sprint_uptime(void) { + struct utmp *utmpstruct; + int upminutes, uphours, updays; + int pos; + struct tm *realtime; + time_t realseconds; + int numuser; + double uptime_secs, idle_secs; + +/* first get the current time */ + + time(&realseconds); + realtime = localtime(&realseconds); + pos = sprintf(buf, " %02d:%02d:%02d ", + realtime->tm_hour, realtime->tm_min, realtime->tm_sec); + +/* read and calculate the amount of uptime */ + + uptime(&uptime_secs, &idle_secs); + + updays = (int) uptime_secs / (60*60*24); + strcat (buf, "up "); + pos += 3; + if (updays) + pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : ""); + upminutes = (int) uptime_secs / 60; + uphours = upminutes / 60; + uphours = uphours % 24; + upminutes = upminutes % 60; + if(uphours) + pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes); + else + pos += sprintf(buf + pos, "%d min, ", upminutes); + +/* count the number of users */ + + numuser = 0; + setutent(); + while ((utmpstruct = getutent())) { + if ((utmpstruct->ut_type == USER_PROCESS) && + (utmpstruct->ut_name[0] != '\0')) + numuser++; + } + endutent(); + + pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s"); + + loadavg(&av[0], &av[1], &av[2]); + + pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f", + av[0], av[1], av[2]); + + return buf; +} + +void print_uptime(void) { + printf("%s\n", sprint_uptime()); +} diff --git a/proc/whattime.h b/proc/whattime.h new file mode 100644 index 0000000..891ccd3 --- /dev/null +++ b/proc/whattime.h @@ -0,0 +1,13 @@ +#ifndef PROC_WHATTIME_H +#define PROC_WHATTIME_H + +#include "procps.h" + +EXTERN_C_BEGIN + +extern void print_uptime(void); +extern char *sprint_uptime(void); + +EXTERN_C_END + +#endif diff --git a/procps.lsm b/procps.lsm new file mode 100644 index 0000000..f2b8064 --- /dev/null +++ b/procps.lsm @@ -0,0 +1,15 @@ +Begin4 +Title: procps +Version: 3.2.7 +Entered-date: 2006-06-25 +Description: Linux system utilities +Keywords: procps /proc libproc sysctl pmap ps uptime tload slabtop + free w top vmstat watch skill snice kill pgrep pkill +Author: Albert Cahalan, Michael K. Johnson, Jim Warner, etc. +Maintained-by: various +Primary-site: http://procps.sf.net/ + 281kB procps-3.2.7.tar.gz +Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html + 281kB procps-3.2.7.tar.gz +Copying-policy: mixed +End diff --git a/procps.spec b/procps.spec new file mode 100644 index 0000000..5ac1be9 --- /dev/null +++ b/procps.spec @@ -0,0 +1,52 @@ +URL: http://procps.sf.net/ +Summary: System and process monitoring utilities +Name: procps +%define major_version 3 +%define minor_version 2 +%define revision 8 +%define version %{major_version}.%{minor_version}.%{revision} +Version: %{version} +Release: 1 +License: LGPL, GPL, BSD-like +Group: Applications/System +Source: http://procps.sf.net/procps-%{version}.tar.gz +BuildRoot: %{_tmppath}/procps-root +Packager: + +%description +The procps package contains a set of system utilities which provide +system information. Procps includes ps, free, sysctl, skill, snice, +tload, top, uptime, vmstat, w, and watch. You need some of these. + +%prep +%setup + +%build +make SKIP="/bin/kill /usr/share/man/man1/kill.1" CFLAGS="$RPM_OPT_FLAGS" + +%install +rm -rf $RPM_BUILD_ROOT +make SKIP="/bin/kill /usr/share/man/man1/kill.1" DESTDIR=$RPM_BUILD_ROOT ldconfig=echo install="install -D" lib="$RPM_BUILD_ROOT/%{_lib}/" install + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +# add libproc to the cache +/sbin/ldconfig + +%files +%defattr(0644,root,root,755) +%doc NEWS BUGS TODO COPYING COPYING.LIB README.top README AUTHORS sysctl.conf +%attr(555,root,root) /lib*/libproc*.so* +%attr(555,root,root) /bin/* +%attr(555,root,root) /sbin/* +%attr(555,root,root) /usr/bin/* + +%attr(0644,root,root) /usr/share/man/man1/* +%attr(0644,root,root) /usr/share/man/man5/* +%attr(0644,root,root) /usr/share/man/man8/* + +%changelog +* Fri Apr 14 09:23:45 PDT 2006 Jesse Brandeburg +- fix missing trailing slash in %install to fix builds on x86_64 diff --git a/ps/COPYING b/ps/COPYING new file mode 100644 index 0000000..92b8903 --- /dev/null +++ b/ps/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ps/HACKING b/ps/HACKING new file mode 100644 index 0000000..ffae92c --- /dev/null +++ b/ps/HACKING @@ -0,0 +1,53 @@ +Warning: + +This code must corrctly handle lots of picky little details to meet +the Unix98 standard while simultaneously being as compatible as +possible with the original Linux ps. Don't "fix" something without +considering the impact on all the special-case code. For example, +the "tty" format _must_ use "TT" as the header, even though the SysV +output formats _must_ use "TTY". + +File overview: + +display.c main(), debug code, iterates over processes +escape.c Does stuff like \202 and < to command and environment. +global.c Data + code to init it. +help.c Help message. +output.c Giant tables and lots of output functions. +parser.c Initial command parsing. +select.c want_this_proc() checks a process against flags & lists +sortformat.c Parses sort & format specifier lists. Picks output format. +stacktrace.c Debug code, not normally used. +../proc/* Library used to gather data. +regression Regression tests that ought to be run. +common.h Lots of interesting stuff. +Makefile Makefile +p Script used to test ps when the library is not installed. +utf Empty file used to test "ps ut?" unmangling behavior. +ps.1 Man page. + +Compiling: + +Whatever you do, don't trust the Makefiles. They are severely broken. +You can touch top.h and top won't be recompiled, or you can change +library files and discover that the library and programs won't be +recompiled. + +Operation: + +Unless the personality forces BSD parsing, parser.c tries to parse the +command line as a mixed BSD+SysV+Gnu mess. On failure, BSD parsing is +attempted. If BSD parsing fails _after_ SysV parsing has been attempted, +the error message comes from the original SysV parse. + +Control goes to sortformat.c, which must pick apart ambiguous options +like "O". Failure can reset the whole program and set PER_FORCE_BSD, +which means a second trip through parser.c and sortformat.c. + +The choice of output format happens in sortformat.c. There is a switch() +with all the valid format_flags combinations. The SysV and default +options are NULL (unless overridden by personality), which causes a +trip through SysV output format generation hackery. Note that the +default format always goes through there, even if it is for BSD. +Formats that came from the switch() (generally BSD, plus overrides) +get mangled a bit to support various SysV output modifiers. diff --git a/ps/TRANSLATION b/ps/TRANSLATION new file mode 100644 index 0000000..ff8df84 --- /dev/null +++ b/ps/TRANSLATION @@ -0,0 +1,39 @@ +Initially I only want to translate the --help output and man page. +Common error messages would be next on the list. I want to avoid +run-time overhead and bloat. + +Translations of the --help output should not be longer than 22 lines long. +Feel free to leave out the less useful options to save space. (not even +the English help text has all the options) + +I think these are the most important options: + +*** selection *** +-C by command name list +-G by real group ID list (supports names) +-U by real user ID list (supports names) +-u by effective user ID list (supports names) +-e all processes +-p by process ID list + +*** output *** +--no-heading No header line. +-o,o user-defined output +-j,j job control format +-l,l long format +-f full format +s signal format +u user-oriented format +--forest ASCII art forest (process hierarchy) +c show true command name + +List of man page translators: + +de Wed Jan 10 19:09:15 2001 by Martin Schulze +es 19 Jan 1999 by Diego Sevilla Ruiz (dsevilla@ditec.um.es) +fr 09/06/1997 par Christophe Blaess (ccb@club-internet.fr) +hu Horv#th Andr#s (the '#' is 'a' w/ '/') +it Traduzione in italiano di Giovanni Bortolozzo +it Revisione parziale di Daniele Giacomini 30/03/1999 +ja Tue Nov 14 2000 by NAKANO Takeo +nl diff --git a/ps/common.h b/ps/common.h new file mode 100644 index 0000000..0b47d90 --- /dev/null +++ b/ps/common.h @@ -0,0 +1,337 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#ifndef PROCPS_PS_H +#define PROCPS_PS_H + +#include "../proc/procps.h" +#include "../proc/escape.h" +#include "../proc/readproc.h" + +#if 0 +#define trace(args...) printf(## args) +#else +#define trace(args...) +#endif + + +/***************** GENERAL DEFINE ********************/ + + +/* selection list */ +#define SEL_RUID 1 +#define SEL_EUID 2 +#define SEL_SUID 3 +#define SEL_FUID 4 +#define SEL_RGID 5 +#define SEL_EGID 6 +#define SEL_SGID 7 +#define SEL_FGID 8 +#define SEL_PGRP 9 +#define SEL_PID 10 +#define SEL_TTY 11 +#define SEL_SESS 12 +#define SEL_COMM 13 +#define SEL_PPID 14 + +/* Since an enum could be smashed by a #define, it would be bad. */ +#define U98 0 /* Unix98 standard */ /* This must be 0 */ +#define XXX 1 /* Common extension */ +#define DEC 2 /* Digital Unix */ +#define AIX 3 /* AIX */ +#define SCO 4 /* SCO */ +#define LNX 5 /* Linux original :-) */ +#define BSD 6 /* FreeBSD and OpenBSD */ +#define SUN 7 /* SunOS 5 (Solaris) */ +#define HPU 8 /* HP-UX */ +#define SGI 9 /* Irix */ +#define SOE 10 /* IBM's S/390 OpenEdition */ +#define TST 11 /* test code */ + +/* + * Try not to overflow the output buffer: + * 32 pages for env+cmd + * 64 kB pages on IA-64 + * 4 chars for "\377", or 1 when mangling to '?' (ESC_STRETCH) + * plus some slack for other stuff + * That is about 8.5 MB on IA-64, or 0.6 MB on i386 + * + * Sadly, current kernels only supply one page of env/command data. + * The buffer is now protected with a guard page, and via other means + * to avoid hitting the guard page. + */ + +/* output buffer size */ +#define OUTBUF_SIZE (2 * 64*1024 * ESC_STRETCH) + +/******************* PS DEFINE *******************/ + +// Column flags +// Justification control for flags field comes first. +#define CF_JUST_MASK 0x0f +// CF_AIXHACK 0 +#define CF_USER 1 // left if text, right if numeric +#define CF_LEFT 2 +#define CF_RIGHT 3 +#define CF_UNLIMITED 4 +#define CF_WCHAN 5 // left if text, right if numeric +#define CF_SIGNAL 6 // right in 9, or 16 if screen_cols>107 +// Then the other flags +#define CF_PIDMAX 0x00000010 // react to pid_max +// Only one allowed; use separate bits to catch errors. +#define CF_PRINT_THREAD_ONLY 0x10000000 +#define CF_PRINT_PROCESS_ONLY 0x20000000 +#define CF_PRINT_EVERY_TIME 0x40000000 +#define CF_PRINT_AS_NEEDED 0x80000000 // means we have no clue, so assume EVERY TIME +#define CF_PRINT_MASK 0xf0000000 + +#define needs_for_select (PROC_FILLSTAT | PROC_FILLSTATUS) + +/* thread_flags */ +#define TF_B_H 0x0001 +#define TF_B_m 0x0002 +#define TF_U_m 0x0004 +#define TF_U_T 0x0008 +#define TF_U_L 0x0010 +#define TF_show_proc 0x0100 // show the summary line +#define TF_show_task 0x0200 // show the per-thread lines +#define TF_show_both 0x0400 // distinct proc/task format lists +#define TF_loose_tasks 0x0800 // let sorting break up task groups (BSDish) +#define TF_no_sort 0x1000 // don't know if thread-grouping should survive a sort +#define TF_no_forest 0x2000 // don't see how to do threads w/ forest option +#define TF_must_use 0x4000 // options only make sense if LWP/SPID column added + +/* personality control flags */ +#define PER_BROKEN_o 0x0001 +#define PER_BSD_h 0x0002 +#define PER_BSD_m 0x0004 +#define PER_IRIX_l 0x0008 +#define PER_FORCE_BSD 0x0010 +#define PER_GOOD_o 0x0020 +#define PER_OLD_m 0x0040 +#define PER_NO_DEFAULT_g 0x0080 +#define PER_ZAP_ADDR 0x0100 +#define PER_SANE_USER 0x0200 +#define PER_HPUX_x 0x0400 +#define PER_SVR4_x 0x0800 +#define PER_BSD_COLS 0x1000 +#define PER_UNIX_COLS 0x2000 + +/* Simple selections by bit mask */ +#define SS_B_x 0x01 +#define SS_B_g 0x02 +#define SS_U_d 0x04 +#define SS_U_a 0x08 +#define SS_B_a 0x10 + +/* predefined format flags such as: -l -f l u s -j */ +#define FF_Uf 0x0001 /* -f */ +#define FF_Uj 0x0002 /* -j */ +#define FF_Ul 0x0004 /* -l */ +#define FF_Bj 0x0008 /* j */ +#define FF_Bl 0x0010 /* l */ +#define FF_Bs 0x0020 /* s */ +#define FF_Bu 0x0040 /* u */ +#define FF_Bv 0x0080 /* v */ +#define FF_LX 0x0100 /* X */ +#define FF_Lm 0x0200 /* m */ /* overloaded: threads, sort, format */ +#define FF_Fc 0x0400 /* --context */ /* Flask security context format */ + +/* predefined format modifier flags such as: -l -f l u s -j */ +#define FM_c 0x0001 /* -c */ +#define FM_j 0x0002 /* -j */ /* only set when !sysv_j_format */ +#define FM_y 0x0004 /* -y */ +//#define FM_L 0x0008 /* -L */ +#define FM_P 0x0010 /* -P */ +#define FM_M 0x0020 /* -M */ +//#define FM_T 0x0040 /* -T */ +#define FM_F 0x0080 /* -F */ /* -F also sets the regular -f flags */ + +/* sorting & formatting */ +/* U,B,G is Unix,BSD,Gnu and then there is the option itself */ +#define SF_U_O 1 +#define SF_U_o 2 +#define SF_B_O 3 +#define SF_B_o 4 +#define SF_B_m 5 /* overloaded: threads, sort, format */ +#define SF_G_sort 6 +#define SF_G_format 7 + +/* headers */ +#define HEAD_SINGLE 0 /* default, must be 0 */ +#define HEAD_NONE 1 +#define HEAD_MULTI 2 + + +/********************** GENERAL TYPEDEF *******************/ + +/* Other fields that might be useful: + * + * char *name; user-defined column name (format specification) + * int reverse; sorting in reverse (sort specification) + * + * name in place of u + * reverse in place of n + */ + +typedef union sel_union { + pid_t pid; + pid_t ppid; + uid_t uid; + gid_t gid; + dev_t tty; + char cmd[16]; /* this is _not_ \0 terminated */ +} sel_union; + +typedef struct selection_node { + struct selection_node *next; + sel_union *u; /* used if selection type has a list of values */ + int n; /* used if selection type has a list of values */ + int typecode; +} selection_node; + +typedef struct sort_node { + struct sort_node *next; + int (*sr)(const proc_t* P, const proc_t* Q); /* sort function */ + int reverse; /* can sort backwards */ + int typecode; + int need; +} sort_node; + +typedef struct format_node { + struct format_node *next; + char *name; /* user can override default name */ + int (*pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function +/* int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */ + int width; + int need; + int vendor; /* Vendor that invented this */ + int flags; + int typecode; +} format_node; + +typedef struct format_struct { + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ + int (* const pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function + int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */ + const int width; + const int need; /* data we will need (files to read, etc.) */ + const int vendor; /* Where does this come from? */ + const int flags; +} format_struct; + +/* though ps-specific, needed by general file */ +typedef struct macro_struct { + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ +} macro_struct; + +/**************** PS TYPEDEF ***********************/ + +typedef struct aix_struct { + const int desc; /* 1-character format code */ + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ +} aix_struct; + +typedef struct shortsort_struct { + const int desc; /* 1-character format code */ + const char *spec; /* format specifier */ +} shortsort_struct; + +/* Save these options for later: -o o -O O --format --sort */ +typedef struct sf_node { + struct sf_node *next; /* next arg */ + format_node *f_cooked; /* convert each arg alone, then merge */ + sort_node *s_cooked; /* convert each arg alone, then merge */ + char *sf; + int sf_code; +} sf_node; + +/********************* UNDECIDED GLOBALS **************/ + +/* output.c */ +extern void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt); +extern void print_format_specifiers(void); +extern const aix_struct *search_aix_array(const int findme); +extern const shortsort_struct *search_shortsort_array(const int findme); +extern const format_struct *search_format_array(const char *findme); +extern const macro_struct *search_macro_array(const char *findme); +extern void init_output(void); +extern int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp); + +/* global.c */ +extern void reset_global(void); + +/* global.c */ +extern int all_processes; +extern const char *bsd_j_format; +extern const char *bsd_l_format; +extern const char *bsd_s_format; +extern const char *bsd_u_format; +extern const char *bsd_v_format; +extern int bsd_c_option; +extern int bsd_e_option; +extern uid_t cached_euid; +extern dev_t cached_tty; +extern char forest_prefix[4 * 32*1024 + 100]; +extern int forest_type; +extern unsigned format_flags; /* -l -f l u s -j... */ +extern format_node *format_list; /* digested formatting options */ +extern unsigned format_modifiers; /* -c -j -y -P -L... */ +extern int header_gap; +extern int header_type; /* none, single, multi... */ +extern int include_dead_children; +extern int lines_to_next_header; +extern int max_line_width; +extern const char *namelist_file; +extern int negate_selection; +extern int page_size; // "int" for math reasons? +extern unsigned personality; +extern int prefer_bsd_defaults; +extern int running_only; +extern int screen_cols; +extern int screen_rows; +extern unsigned long seconds_since_boot; +extern selection_node *selection_list; +extern unsigned simple_select; +extern sort_node *sort_list; +extern const char *sysv_f_format; +extern const char *sysv_fl_format; +extern const char *sysv_j_format; +extern const char *sysv_l_format; +extern unsigned thread_flags; +extern int unix_f_option; +extern int user_is_number; +extern int wchan_is_number; + +/************************* PS GLOBALS *********************/ + +/* sortformat.c */ +extern int defer_sf_option(const char *arg, int source); +extern const char *process_sf_options(int localbroken); +extern void reset_sortformat(void); + +/* select.c */ +extern int want_this_proc(proc_t *buf); +extern const char *select_bits_setup(void); + +/* help.c */ +extern const char *help_message; + +/* global.c */ +extern void self_info(void); + +/* parser.c */ +extern int arg_parse(int argc, char *argv[]); + +#endif diff --git a/ps/display.c b/ps/display.c new file mode 100644 index 0000000..4574b9c --- /dev/null +++ b/ps/display.c @@ -0,0 +1,594 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include + +#if (__GNU_LIBRARY__ >= 6) +# include +#endif + +/* username lookups */ +#include +#include +#include + +/* major/minor number */ +#include + +#include /* catch signals */ + +#include "common.h" +#include "../proc/wchan.h" +#include "../proc/version.h" +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "../proc/sig.h" + +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif + +/* just reports a crash */ +static void signal_handler(int signo){ + if(signo==SIGPIPE) _exit(0); /* "ps | head" will cause this */ + /* fprintf() is not reentrant, but we _exit() anyway */ + fprintf(stderr, + "\n\n" + "Signal %d (%s) caught by ps (%s).\n" + "Please send bug reports to or \n", + signo, + signal_number_to_name(signo), + procps_version + ); + _exit(signo+128); +} + +///////////////////////////////////////////////////////////////////////////////////// +#undef DEBUG +#ifdef DEBUG +void init_stack_trace(char *prog_name); + +#include + +void hex_dump(void *vp){ + char *charlist; + int i = 0; + int line = 45; + char *cp = (char *)vp; + + while(line--){ + printf("%8lx ", (unsigned long)cp); + charlist = cp; + cp += 16; + for(i=0; i<16; i++){ + if((charlist[i]>31) && (charlist[i]<127)){ + printf("%c", charlist[i]); + }else{ + printf("."); + } + } + printf(" "); + for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i]))); + printf("\n"); + i=0; + } +} + +static void show_tgid(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d,", data[n].tgid); + } + printf("%d\n", data[0].tgid); +} + +static void show_uid(char *s, int n, sel_union *data){ + struct passwd *pw_data; + printf("%s ", s); + while(--n){ + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s,", pw_data->pw_name); + else printf("%d,", data[n].uid); + } + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s\n", pw_data->pw_name); + else printf("%d\n", data[n].uid); +} + +static void show_gid(char *s, int n, sel_union *data){ + struct group *gr_data; + printf("%s ", s); + while(--n){ + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s,", gr_data->gr_name); + else printf("%d,", data[n].gid); + } + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s\n", gr_data->gr_name); + else printf("%d\n", data[n].gid); +} + +static void show_tty(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty)); + } + printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty)); +} + +static void show_cmd(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%.8s,", data[n].cmd); + } + printf("%.8s\n", data[0].cmd); +} + +static void arg_show(void){ + selection_node *walk = selection_list; + while(walk){ + switch(walk->typecode){ + case SEL_RUID: show_uid("RUID", walk->n, walk->u); break; + case SEL_EUID: show_uid("EUID", walk->n, walk->u); break; + case SEL_SUID: show_uid("SUID", walk->n, walk->u); break; + case SEL_FUID: show_uid("FUID", walk->n, walk->u); break; + case SEL_RGID: show_gid("RGID", walk->n, walk->u); break; + case SEL_EGID: show_gid("EGID", walk->n, walk->u); break; + case SEL_SGID: show_gid("SGID", walk->n, walk->u); break; + case SEL_FGID: show_gid("FGID", walk->n, walk->u); break; + case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break; + case SEL_PID : show_pid("PID ", walk->n, walk->u); break; + case SEL_PPID: show_pid("PPID", walk->n, walk->u); break; + case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break; + case SEL_SESS: show_pid("SESS", walk->n, walk->u); break; + case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break; + default: printf("Garbage typecode value!\n"); + } + walk = walk->next; + } +} + +#endif +////////////////////////////////////////////////////////////////////////// + + +/***** check the header */ +/* Unix98: must not print empty header */ +static void check_headers(void){ + format_node *walk = format_list; + int head_normal = 0; + if(header_type==HEAD_MULTI){ + header_gap = screen_rows-1; /* true BSD */ + return; + } + if(header_type==HEAD_NONE){ + lines_to_next_header = -1; /* old Linux */ + return; + } + while(walk){ + if(!*(walk->name)){ + walk = walk->next; + continue; + } + if(walk->pr){ + head_normal++; + walk = walk->next; + continue; + } + walk = walk->next; + } + if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */ +} + +/***** check sort needs */ +/* see what files need to be read, etc. */ +static unsigned check_sort_needs(sort_node *walk){ + unsigned needs = 0; + while(walk){ + needs |= walk->need; + walk = walk->next; + } + return needs; +} + +/***** check needs */ +/* see what files need to be read, etc. */ +static unsigned collect_format_needs(format_node *walk){ + unsigned needs = 0; + while(walk){ + needs |= walk->need; + walk = walk->next; + } + return needs; +} + +static format_node *proc_format_list; +static format_node *task_format_list; + +static unsigned needs_for_threads; +static unsigned needs_for_sort; +static unsigned proc_format_needs; +static unsigned task_format_needs; + +#define needs_for_format (proc_format_needs|task_format_needs) + +#define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM) + +/***** munge lists and determine openproc() flags */ +static void lists_and_needs(void){ + check_headers(); + + // only care about the difference when showing both + if(thread_flags & TF_show_both){ + format_node pfn, tfn; // junk, to handle special case at begin of list + format_node *walk = format_list; + format_node *p_end = &pfn; + format_node *t_end = &tfn; + while(walk){ + format_node *new = malloc(sizeof(format_node)); + memcpy(new,walk,sizeof(format_node)); + p_end->next = walk; + t_end->next = new; + p_end = walk; + t_end = new; + switch(walk->flags & CF_PRINT_MASK){ + case CF_PRINT_THREAD_ONLY: + p_end->pr = pr_nop; + p_end->need = 0; + break; + case CF_PRINT_PROCESS_ONLY: + t_end->pr = pr_nop; + t_end->need = 0; + break; + default: + fprintf(stderr, "please report this bug\n"); + // FALL THROUGH + case CF_PRINT_AS_NEEDED: + case CF_PRINT_EVERY_TIME: + break; + } + walk = walk->next; + } + t_end->next = NULL; + p_end->next = NULL; + proc_format_list = pfn.next; + task_format_list = tfn.next; + }else{ + proc_format_list = format_list; + task_format_list = format_list; + } + + proc_format_needs = collect_format_needs(proc_format_list); + task_format_needs = collect_format_needs(task_format_list); + + needs_for_sort = check_sort_needs(sort_list); + + // move process-only flags to the process + proc_format_needs |= (task_format_needs &~ PROC_ONLY_FLAGS); + task_format_needs &= ~PROC_ONLY_FLAGS; + + if(bsd_c_option){ + proc_format_needs &= ~PROC_FILLARG; + needs_for_sort &= ~PROC_FILLARG; + } + if(!unix_f_option){ + proc_format_needs &= ~PROC_FILLCOM; + needs_for_sort &= ~PROC_FILLCOM; + } + // convert ARG to COM as a standard + if(proc_format_needs & PROC_FILLARG){ + proc_format_needs |= PROC_FILLCOM; + proc_format_needs &= ~PROC_FILLARG; + } + if(bsd_e_option){ + if(proc_format_needs&PROC_FILLCOM) proc_format_needs |= PROC_FILLENV; + } + + /* FIXME broken filthy hack -- got to unify some stuff here */ + if( ( (proc_format_needs|task_format_needs|needs_for_sort) & PROC_FILLWCHAN) && !wchan_is_number) + if (open_psdb(namelist_file)) wchan_is_number = 1; + + if(thread_flags&TF_loose_tasks) needs_for_threads |= PROC_LOOSE_TASKS; +} + +////////////////////////////////////////////////////////////////////////// + +/***** fill in %CPU; not in libproc because of include_dead_children */ +/* Note: for sorting, not display, so 0..0x7fffffff would be OK */ +static int want_this_proc_pcpu(proc_t *buf){ + unsigned long long used_jiffies; + unsigned long pcpu = 0; + unsigned long long avail_jiffies; + + if(!want_this_proc(buf)) return 0; + + used_jiffies = buf->utime + buf->stime; + if(include_dead_children) used_jiffies += (buf->cutime + buf->cstime); + + avail_jiffies = seconds_since_boot * Hertz - buf->start_time; + if(avail_jiffies) pcpu = (used_jiffies << 24) / avail_jiffies; + + buf->pcpu = pcpu; // fits in an int, summing children on 128 CPUs + + return 1; +} + +/***** just display */ +static void simple_spew(void){ + proc_t buf; + PROCTAB* ptp; + ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + memset(&buf, '#', sizeof(proc_t)); + switch(thread_flags & (TF_show_proc|TF_loose_tasks|TF_show_task)){ + case TF_show_proc: // normal non-thread output + while(readproc(ptp,&buf)){ + if(want_this_proc(&buf)){ + show_one_proc(&buf, proc_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + case TF_show_proc|TF_loose_tasks: // H option + while(readproc(ptp,&buf)){ + proc_t buf2; + // must still have the process allocated + while(readtask(ptp,&buf,&buf2)){ + if(!want_this_proc(&buf)) continue; + show_one_proc(&buf2, task_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + case TF_show_proc|TF_show_task: // m and -m options + while(readproc(ptp,&buf)){ + if(want_this_proc(&buf)){ + proc_t buf2; + show_one_proc(&buf, proc_format_list); + // must still have the process allocated + while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + case TF_show_task: // -L and -T options + while(readproc(ptp,&buf)){ + if(want_this_proc(&buf)){ + proc_t buf2; + // must still have the process allocated + while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list); + } + if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse + if(buf.environ) free((void*)*buf.environ); // ought to reuse + } + break; + } + closeproc(ptp); +} + +/***** forest output requires sorting by ppid; add start_time by default */ +static void prep_forest_sort(void){ + sort_node *tmp_list = sort_list; + const format_struct *incoming; + + if(!sort_list) { /* assume start time order */ + incoming = search_format_array("start_time"); + if(!incoming) fprintf(stderr, "Could not find start_time!\n"); + tmp_list = malloc(sizeof(sort_node)); + tmp_list->reverse = 0; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->need = incoming->need; + tmp_list->next = sort_list; + sort_list = tmp_list; + } + /* this is required for the forest option */ + incoming = search_format_array("ppid"); + if(!incoming) fprintf(stderr, "Could not find ppid!\n"); + tmp_list = malloc(sizeof(sort_node)); + tmp_list->reverse = 0; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->need = incoming->need; + tmp_list->next = sort_list; + sort_list = tmp_list; +} + +/* we rely on the POSIX requirement for zeroed memory */ +//static proc_t *processes[98*1024]; // FIXME +static proc_t **processes; + +/***** compare function for qsort */ +static int compare_two_procs(const void *a, const void *b){ + sort_node *tmp_list = sort_list; + while(tmp_list){ + int result; + result = (*tmp_list->sr)(*(const proc_t *const*)a, *(const proc_t *const*)b); + if(result) return (tmp_list->reverse) ? -result : result; + tmp_list = tmp_list->next; + } + return 0; /* no conclusion */ +} + +/***** show pre-sorted array of process pointers */ +static void show_proc_array(PROCTAB *restrict ptp, int n){ + proc_t **p = processes; + while(n--){ + if(thread_flags & TF_show_proc) show_one_proc(*p, proc_format_list); + if(thread_flags & TF_show_task){ + proc_t buf2; + // must still have the process allocated + while(readtask(ptp,*p,&buf2)) show_one_proc(&buf2, task_format_list); + // must not attempt to free cmdline and environ + } + /* no point freeing any of this -- won't need more mem */ +// if((*p)->cmdline) free((void*)*(*p)->cmdline); +// if((*p)->environ) free((void*)*(*p)->environ); +// memset(*p, '%', sizeof(proc_t)); /* debug */ +// free(*p); + p++; + } +} + +/***** show tree */ +/* this needs some optimization work */ +#define ADOPTED(x) 1 +static void show_tree(const int self, const int n, const int level, const int have_sibling){ + int i = 0; + if(level){ + /* add prefix of "+" or "L" */ + if(have_sibling) forest_prefix[level-1] = '+'; + else forest_prefix[level-1] = 'L'; + forest_prefix[level] = '\0'; + } + show_one_proc(processes[self],format_list); /* first show self */ + /* no point freeing any of this -- won't need more mem */ +// if(processes[self]->cmdline) free((void*)*processes[self]->cmdline); +// if(processes[self]->environ) free((void*)*processes[self]->environ); + for(;;){ /* look for children */ + if(i >= n) return; /* no children */ + if(processes[i]->ppid == processes[self]->XXXID) break; + i++; + } + if(level){ + /* change our prefix to "|" or " " for the children */ + if(have_sibling) forest_prefix[level-1] = '|'; + else forest_prefix[level-1] = ' '; + forest_prefix[level] = '\0'; + } + for(;;){ + int self_pid; + int more_children = 1; + if(i >= n) break; /* over the edge */ + self_pid=processes[self]->XXXID; + if(i+1 >= n) + more_children = 0; + else + if(processes[i+1]->ppid != self_pid) more_children = 0; + if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u') + show_tree(i++, n, level, more_children); + else + show_tree(i++, n, level+1, more_children); + if(!more_children) break; + } + /* chop prefix that children added -- do we need this? */ + forest_prefix[level] = '\0'; +// memset(processes[self], '$', sizeof(proc_t)); /* debug */ +} + +/***** show forest */ +static void show_forest(const int n){ + int i = n; + int j; + while(i--){ /* cover whole array looking for trees */ + j = n; + while(j--){ /* search for parent: if none, i is a tree! */ + if(processes[j]->XXXID == processes[i]->ppid) goto not_root; + } + show_tree(i,n,0,0); +not_root: + ; + } + /* don't free the array because it takes time and ps will exit anyway */ +} + +static int want_this_proc_nop(proc_t *dummy){ + (void)dummy; + return 1; +} + +/***** sorted or forest */ +static void fancy_spew(void){ + proc_data_t *pd = NULL; + PROCTAB *restrict ptp; + int n = 0; /* number of processes & index into array */ + + ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + + if(thread_flags & TF_loose_tasks){ + pd = readproctab2(want_this_proc_nop, want_this_proc_pcpu, ptp); + }else{ + pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp); + } + n = pd->n; + processes = pd->tab; + + if(!n) return; /* no processes */ + if(forest_type) prep_forest_sort(); + qsort(processes, n, sizeof(proc_t*), compare_two_procs); + if(forest_type) show_forest(n); + else show_proc_array(ptp,n); + closeproc(ptp); +} + + +/***** no comment */ +int main(int argc, char *argv[]){ +#if (__GNU_LIBRARY__ >= 6) + setlocale (LC_CTYPE, ""); +#endif + +#ifdef DEBUG + init_stack_trace(argv[0]); +#else + do { + struct sigaction sa; + int i = 32; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigfillset(&sa.sa_mask); + while(i--) switch(i){ + default: + sigaction(i,&sa,NULL); + case 0: + case SIGINT: /* ^C */ + case SIGTSTP: /* ^Z */ + case SIGTTOU: /* see stty(1) man page */ + case SIGQUIT: /* ^\ */ + case SIGPROF: /* profiling */ + case SIGKILL: /* can not catch */ + case SIGSTOP: /* can not catch */ + case SIGWINCH: /* don't care if window size changes */ + ; + } + } while (0); +#endif + + reset_global(); /* must be before parser */ + arg_parse(argc,argv); + +/* arg_show(); */ + trace("screen is %ux%u\n",screen_cols,screen_rows); +/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */ + trace("======= ps output follows =======\n"); + + init_output(); /* must be between parser and output */ + + lists_and_needs(); + + if(forest_type || sort_list) fancy_spew(); /* sort or forest */ + else simple_spew(); /* no sort, no forest */ + show_one_proc((proc_t *)-1,format_list); /* no output yet? */ + return 0; +} diff --git a/ps/global.c b/ps/global.c new file mode 100644 index 0000000..0b3bc5b --- /dev/null +++ b/ps/global.c @@ -0,0 +1,498 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "common.h" + +#include +#include "../proc/wchan.h" +#include "../proc/version.h" +#include "../proc/sysinfo.h" + + +#ifndef __GNU_LIBRARY__ +#define __GNU_LIBRARY__ -1 +#endif +#ifndef __GLIBC__ +#define __GLIBC__ -1 +#endif +#ifndef __GLIBC_MINOR__ +#define __GLIBC_MINOR__ -1 +#endif + + +static const char * saved_personality_text = "You found a bug!"; + +int all_processes = -1; +const char *bsd_j_format = (const char *)0xdeadbeef; +const char *bsd_l_format = (const char *)0xdeadbeef; +const char *bsd_s_format = (const char *)0xdeadbeef; +const char *bsd_u_format = (const char *)0xdeadbeef; +const char *bsd_v_format = (const char *)0xdeadbeef; +int bsd_c_option = -1; +int bsd_e_option = -1; +uid_t cached_euid = -1; +dev_t cached_tty = -1; +char forest_prefix[4 * 32*1024 + 100]; // FIXME +int forest_type = -1; +unsigned format_flags = 0xffffffff; /* -l -f l u s -j... */ +format_node *format_list = (format_node *)0xdeadbeef; /* digested formatting options */ +unsigned format_modifiers = 0xffffffff; /* -c -j -y -P -L... */ +int header_gap = -1; +int header_type = -1; +int include_dead_children = -1; +int lines_to_next_header = -1; +const char *namelist_file = (const char *)0xdeadbeef; +int negate_selection = -1; +int running_only = -1; +int page_size = -1; // "int" for math reasons? +unsigned personality = 0xffffffff; +int prefer_bsd_defaults = -1; +int screen_cols = -1; +int screen_rows = -1; +unsigned long seconds_since_boot = -1; +selection_node *selection_list = (selection_node *)0xdeadbeef; +unsigned simple_select = 0xffffffff; +sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */ +const char *sysv_f_format = (const char *)0xdeadbeef; +const char *sysv_fl_format = (const char *)0xdeadbeef; +const char *sysv_j_format = (const char *)0xdeadbeef; +const char *sysv_l_format = (const char *)0xdeadbeef; +unsigned thread_flags = 0xffffffff; +int unix_f_option = -1; +int user_is_number = -1; +int wchan_is_number = -1; + + +static void reset_selection_list(void){ + selection_node *old; + selection_node *walk = selection_list; + if(selection_list == (selection_node *)0xdeadbeef){ + selection_list = NULL; + return; + } + while(walk){ + old = walk; + walk = old->next; + free(old->u); + free(old); + } + selection_list = NULL; +} + +// The rules: +// 1. Defaults are implementation-specific. (ioctl,termcap,guess) +// 2. COLUMNS and LINES override the defaults. (standards compliance) +// 3. Command line options override everything else. +// 4. Actual output may be more if the above is too narrow. +// +// SysV tends to spew semi-wide output in all cases. The args +// will be limited to 64 or 80 characters, without regard to +// screen size. So lines of 120 to 160 chars are normal. +// Tough luck if you want more or less than that! HP-UX has a +// new "-x" option for 1024-char args in place of comm that +// we'll implement at some point. +// +// BSD tends to make a good effort, then fall back to 80 cols. +// Use "ww" to get infinity. This is nicer for "ps | less" +// and "watch ps". It can run faster too. +static void set_screen_size(void){ + struct winsize ws; + char *columns; /* Unix98 environment variable */ + char *lines; /* Unix98 environment variable */ + + do{ + int fd; + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break; + if(ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break; + if(ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break; + fd = open("/dev/tty", O_NOCTTY|O_NONBLOCK|O_RDONLY); + if(fd != -1){ + int ret = ioctl(fd, TIOCGWINSZ, &ws); + close(fd); + if(ret != -1 && ws.ws_col>0 && ws.ws_row>0) break; + } + // TODO: ought to do tgetnum("co") and tgetnum("li") here + ws.ws_col = 80; + ws.ws_row = 24; + }while(0); + screen_cols = ws.ws_col; // hmmm, NetBSD subtracts 1 + screen_rows = ws.ws_row; + + // TODO: delete this line + if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE; + + columns = getenv("COLUMNS"); + if(columns && *columns){ + long t; + char *endptr; + t = strtol(columns, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t; + } + + lines = getenv("LINES"); + if(lines && *lines){ + long t; + char *endptr; + t = strtol(lines, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t; + } + + if((screen_cols<9) || (screen_rows<2)) + fprintf(stderr,"Your %dx%d screen size is bogus. Expect trouble.\n", + screen_cols, screen_rows + ); +} + +/**************** personality control **************/ + +typedef struct personality_table_struct { + const char *name; /* personality name */ + const void *jump; /* See gcc extension info. :-) */ +} personality_table_struct; + +static int compare_personality_table_structs(const void *a, const void *b){ + return strcasecmp(((const personality_table_struct*)a)->name,((const personality_table_struct*)b)->name); +} + +static const char *set_personality(void){ + const char *s; + size_t sl; + char buf[16]; + personality_table_struct findme = { buf, NULL}; + personality_table_struct *found; + static const personality_table_struct personality_table[] = { + {"390", &&case_390}, + {"aix", &&case_aix}, + {"bsd", &&case_bsd}, + {"compaq", &&case_compaq}, + {"debian", &&case_debian}, + {"default", &&case_default}, + {"digital", &&case_digital}, + {"gnu", &&case_gnu}, + {"hp", &&case_hp}, + {"hpux", &&case_hpux}, + {"irix", &&case_irix}, + {"linux", &&case_linux}, + {"old", &&case_old}, + {"os390", &&case_os390}, + {"posix", &&case_posix}, + {"s390", &&case_s390}, + {"sco", &&case_sco}, + {"sgi", &&case_sgi}, + {"solaris2", &&case_solaris2}, + {"sunos4", &&case_sunos4}, + {"svr4", &&case_svr4}, + {"sysv", &&case_sysv}, + {"tru64", &&case_tru64}, + {"unix", &&case_unix}, + {"unix95", &&case_unix95}, + {"unix98", &&case_unix98}, + {"unknown", &&case_unknown} + }; + const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct); + + personality = 0; + prefer_bsd_defaults = 0; + + bsd_j_format = "OL_j"; + bsd_l_format = "OL_l"; + bsd_s_format = "OL_s"; + bsd_u_format = "OL_u"; + bsd_v_format = "OL_v"; + + /* When these are NULL, the code does SysV output modifier logic */ + sysv_f_format = NULL; + sysv_fl_format = NULL; + sysv_j_format = NULL; + sysv_l_format = NULL; + + s = getenv("PS_PERSONALITY"); + if(!s || !*s) s = getenv("CMD_ENV"); + if(!s || !*s) s="unknown"; /* "Do The Right Thing[tm]" */ + if(getenv("I_WANT_A_BROKEN_PS")) s="old"; + sl = strlen(s); + if(sl > 15) return "Environment specified an unknown personality."; + strncpy(buf, s, sl); + buf[sl] = '\0'; + saved_personality_text = strdup(buf); + + found = bsearch(&findme, personality_table, personality_table_count, + sizeof(personality_table_struct), compare_personality_table_structs + ); + + if(!found) return "Environment specified an unknown personality."; + + goto *(found->jump); /* See gcc extension info. :-) */ + + case_bsd: + personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m; + prefer_bsd_defaults = 1; + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_old: + personality = PER_FORCE_BSD | PER_OLD_m; + prefer_bsd_defaults = 1; + return NULL; + + case_debian: /* Toss this? They don't seem to care much. */ + case_gnu: + personality = PER_GOOD_o | PER_OLD_m; + prefer_bsd_defaults = 1; + sysv_f_format = "RD_f"; + /* sysv_fl_format = "RD_fl"; */ /* old Debian ps can't do this! */ + sysv_j_format = "RD_j"; + sysv_l_format = "RD_l"; + return NULL; + + case_linux: + personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER; + return NULL; + + case_default: /* use defaults for ps, ignoring other environment variables */ + return NULL; + + case_unknown: /* defaults, but also check inferior environment variables */ + if( + getenv("UNIX95") /* Irix */ + || getenv("POSIXLY_CORRECT") /* most gnu stuff */ + || (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */ + ) personality = PER_BROKEN_o; + return NULL; + + case_aix: + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_tru64: + case_compaq: + case_digital: + // no PER_NO_DEFAULT_g even though man page claims it + // Reality: the g is a NOP + personality = PER_GOOD_o | PER_BSD_h; + prefer_bsd_defaults = 1; + sysv_f_format = "F5FMT"; + sysv_fl_format = "FL5FMT"; + sysv_j_format = "JFMT"; + sysv_l_format = "L5FMT"; + bsd_j_format = "JFMT"; + bsd_l_format = "LFMT"; + bsd_s_format = "SFMT"; + bsd_u_format = "UFMT"; + bsd_v_format = "VFMT"; + return NULL; + + case_sunos4: + personality = PER_NO_DEFAULT_g; + prefer_bsd_defaults = 1; + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_irix: + case_sgi: + s = getenv("_XPG"); + if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o; + else personality = PER_IRIX_l; + return NULL; + + case_os390: /* IBM's OS/390 OpenEdition on the S/390 mainframe */ + case_s390: + case_390: + sysv_j_format = "J390"; /* don't know what -jl and -jf do */ + return NULL; + + case_hp: + case_hpux: + personality = PER_BROKEN_o | PER_HPUX_x; + return NULL; + + case_svr4: + case_sysv: + case_sco: + personality = PER_BROKEN_o | PER_SVR4_x; + return NULL; + + case_posix: + case_solaris2: + case_unix95: + case_unix98: + case_unix: + personality = PER_BROKEN_o; + return NULL; +} + + +/************ Call this to reinitialize everything ***************/ +void reset_global(void){ + static proc_t p; + reset_selection_list(); + look_up_our_self(&p); + set_screen_size(); + set_personality(); + + all_processes = 0; + bsd_c_option = 0; + bsd_e_option = 0; + cached_euid = geteuid(); + cached_tty = p.tty; +/* forest_prefix must be all zero because of POSIX */ + forest_type = 0; + format_flags = 0; /* -l -f l u s -j... */ + format_list = NULL; /* digested formatting options */ + format_modifiers = 0; /* -c -j -y -P -L... */ + header_gap = -1; /* send lines_to_next_header to -infinity */ + header_type = HEAD_SINGLE; + include_dead_children = 0; + lines_to_next_header = 1; + namelist_file = NULL; + negate_selection = 0; + page_size = getpagesize(); + running_only = 0; + seconds_since_boot = uptime(0,0); + selection_list = NULL; + simple_select = 0; + sort_list = NULL; + thread_flags = 0; + unix_f_option = 0; + user_is_number = 0; + wchan_is_number = 0; +} + +static const char archdefs[] = +#ifdef __alpha__ +" alpha" +#endif +#ifdef __arm__ +" arm" +#endif +#ifdef __hppa__ +" hppa" +#endif +#ifdef __i386__ +" i386" +#endif +#ifdef __ia64__ +" ia64" +#endif +#ifdef __mc68000__ +" mc68000" +#endif +#ifdef __mips64__ +" mips64" +#endif +#ifdef __mips__ +" mips" +#endif +#ifdef __powerpc__ +" powerpc" +#endif +#ifdef __sh3__ +" sh3" +#endif +#ifdef __sh__ +" sh" +#endif +#ifdef __sparc__ +" sparc" +#endif +#ifdef __sparc_v9__ +" sparc_v9" +#endif +#ifdef __x86_64__ +" x86_64" +#endif +""; + +/*********** spew variables ***********/ +void self_info(void){ + fprintf(stderr, + "BSD j %s\n" + "BSD l %s\n" + "BSD s %s\n" + "BSD u %s\n" + "BSD v %s\n" + "SysV -f %s\n" + "SysV -fl %s\n" + "SysV -j %s\n" + "SysV -l %s\n" + "\n", + bsd_j_format ? bsd_j_format : "(none)", + bsd_l_format ? bsd_l_format : "(none)", + bsd_s_format ? bsd_s_format : "(none)", + bsd_u_format ? bsd_u_format : "(none)", + bsd_v_format ? bsd_v_format : "(none)", + sysv_f_format ? sysv_f_format : "(none)", + sysv_fl_format ? sysv_fl_format : "(none)", + sysv_j_format ? sysv_j_format : "(none)", + sysv_l_format ? sysv_l_format : "(none)" + ); + + display_version(); + fprintf(stderr, "Linux version %d.%d.%d\n", + LINUX_VERSION_MAJOR(linux_version_code), + LINUX_VERSION_MINOR(linux_version_code), + LINUX_VERSION_PATCH(linux_version_code) + ); + /* __libc_print_version(); */ /* how can we get the run-time version? */ + fprintf(stderr, "Compiled with: glibc %d.%d, gcc %d.%d\n\n", + __GLIBC__, __GLIBC_MINOR__, __GNUC__, __GNUC_MINOR__ + ); + + fprintf(stderr, + "header_gap=%d lines_to_next_header=%d\n" + "screen_cols=%d screen_rows=%d\n" + "\n", + header_gap, lines_to_next_header, + screen_cols, screen_rows + ); + + fprintf(stderr, + "personality=0x%08x (from \"%s\")\n" + "EUID=%d TTY=%d,%d Hertz=%Ld page_size=%d\n", + personality, saved_personality_text, + cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz, + (int)(page_size) + ); + + fprintf(stderr, + "sizeof(proc_t)=%d sizeof(long)=%d sizeof(KLONG)=%d\n", + (int)sizeof(proc_t), (int)sizeof(long), (int)sizeof(KLONG) + ); + + fprintf(stderr, "archdefs:%s\n", archdefs); + + open_psdb(namelist_file); + fprintf(stderr,"namelist_file=\"%s\"\n",namelist_file?namelist_file:""); +} diff --git a/ps/help.c b/ps/help.c new file mode 100644 index 0000000..2f39a9d --- /dev/null +++ b/ps/help.c @@ -0,0 +1,48 @@ +/* + * Copyright 1998-2004 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* + * The help message must not become longer, because it must fit + * on an 80x24 screen _with_ the error message and command prompt. + */ + +const char *help_message = +"********* simple selection ********* ********* selection by list *********\n" +"-A all processes -C by command name\n" +"-N negate selection -G by real group ID (supports names)\n" +"-a all w/ tty except session leaders -U by real user ID (supports names)\n" +"-d all except session leaders -g by session OR by effective group name\n" +"-e all processes -p by process ID\n" +"T all processes on this terminal -s processes in the sessions given\n" +"a all w/ tty, including other users -t by tty\n" +"g OBSOLETE -- DO NOT USE -u by effective user ID (supports names)\n" +"r only running processes U processes for specified users\n" +"x processes w/o controlling ttys t by tty\n" +"*********** output format ********** *********** long options ***********\n" +"-o,o user-defined -f full --Group --User --pid --cols --ppid\n" +"-j,j job control s signal --group --user --sid --rows --info\n" +"-O,O preloaded -o v virtual memory --cumulative --format --deselect\n" +"-l,l long u user-oriented --sort --tty --forest --version\n" +"-F extra full X registers --heading --no-heading --context\n" +" ********* misc options *********\n" +"-V,V show version L list format codes f ASCII art forest\n" +"-m,m,-L,-T,H threads S children in sum -y change -l format\n" +"-M,Z security data c true command name -c scheduling class\n" +"-w,w wide output n numeric WCHAN,UID -H process hierarchy\n" +; + + + +/* Missing: + * + * -P e k + * + */ diff --git a/ps/it b/ps/it new file mode 100644 index 0000000..07fd6dc --- /dev/null +++ b/ps/it @@ -0,0 +1,35 @@ +From ddainese@dsi.unive.it Sun Apr 18 14:12:27 1999 + +here is a first translation of the text: +--------------------------------------------------------------------- +const char *help_message = +"****** seleziona i processi ******* * seleziona una lista specificando: *\n" +"-A tutti -C il nome del comando\n" +"-N nega la selezione -G il real group ID (supporta i nomi)\n" +"-a con tty, tranne i session leader -U il real user ID (supporta i nomi)\n" +"-d tutti, tranne i session leader -g il session leader OPPURE il gruppo\n" +"-e tutti -p l'ID del processo\n" +"T su questo terminale -s la sessione\n" +"a con tty, di tutti gli utenti -t il tty\n" +"g tutti, anche i leader di gruppo -u l'effective user ID (supporta i nomi)\n" +"r in stato running U una lista di utenti\n" +"x senza tty t il tty\n" +"******** formato dell'output ****** ********** opzioni lunghe **********\n" +"-o,o definito dall'utente --Group --User --pid --cols\n" +"-j,j job s segnali --group --user --sid --rows\n" +"-O,O -o preimpostato v memoria virtuale --cumulative --format --deselect\n" +"-l,l lungo u utenti --sort --tty --forest --version\n" +"-f completo X registri --heading --no-heading\n" +" ******** opzioni varie *********\n" +"-V,V versione L codici di formato f foresta di ASCII\n" +"-m,m vista ad albero S figli in sum -y cambia il formato -l\n" +"-n,N namelist file c nome reale del comando n WCHAN,UID numerici\n" +"-w,w output ampio e mostra l'environment -H gerarchia dei processi\n" +; +--------------------------------------------------------------------- + +Unfortunately it isn't really understandable for a newbie, because +there is too little space for a good translation; to make it more +meaningful, I would need about an entire line for every option, thus +if you really want the help text stays under 22 lines, it must +contains only 22 options. What do you think about it? diff --git a/ps/module.mk b/ps/module.mk new file mode 100755 index 0000000..2902a3a --- /dev/null +++ b/ps/module.mk @@ -0,0 +1,40 @@ +# This file gets included into the main Makefile, in the top directory. + +INSTALL += $(bin)ps $(man1)ps.1 + +# files to remove +CLEAN += ps/ps ps/debug + +# a directory for cleaning +DIRS += ps/ + +# a file to create +ALL += ps/ps + +PS_C := display global help output parser select sortformat +PSNAMES := $(addprefix ps/,$(PS_C)) +PSOBJ := $(addsuffix .o,$(PSNAMES)) +PSSRC := $(addsuffix .c,$(PSNAMES)) + +PS_X := COPYING HACKING TRANSLATION common.h module.mk it p ps.1 regression +TARFILES += $(PSSRC) $(addprefix ps/,$(PS_X)) + +ps/ps: $(PSOBJ) $(LIBPROC) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ $(ldl) + +# This just adds the stacktrace code +ps/debug: $(PSOBJ) stacktrace.o $(LIBPROC) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ -lefence $(ldl) + +$(PSOBJ): %.o: %.c ps/common.h $(LIBPROC) + $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< -o $@ + +ps/stacktrace.o: ps/stacktrace.c + + +$(bin)ps: ps/ps + $(install) --mode a=rx $< $@ + +$(man1)ps.1 : ps/ps.1 + $(install) --mode a=r $< $@ + -rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz diff --git a/ps/output.c b/ps/output.c new file mode 100644 index 0000000..87bf9de --- /dev/null +++ b/ps/output.c @@ -0,0 +1,1969 @@ +/* + * Copyright 1999-2004 by Albert Cahalan; all rights reserved. + * + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* + * This file is really gross, and I know it. I looked into several + * alternate ways to deal with the mess, and they were all ugly. + * + * FreeBSD has a fancy hack using offsets into a struct -- that + * saves code but it is _really_ gross. See the PO macro below. + * + * We could have a second column width for wide output format. + * For example, Digital prints the real-time signals. + */ + + +/* + * Data table idea: + * + * table 1 maps aix to specifier + * table 2 maps shortsort to specifier + * table 3 maps macro to specifiers + * table 4 maps specifier to title,datatype,offset,vendor,helptext + * table 5 maps datatype to justification,width,widewidth,sorting,printing + * + * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty... + * It must be enough to determine printing and sorting. + * + * After the tables, increase width as needed to fit the header. + * + * Table 5 could go in a file with the output functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "../proc/wchan.h" +#include "../proc/procps.h" +#include "../proc/devname.h" +#include "../proc/escape.h" +#include "common.h" + +/* TODO: + * Stop assuming system time is local time. + */ + +#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */ + +static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */ +static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */ + + + +static int wide_signals; /* true if we have room */ + +static unsigned long seconds_since_1970; +static unsigned long time_of_boot; +static unsigned long page_shift; + + +/*************************************************************************/ +/************ Lots of sort functions, starting with the NOP **************/ + +static int sr_nop(const proc_t* a, const proc_t* b){ + (void)a;(void)b; /* shut up gcc */ + return 0; +} + +#define CMP_STR(NAME) \ +static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \ + return strcmp(P->NAME, Q->NAME); \ +} + +#define CMP_INT(NAME) \ +static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ + if (P->NAME < Q->NAME) return -1; \ + if (P->NAME > Q->NAME) return 1; \ + return 0; \ +} + +/* fast version, for values which either: + * a. differ by no more than 0x7fffffff + * b. only need to be grouped same w/ same + */ +#define CMP_SMALL(NAME) \ +static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ + return (int)(P->NAME) - (int)(Q->NAME); \ +} + +CMP_INT(rtprio) +CMP_SMALL(sched) +CMP_INT(cutime) +CMP_INT(cstime) +CMP_SMALL(priority) /* nice */ +CMP_SMALL(nlwp) +CMP_SMALL(nice) /* priority */ +CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */ +CMP_INT(alarm) +CMP_INT(size) /* total pages */ /* vm_size, vsize */ +CMP_INT(resident) /* resident pages */ /* vm_rss, rss */ +CMP_INT(share) /* shared pages */ +CMP_INT(trs) /* executable pages */ +CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */ +CMP_INT(drs) /* other pages (assumed data?) */ +CMP_INT(dt) /* dirty pages */ + +CMP_INT(vm_size) /* kB VM */ /* size, vsize */ +CMP_INT(vm_lock) /* kB locked */ +CMP_INT(vm_rss) /* kB rss */ /* rss, resident */ +CMP_INT(vm_data) /* kB "data" == data-stack */ +CMP_INT(vm_stack) /* kB stack */ +CMP_INT(vm_exe) /* kB "exec" == exec-lib */ +CMP_INT(vm_lib) /* kB "libraries" */ +CMP_INT(vsize) /* pages VM */ /* size, vm_size */ +CMP_INT(rss_rlim) +CMP_SMALL(flags) +CMP_INT(min_flt) +CMP_INT(maj_flt) +CMP_INT(cmin_flt) +CMP_INT(cmaj_flt) +CMP_INT(utime) +CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */ +CMP_INT(start_code) +CMP_INT(end_code) +CMP_INT(start_stack) +CMP_INT(kstk_esp) +CMP_INT(kstk_eip) +CMP_INT(start_time) +CMP_INT(wchan) + +/* CMP_STR(*environ) */ +/* CMP_STR(*cmdline) */ + +CMP_STR(ruser) +CMP_STR(euser) +CMP_STR(suser) +CMP_STR(fuser) +CMP_STR(rgroup) +CMP_STR(egroup) +CMP_STR(sgroup) +CMP_STR(fgroup) +CMP_STR(cmd) +/* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */ + +CMP_INT(ruid) +CMP_INT(rgid) +CMP_INT(euid) +CMP_INT(egid) +CMP_INT(suid) +CMP_INT(sgid) +CMP_INT(fuid) +CMP_INT(fgid) +CMP_SMALL(tid) +CMP_SMALL(tgid) +CMP_SMALL(ppid) +CMP_SMALL(pgrp) +CMP_SMALL(session) +CMP_INT(tty) +CMP_SMALL(tpgid) + +CMP_SMALL(pcpu) + +CMP_SMALL(state) + +/* approximation to: kB of address space that could end up in swap */ +static int sr_swapable(const proc_t* P, const proc_t* Q) { + unsigned long p_swapable = P->vm_data + P->vm_stack; + unsigned long q_swapable = Q->vm_data + Q->vm_stack; + if (p_swapable < q_swapable) return -1; + if (p_swapable > q_swapable) return 1; + return 0; +} + + +/***************************************************************************/ +/************ Lots of format functions, starting with the NOP **************/ + +// so popular it can't be "static" +int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){ + (void)pp; + return snprintf(outbuf, COLWID, "%c", '-'); +} + + +/********* Unix 98 ************/ + +/*** + +Only comm and args are allowed to contain blank characters; all others are +not. Any implementation-dependent variables will be specified in the system +documentation along with the default header and indicating if the field +may contain blank characters. + +Some headers do not have a standardized specifier! + +%CPU pcpu The % of cpu time used recently, with unspecified "recently". +ADDR The address of the process. +C Processor utilisation for scheduling. +CMD The command name, or everything with -f. +COMMAND args Command + args. May chop as desired. May use either version. +COMMAND comm argv[0] +ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss +F Flags (octal and additive) +GROUP group Effective group ID, prefer text over decimal. +NI nice Decimal system scheduling priority, see nice(1). +PGID pgid The decimal value of the process group ID. +PID pid Decimal PID. +PPID ppid Decimal PID. +PRI Priority. Higher numbers mean lower priority. +RGROUP rgroup Real group ID, prefer text over decimal. +RUSER ruser Real user ID, prefer text over decimal. +S The state of the process. +STIME Starting time of the process. +SZ The size in blocks of the core image of the process. +TIME time Cumulative CPU time. [dd-]hh:mm:ss +TT tty Name of tty in format used by who(1). +TTY The controlling terminal for the process. +UID UID, or name when -f +USER user Effective user ID, prefer text over decimal. +VSZ vsz Virtual memory size in decimal kB. +WCHAN Where waiting/sleeping or blank if running. + +The nice value is used to compute the priority. + +For some undefined ones, Digital does: + +F flag Process flags -- but in hex! +PRI pri Process priority +S state Symbolic process status +TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??" +UID uid Process user ID (effective UID) +WCHAN wchan Address of event on which a + +For some undefined ones, Sun does: + +ADDR addr memory address of the process +C c Processor utilization for scheduling (obsolete). +CMD +F f +S s state: OSRZT +STIME start time, printed w/o blanks. If 24h old, months & days +SZ size (in pages) of the swappable process's image in main memory +TTY +UID uid +WCHAN wchan + +For some undefined ones, SCO does: +ADDR addr Virtual address of the process' entry in the process table. +SZ swappable size in kB of the virtual data and stack +STIME stime hms or md time format +***/ + +/* Source & destination are known. Return bytes or screen characters? */ +static int forest_helper(char *restrict const outbuf){ + char *p = forest_prefix; + char *q = outbuf; + int rightward=max_rightward; + if(!*p) return 0; + /* Arrrgh! somebody defined unix as 1 */ + if(forest_type == 'u') goto unixy; + while(*p){ + switch(*p){ + case ' ': strcpy(q, " "); break; + case 'L': strcpy(q, " \\_ "); break; + case '+': strcpy(q, " \\_ "); break; + case '|': strcpy(q, " | "); break; + case '\0': return q-outbuf; /* redundant & not used */ + } + if (rightward-4 < 0) { + *(q+rightward)='\0'; + return max_rightward; + } + q += 4; + rightward -= 4; + p++; + } + return q-outbuf; /* gcc likes this here */ +unixy: + while(*p){ + switch(*p){ + case ' ': strcpy(q, " "); break; + case 'L': strcpy(q, " "); break; + case '+': strcpy(q, " "); break; + case '|': strcpy(q, " "); break; + case '\0': return q-outbuf; /* redundant & not used */ + } + if (rightward-2 < 0) { + *(q+rightward)='\0'; + return max_rightward; + } + q += 2; + rightward -= 2; + p++; + } + return q-outbuf; /* gcc likes this here */ +} + + +/* XPG4-UNIX, according to Digital: +The "args" and "command" specifiers show what was passed to the command. +Modifications to the arguments are not shown. +*/ + +/* + * pp->cmd short accounting name (comm & ucomm) + * pp->cmdline long name with args (args & command) + * pp->environ environment + */ + +// FIXME: some of these may hit the guard page in forest mode + +/* "command" is the same thing: long unless c */ +static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){ + char *endp = outbuf; + unsigned flags; + int rightward=max_rightward; + + if(forest_prefix){ + int fh = forest_helper(outbuf); + endp += fh; + rightward -= fh; + } + if(bsd_c_option) flags = ESC_DEFUNCT; + else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; + endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags); + + if(bsd_e_option && rightward>1){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + rightward--; + endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward); + } + } + //return endp - outbuf; + return max_rightward-rightward; +} + +/* "ucomm" is the same thing: short unless -f */ +static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){ + char *endp = outbuf; + unsigned flags; + int rightward=max_rightward; + + if(forest_prefix){ + int fh = forest_helper(outbuf); + endp += fh; + rightward -= fh; + } + if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; + else flags = ESC_DEFUNCT; + endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags); + + if(bsd_e_option && rightward>1){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + rightward--; + endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward); + } + } + //return endp - outbuf; + return max_rightward-rightward; +} +/* Non-standard, from SunOS 5 */ +static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){ + char *endp = outbuf; + int rightward = max_rightward; + + if(forest_prefix){ + int fh = forest_helper(outbuf); + endp += fh; + rightward -= fh; + } + if (rightward>8) /* 8=default, but forest maybe feeds more */ + rightward = 8; + + endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward); + //return endp - outbuf; + return max_rightward-rightward; +} + +/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */ +static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long t; + unsigned dd,hh,mm,ss; + char *cp = outbuf; + t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz); + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + dd = t; + cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 ); + cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 ); + cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ; + return (int)(cp-outbuf); +} + +/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */ +static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long total_time; /* jiffies used by this process */ + unsigned pcpu = 0; /* scaled %cpu, 99 means 99% */ + unsigned long long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = seconds_since_boot - pp->start_time / Hertz; + if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds; + if (pcpu > 99U) pcpu = 99U; + return snprintf(outbuf, COLWID, "%2u", pcpu); +} +/* normal %CPU in ##.# format. */ +static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long total_time; /* jiffies used by this process */ + unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned long long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = seconds_since_boot - pp->start_time / Hertz; + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999U) + return snprintf(outbuf, COLWID, "%u", pcpu/10U); + return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U); +} +/* this is a "per-mill" format, like %cpu with no decimal point */ +static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long total_time; /* jiffies used by this process */ + unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned long long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = seconds_since_boot - pp->start_time / Hertz ; + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999U) pcpu = 999U; + return snprintf(outbuf, COLWID, "%3u", pcpu); +} + +static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->pgrp); +} +static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->tgid); +} +static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->ppid); +} + + +/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */ +static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long t; + unsigned dd,hh,mm,ss; + int c; + t = (pp->utime + pp->stime) / Hertz; + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + dd = t; + c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 ); + c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) ); + return c; +} + +/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages. + * Unix98 requires "vsz" to be kB. + * Tru64 does both vsize and vsz like "1.23M" + * + * Our pp->vm_size is kB and our pp->vsize is pages. + * + * TODO: add flag for "1.23M" behavior, on this and other columns. + */ +static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%lu", pp->vm_size); +} + +////////////////////////////////////////////////////////////////////////////////////// + +// "PRI" is created by "opri", or by "pri" when -c is used. +// +// Unix98 only specifies that a high "PRI" is low priority. +// Sun and SCO add the -c behavior. Sun defines "pri" and "opri". +// Linux may use "priority" for historical purposes. +// +// According to the kernel's fs/proc/array.c and kernel/sched.c source, +// the kernel reports it in /proc via this: +// p->prio - MAX_RT_PRIO +// such that "RT tasks are offset by -200. Normal tasks are centered +// around 0, value goes from -16 to +15" but who knows if that is +// before or after the conversion... +// +// says: +// MAX_RT_PRIO is currently 100. (so we see 0 in /proc) +// RT tasks have a p->prio of 0 to 99. (so we see -100 to -1) +// non-RT tasks are from 100 to 139. (so we see 0 to 39) +// Lower values have higher priority, as in the UNIX standard. +// +// In any case, pp->priority+100 should get us back to what the kernel +// has for p->prio. +// +// Test results with the "yes" program on a 2.6.x kernel: +// +// # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm +// PRI PRI PRI PRI NI %CPU PID COMMAND +// 0 99 99 39 19 10.6 8686 19 +// 34 65 65 5 -20 94.7 8687 _20 +// +// Grrr. So the UNIX standard "PRI" must NOT be from "pri". +// Either of the others will do. We use "opri" for this. +// (and use "pri" when the "-c" option is used) +// Probably we should have Linux-specific "pri_for_l" and "pri_for_lc" +// +// sched_get_priority_min.2 says the Linux static priority is +// 1..99 for RT and 0 for other... maybe 100 is kernel-only? +// +// A nice range would be -99..0 for RT and 1..40 for normal, +// which is pp->priority+1. (3-digit max, positive is normal, +// negative or 0 is RT, and meets the standard for PRI) +// + +// legal as UNIX "PRI" +// "priority" (was -20..20, now -100..39) +static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){ /* -20..20 */ + return snprintf(outbuf, COLWID, "%ld", pp->priority); +} + +// legal as UNIX "PRI" +// "intpri" and "opri" (was 39..79, now -40..99) +static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 39..79 */ + return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority); +} + +// legal as UNIX "PRI" +// "pri_foo" -- match up w/ nice values of sleeping processes (-120..19) +static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->priority - 20); +} + +// legal as UNIX "PRI" +// "pri_bar" -- makes RT pri show as negative (-99..40) +static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->priority + 1); +} + +// legal as UNIX "PRI" +// "pri_baz" -- the kernel's ->prio value, as of Linux 2.6.8 (1..140) +static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->priority + 100); +} + + +// not legal as UNIX "PRI" +// "pri" (was 20..60, now 0..139) +static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 20..60 */ + return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority); +} + +// not legal as UNIX "PRI" +// "pri_api" -- match up w/ RT API (-40..99) +static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority); +} + +static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%ld", pp->nice); +} + +// HP-UX "cls": RT RR RR2 ???? HPUX FIFO KERN +// Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant) +// FIFO+RR share RT; FIFO has Inf quant +// IA=interactive; FX=fixed; TS=timeshare; SYS=system +// FSS=fairshare; INTS=interrupts +// Tru64 "policy": FF RR TS +// IRIX "class": RT TS B BC WL GN +// RT=real-time; TS=time-share; B=batch; BC=batch-critical +// WL=weightless; GN=gang-scheduled +// see miser(1) for this; PRI has some letter codes too +static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){ + switch(pp->sched){ + case -1: return snprintf(outbuf, COLWID, "-"); // not reported + case 0: return snprintf(outbuf, COLWID, "TS"); // SCHED_OTHER SCHED_NORMAL + case 1: return snprintf(outbuf, COLWID, "FF"); // SCHED_FIFO + case 2: return snprintf(outbuf, COLWID, "RR"); // SCHED_RR + case 3: return snprintf(outbuf, COLWID, "B"); // SCHED_BATCH + case 4: return snprintf(outbuf, COLWID, "ISO"); // reserved for SCHED_ISO (Con Kolivas) + case 5: return snprintf(outbuf, COLWID, "IDL"); // SCHED_IDLE + case 6: return snprintf(outbuf, COLWID, "#6"); // + case 7: return snprintf(outbuf, COLWID, "#7"); // + case 8: return snprintf(outbuf, COLWID, "#8"); // + case 9: return snprintf(outbuf, COLWID, "#9"); // + default: return snprintf(outbuf, COLWID, "?"); // unknown value + } +} +// Based on "type", FreeBSD would do: +// REALTIME "real:%u", prio +// NORMAL "normal" +// IDLE "idle:%u", prio +// default "%u:%u", type, prio +// We just print the priority, and have other keywords for type. +static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%ld", pp->rtprio); +} +static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->sched==-1) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%ld", pp->sched); +} + +//////////////////////////////////////////////////////////////////////////////// + +static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){ +/* + * Unix98 says "blank if running" and also "no blanks"! :-( + * Unix98 also says to use '-' if something is meaningless. + * Digital uses both '*' and '-', with undocumented differences. + * (the '*' for -1 (rare) and the '-' for 0) + * Sun claims to use a blank AND use '-', in the same man page. + * Perhaps "blank" should mean '-'. + * + * AIX uses '-' for running processes, the location when there is + * only one thread waiting in the kernel, and '*' when there is + * more than one thread waiting in the kernel. + * + * The output should be truncated to maximal columns width -- overflow + * is not supported for the "wchan". + */ + const char *w; + size_t len; + if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1; + if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu); + w = lookup_wchan(pp->wchan, pp->XXXID); + len = strlen(w); + if(len>max_rightward) len=max_rightward; + memcpy(outbuf, w, len); + outbuf[len] = '\0'; + return len; +} + +static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){ +/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too. + * + * We use '-' for running processes, the location when there is + * only one thread waiting in the kernel, and '*' when there is + * more than one thread waiting in the kernel. + * + * The output should be truncated to maximal columns width -- overflow + * is not supported for the "wchan". + */ + const char *w; + size_t len; + if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1; + w = lookup_wchan(pp->wchan, pp->XXXID); + len = strlen(w); + if(len>max_rightward) len=max_rightward; + memcpy(outbuf, w, len); + outbuf[len] = '\0'; + return len; +} + +static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){ + if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1; + return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu); +} + +/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */ +/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */ +static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS); +} + +/* Unix98: format is unspecified, but must match that used by who(1). */ +static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV); +} + +#if 0 +/* This BSD state display may contain spaces, which is illegal. */ +static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%s", status(pp)); +} +#endif + +// This state display is Unix98 compliant and has lots of info like BSD. +static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){ + int end = 0; + outbuf[end++] = pp->state; +// if(pp->rss==0 && pp->state!='Z') outbuf[end++] = 'W'; // useless "swapped out" + if(pp->nice < 0) outbuf[end++] = '<'; + if(pp->nice > 0) outbuf[end++] = 'N'; +// In this order, NetBSD would add: +// traced 'X' +// systrace 'x' +// exiting 'E' (not printed for zombies) +// vforked 'V' +// system 'K' (and do not print 'L' too) + if(pp->vm_lock) outbuf[end++] = 'L'; + if(pp->session == pp->tgid) outbuf[end++] = 's'; // session leader + if(pp->nlwp > 1) outbuf[end++] = 'l'; // multi-threaded + if(pp->pgrp == pp->tpgid) outbuf[end++] = '+'; // in foreground process group + outbuf[end] = '\0'; + return end; +} + +/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */ +static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){ + outbuf[0] = pp->state; + outbuf[1] = '\0'; + return 1; +} + +static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){ + /* Unix98 requires octal flags */ + /* this user-hostile and volatile junk gets 1 character */ + return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>6U)&0x7U); +} + +// plus these: euid,ruid,egroup,rgroup (elsewhere in this file) + +/*********** non-standard ***********/ + +/*** BSD +sess session pointer +(SCO has:Process session leader ID as a decimal value. (SESSION)) +jobc job control count +cpu short-term cpu usage factor (for scheduling) +sl sleep time (in seconds; 127 = infinity) +re core residency time (in seconds; 127 = infinity) +pagein pageins (same as majflt) +lim soft memory limit +tsiz text size (in Kbytes) +***/ + +static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack)); +} + +static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp)); +} + +static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip)); +} + +/* This function helps print old-style time formats */ +static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) { + if(!t) return snprintf(dst, COLWID, " -"); + if(t == ~0ULL) return snprintf(dst, COLWID, " xx"); + if((long long)(t-=rel) < 0) t=0ULL; + if(t>9999ULL) return snprintf(dst, COLWID, "%5Lu", t/100ULL); + else return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U); +} + +static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long long t; + unsigned u; + t = pp->utime + pp->stime; + if(include_dead_children) t += (pp->cutime + pp->cstime); + u = t / Hertz; + return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U); +} + +static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){ + time_t start; + time_t seconds_ago; + start = time_of_boot + pp->start_time / Hertz; + seconds_ago = seconds_since_1970 - start; + if(seconds_ago < 0) seconds_ago=0; + if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4); + else strcpy(outbuf, ctime(&start)+10); + outbuf[6] = '\0'; + return 6; +} + +static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){ + return old_time_helper(outbuf, pp->alarm, 0ULL); +} + +/* HP-UX puts this in pages and uses "vsz" for kB */ +static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024)); +} + + +/* + * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss + * I suspect some/all of those are broken. They seem to have been + * inherited by Linux and AIX from early BSD systems. FreeBSD only + * retains tsiz. The prefixed versions come from Debian. + * Sun and Digital have none of this crap. The code here comes + * from an old Linux ps, and might not be correct for ELF executables. + * + * AIX TRS size of resident-set (real memory) of text + * AIX TSIZ size of text (shared-program) image + * FreeBSD tsiz text size (in Kbytes) + * 4.3BSD NET/2 trss text resident set size (in Kbytes) + * 4.3BSD NET/2 tsiz text size (in Kbytes) + */ + +/* kB data size. See drs, tsiz & trs. */ +static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){ + long dsiz = 0; + if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", dsiz); +} + +/* kB text (code) size. See trs, dsiz & drs. */ +static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){ + long tsiz = 0; + if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", tsiz); +} + +/* kB _resident_ data size. See dsiz, tsiz & trs. */ +static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){ + long drs = 0; + if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", drs); +} + +/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */ +static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){ + long trs = 0; + if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", trs); +} + +/* approximation to: kB of address space that could end up in swap */ +static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack); +} + +/* nasty old Debian thing */ +static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%ld", pp->size); +} + + +static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){ + long flt = pp->min_flt; + if(include_dead_children) flt += pp->cmin_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){ + long flt = pp->maj_flt; + if(include_dead_children) flt += pp->cmaj_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){ + if(pp->rss_rlim == RLIM_INFINITY){ + outbuf[0] = 'x'; + outbuf[1] = 'x'; + outbuf[2] = '\0'; + return 2; + } + return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10); +} + +/* should print leading tilde ('~') if process is bound to the CPU */ +static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->processor); +} + +static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%lu", pp->vm_rss); +} + +/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */ +static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){ + unsigned long pmem = 0; + pmem = pp->vm_rss * 1000ULL / kb_main_total; + if (pmem > 999) pmem = 999; + return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10)); +} + +static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){ + time_t t; + t = time_of_boot + pp->start_time / Hertz; + return snprintf(outbuf, COLWID, "%24.24s", ctime(&t)); +} + +/* Unix98 specifies a STIME header for a column that shows the start + * time of the process, but does not specify a format or format specifier. + * From the general Unix98 rules, we know there must not be any spaces. + * Most systems violate that rule, though the Solaris documentation + * claims to print the column without spaces. (NOT!) + * + * So this isn't broken, but could be renamed to u98_std_stime, + * as long as it still shows as STIME when using the -f option. + */ +static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){ + struct tm *proc_time; + struct tm *our_time; + time_t t; + const char *fmt; + int tm_year; + int tm_yday; + our_time = localtime(&seconds_since_1970); /* not reentrant */ + tm_year = our_time->tm_year; + tm_yday = our_time->tm_yday; + t = time_of_boot + pp->start_time / Hertz; + proc_time = localtime(&t); /* not reentrant, this corrupts our_time */ + fmt = "%H:%M"; /* 03:02 23:59 */ + if(tm_yday != proc_time->tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */ + if(tm_year != proc_time->tm_year) fmt = "%Y"; /* 1991 2001 */ + return strftime(outbuf, 42, fmt, proc_time); +} + +static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){ + time_t t; + char *str; + t = time_of_boot + pp->start_time / Hertz; + str = ctime(&t); + if(str[8]==' ') str[8]='0'; + if(str[11]==' ') str[11]='0'; + if((unsigned long)t+60*60*24 > seconds_since_1970) + return snprintf(outbuf, COLWID, "%8.8s", str+11); + return snprintf(outbuf, COLWID, " %6.6s", str+4); +} + + +#ifdef SIGNAL_STRING +static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){ + long len = 0; + len = strlen(sig); + if(wide_signals){ + if(len>8) return snprintf(outbuf, COLWID, "%s", sig); + return snprintf(outbuf, COLWID, "00000000%s", sig); + } + if(len-strspn(sig,"0") > 8) + return snprintf(outbuf, COLWID, "<%s", sig+len-8); + return snprintf(outbuf, COLWID, "%s", sig+len-8); +} +#else +static int help_pr_sig(unsigned long long sig){ + if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig); + if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL); + return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL); +} +#endif + +// This one is always thread-specific pending. (from Dragonfly BSD) +static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->_sigpnd); +} +// This one is (wrongly?) thread-specific when printing thread lines, +// but process-pending otherwise. +static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->signal); +} +static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->blocked); +} +static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->sigignore); +} +static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){ + return help_pr_sig(outbuf, pp->sigcatch); +} + + +//////////////////////////////////////////////////////////////////////////////// + +/* + * internal terms: ruid euid suid fuid + * kernel vars: uid euid suid fsuid + * command args: ruid uid svuid n/a + */ + +static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->egid); +} +static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->rgid); +} +static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->sgid); +} +static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->fgid); +} + +static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->euid); +} +static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->ruid); +} +static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->suid); +} +static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->fuid); +} + +// The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition) +// requires that user and group names print as decimal numbers if there is +// not enough room in the column, so tough luck if you don't like it. +// +// The UNIX and POSIX way to change column width is to rename it: +// ps -o pid,user=CumbersomeUserNames -o comm +// The easy way is to directly specify the desired width: +// ps -o pid,user:19,comm +// +static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){ + if(!user_is_number){ + int rightward = OUTBUF_SIZE; /* max cells */ + int len; /* real cells */ + + escape_str(outbuf, name, OUTBUF_SIZE, &rightward); + len = OUTBUF_SIZE-rightward; + + if(len <= (int)max_rightward) + return len; /* returns number of cells */ + } + return snprintf(outbuf, COLWID, "%u", u); +} + +static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->ruser, pp->ruid); +} +static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->euser, pp->euid); +} +static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->fuser, pp->fuid); +} +static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->suser, pp->suid); +} + +static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->egroup, pp->egid); +} +static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->rgroup, pp->rgid); +} +static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->fgroup, pp->fgid); +} +static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){ + return do_pr_name(outbuf, pp->sgroup, pp->sgid); +} + +////////////////////////////////////////////////////////////////////////////////// + +// TID tid LWP lwp SPID spid +static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->tid); +} +// thcount THCNT +static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->nlwp); +} + +static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%u", pp->session); +} +static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){ + return snprintf(outbuf, COLWID, "%d", pp->tpgid); +} + + +/* SGI uses "cpu" to print the processor ID with header "P" */ +static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){ /* FIXME */ + if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor); + return snprintf(outbuf, COLWID, "*"); +} + + +/****************** FLASK & seLinux security stuff **********************/ +// move the bulk of this to libproc sometime + +static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){ + char filename[48]; + size_t len; + ssize_t num_read; + int fd; + +// wchan file is suitable for testing +//snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid); +snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid); + + fd = open(filename, O_RDONLY, 0); + if(likely(fd==-1)) goto fail; + num_read = read(fd, outbuf, 666); + close(fd); + if(unlikely(num_read<=0)) goto fail; + outbuf[num_read] = '\0'; + + len = 0; + while(outbuf[len]>' ' && outbuf[len]<='~') len++; + outbuf[len] = '\0'; + if(len) return len; + +fail: + outbuf[0] = '-'; + outbuf[1] = '\0'; + return 1; +} + +#if 0 +// This needs more study, considering: +// 1. the static linking option (maybe disable this in that case) +// 2. the -z and -Z option issue +// 3. width of output +static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){ + static int (*ps_getpidcon)(pid_t pid, char **context) = 0; + static int tried_load = 0; + size_t len; + char *context; + + if(!ps_getpidcon && !tried_load){ + void *handle = dlopen("libselinux.so.1", RTLD_NOW); + if(handle){ + dlerror(); + ps_getpidcon = dlsym(handle, "getpidcon"); + if(dlerror()) + ps_getpidcon = 0; + } + tried_load++; + } + if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){ + size_t max_len = OUTBUF_SIZE-1; + len = strlen(context); + if(len > max_len) len = max_len; + memcpy(outbuf, context, len); + outbuf[len] = '\0'; + free(context); + }else{ + outbuf[0] = '-'; + outbuf[1] = '\0'; + len = 1; + } + return len; +} +#endif + + +////////////////////////////// Test code ///////////////////////////////// + +// like "args" +static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"[123456789-12345] ","ps","123456789-123456"}; + (void)pp; + snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]); + return strlen(outbuf); +} +static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"unlimited", "[123456789-12345] ","ps","123456789-123456"}; + (void)pp; + snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]); + return strlen(outbuf); +} + +// like "etime" +static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]); +} +static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]); +} + +// like "tty" +static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]); +} +static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){ + static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"}; + (void)pp; + return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]); +} + +/***************************************************************************/ +/*************************** other stuff ***********************************/ + +/* + * Old header specifications. + * + * short Up " PID TTY STAT TIME COMMAND" + * long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND + * user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND + * jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND + * sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND + * vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND + * m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND + * regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND + */ + +/* + * Unix98 requires that the heading for tty is TT, though XPG4, Digital, + * and BSD use TTY. The Unix98 headers are: + * args,comm,etime,group,nice,pcpu,pgid + * pid,ppid,rgroup,ruser,time,tty,user,vsz + * + * BSD c: "command" becomes accounting name ("comm" or "ucomm") + * BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number) + */ + +/* Justification control for flags field. */ +#define USER CF_USER // left if text, right if numeric +#define LEFT CF_LEFT +#define RIGHT CF_RIGHT +#define UNLIMITED CF_UNLIMITED +#define WCHAN CF_WCHAN // left if text, right if numeric +#define SIGNAL CF_SIGNAL // right in 9, or 16 if room +#define PIDMAX CF_PIDMAX +#define TO CF_PRINT_THREAD_ONLY +#define PO CF_PRINT_PROCESS_ONLY +#define ET CF_PRINT_EVERY_TIME +#define AN CF_PRINT_AS_NEEDED // no idea + +/* short names to save space */ +#define MEM PROC_FILLMEM /* read statm */ +#define ARG PROC_FILLARG /* read cmdline (cleared if c option) */ +#define COM PROC_FILLCOM /* read cmdline (cleared if not -f option) */ +#define ENV PROC_FILLENV /* read environ */ +#define USR PROC_FILLUSR /* uid_t -> user names */ +#define GRP PROC_FILLGRP /* gid_t -> group names */ +#define WCH PROC_FILLWCHAN /* do WCHAN lookup */ + + +/* TODO + * pull out annoying BSD aliases into another table (to macro table?) + * add sorting functions here (to unify names) + */ + +/* temporary hack -- mark new stuff grabbed from Debian ps */ +#define LNx LNX + +/* there are about 211 listed */ + +/* Many of these are placeholders for unsupported options. */ +static const format_struct format_array[] = { +/* code header print() sort() width need vendor flags */ +{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, ET|RIGHT}, /*pcpu*/ +{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, PO|RIGHT}, /*pmem*/ +{"_left", "LLLLLLLL", pr_t_left, sr_nop, 8, 0, TST, ET|LEFT}, +{"_left2", "L2L2L2L2", pr_t_left2, sr_nop, 8, 0, TST, ET|LEFT}, +{"_right", "RRRRRRRRRRR", pr_t_right, sr_nop, 11, 0, TST, ET|RIGHT}, +{"_right2", "R2R2R2R2R2R", pr_t_right2, sr_nop, 11, 0, TST, ET|RIGHT}, +{"_unlimited","U", pr_t_unlimited, sr_nop, 16, 0, TST, ET|UNLIMITED}, +{"_unlimited2","U2", pr_t_unlimited2, sr_nop, 16, 0, TST, ET|UNLIMITED}, +{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, /*acflg*/ +{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*acflag*/ +{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, +{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT}, +{"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT}, +{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT}, +{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/ +{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */ +{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/ +{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT}, +{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT}, +{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT}, +{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT}, +{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/ +{"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT}, +{"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/ +{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT}, +{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/ +{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT}, +{"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT}, +{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/ +{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/ +{"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT}, +{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/ +{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */ +{"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide! +{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/ +{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT}, +{"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts? +{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, +{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, AN|RIGHT}, +{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, AN|LEFT}, +{"drs", "DRS", pr_drs, sr_drs, 5, MEM, LNX, PO|RIGHT}, +{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, PO|RIGHT}, +{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, ET|RIGHT}, +{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, GRP, LNX, ET|USER}, +{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, TO|RIGHT}, +{"emul", "EMUL", pr_nop, sr_nop, 13, 0, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */ +{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT}, +{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED}, +{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT}, +{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, ET|RIGHT}, /* was 7 wide */ +{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT}, +{"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER}, +{"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/ +{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT}, +{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER}, +{"flag", "F", pr_flag, sr_flags, 1, 0, DEC, ET|RIGHT}, +{"flags", "F", pr_flag, sr_flags, 1, 0, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */ +{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, PO|LEFT}, +{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT}, +{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER}, +{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT}, +{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER}, +{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT}, +{"fuser", "FUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER}, +{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, ET|RIGHT}, +{"group", "GROUP", pr_egroup, sr_egroup, 8, GRP, U98, ET|USER}, +{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigignore*/ +{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/ +{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/ +{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT}, +{"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT}, +{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, +{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"label", "LABEL", pr_context, sr_nop, 31, 0, SGI, ET|LEFT}, +{"lastcpu", "C", pr_psr, sr_nop, 3, 0, BSD, TO|RIGHT}, // DragonFly +{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT}, +{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */ +{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/ +{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT}, +{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, ET|RIGHT}, +{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, ET|RIGHT}, /* login ID */ +{"luser", "LUSER", pr_nop, sr_nop, 8, USR, LNX, ET|USER}, /* login USER */ +{"lwp", "LWP", pr_thread, sr_tid, 5, 0, SUN, TO|PIDMAX|RIGHT}, +{"m_drs", "DRS", pr_drs, sr_drs, 5, MEM, LNx, PO|RIGHT}, +{"m_dt", "DT", pr_nop, sr_dt, 4, MEM, LNx, PO|RIGHT}, +{"m_lrs", "LRS", pr_nop, sr_lrs, 5, MEM, LNx, PO|RIGHT}, +{"m_resident", "RES", pr_nop, sr_resident, 5,MEM, LNx, PO|RIGHT}, +{"m_share", "SHRD", pr_nop, sr_share, 5, MEM, LNx, PO|RIGHT}, +{"m_size", "SIZE", pr_size, sr_size, 5, MEM, LNX, PO|RIGHT}, +{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, PO|RIGHT}, +{"m_trs", "TRS", pr_trs, sr_trs, 5, MEM, LNx, PO|RIGHT}, +{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, AN|RIGHT}, +{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT}, +{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT}, +{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT}, +{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT}, +{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT}, +{"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */ +{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/ +{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/ +{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, +{"nlwp", "NLWP", pr_nlwp, sr_nlwp, 4, 0, SUN, PO|RIGHT}, +{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*nsigs*/ +{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*nsignals*/ +{"nswap", "NSWAP", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, +{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, +{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, TO|RIGHT}, +{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, TO|RIGHT}, +{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, PO|RIGHT}, +{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*oublock*/ +{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*oublk*/ +{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT}, +{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT}, +{"pagein", "PAGEIN", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT}, +{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, ET|RIGHT}, /*%cpu*/ +{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /*sig*/ +{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT}, +{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT}, +{"pid", "PID", pr_pid, sr_tgid, 5, 0, U98, PO|PIDMAX|RIGHT}, +{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, PO|RIGHT}, /*%mem*/ +{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT}, +{"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT}, +{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, PO|PIDMAX|RIGHT}, +{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, TO|RIGHT}, +{"pri_api", "API", pr_pri_api, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"pri_bar", "BAR", pr_pri_bar, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"pri_baz", "BAZ", pr_pri_baz, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"pri_foo", "FOO", pr_pri_foo, sr_nop, 3, 0, LNX, TO|RIGHT}, +{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, TO|RIGHT}, +{"prmgrp", "PRMGRP", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT}, +{"prmid", "PRMID", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT}, +{"project", "PROJECT", pr_nop, sr_nop, 12, 0, SUN, PO|LEFT}, // see prm* andctid +{"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT}, +{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT}, +{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT}, +{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, +{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, +{"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT}, +{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT}, +{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */ +{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */ +{"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/ +{"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/ +{"rtprio", "RTPRIO", pr_rtprio, sr_rtprio, 6, 0, BSD, TO|RIGHT}, +{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, ET|RIGHT}, +{"ruser", "RUSER", pr_ruser, sr_ruser, 8, USR, U98, ET|USER}, +{"s", "S", pr_s, sr_state, 1, 0, SUN, TO|LEFT}, /*stat,state*/ +{"sched", "SCH", pr_sched, sr_sched, 3, 0, AIX, TO|RIGHT}, +{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, AN|RIGHT}, /* man page misspelling of scount? */ +{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */ +{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, +{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, PO|PIDMAX|RIGHT}, +{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, TO|RIGHT}, /* "cpu" number */ +{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, PO|LEFT}, /* SZ:RSS */ +{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, ET|RIGHT}, +{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER}, +{"share", "-", pr_nop, sr_share, 1, MEM, LNX, PO|RIGHT}, +{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */ +{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */ +{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, TO|SIGNAL}, +{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, TO|SIGNAL}, +{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, TO|SIGNAL}, +{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, ET|SIGNAL}, +{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/ +{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/ +{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/ +{"size", "SZ", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT}, +{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT}, +{"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT}, +{"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/ +{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, ET|RIGHT}, +{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, PO|RIGHT}, +{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*stackp*/ +{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, ET|RIGHT}, +{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, TO|LEFT}, /*state,s*/ +{"state", "S", pr_s, sr_state, 1, 0, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */ +{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, +{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */ +{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT}, +{"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER}, +{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT}, +{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER}, +{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, ET|RIGHT}, +{"svuser", "SVUSER", pr_suser, sr_suser, 8, USR, LNX, ET|USER}, +{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, ET|RIGHT}, +{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, PO|RIGHT}, +{"taskid", "TASKID", pr_nop, sr_nop, 5, 0, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID? +{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT}, +{"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT}, +{"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT}, +{"time", "TIME", pr_time, sr_nop, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */ +{"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era +{"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era +{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT}, +{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, PO|PIDMAX|RIGHT}, +{"trs", "TRS", pr_trs, sr_trs, 4, MEM, AIX, PO|RIGHT}, +{"trss", "TRSS", pr_trs, sr_trs, 4, MEM, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */ +{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT}, +{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, PO|PIDMAX|RIGHT}, +{"tsid", "TSID", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT}, +{"tsig", "PENDING", pr_tsig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */ +{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, PO|RIGHT}, +{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, PO|LEFT}, +{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */ +{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT}, +{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT}, +{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT}, +{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/ +{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/ +{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT}, +{"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER}, +{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, +{"uname", "USER", pr_euser, sr_euser, 8, USR, DEC, ET|USER}, /* man page misspelling of user? */ +{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/ +{"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT}, +{"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */ +{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT}, +{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/ +{"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C" +{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT}, +{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT}, +{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT}, +{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT}, +{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, PO|RIGHT}, +{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, PO|RIGHT}, +{"vsize", "VSZ", pr_vsz, sr_vsize, 6, 0, DEC, PO|RIGHT}, /*vsz*/ +{"vsz", "VSZ", pr_vsz, sr_vm_size, 6, 0, U98, PO|RIGHT}, /*vsize*/ +{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, WCH, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */ +{"wname", "WCHAN", pr_wname, sr_nop, 6, WCH, SGI, TO|WCHAN}, /* opposite of nwchan */ +{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, +{"zone", "ZONE", pr_context, sr_nop, 31, 0, SUN, ET|LEFT}, // Solaris zone == Linux context? +{"zoneid", "ZONEID", pr_nop, sr_nop, 31, 0, SUN, ET|RIGHT},// Linux only offers context names +{"~", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT} /* NULL would ruin alphabetical order */ +}; + +#undef USER +#undef LEFT +#undef RIGHT +#undef UNLIMITED +#undef WCHAN +#undef SIGNAL +#undef PIDMAX +#undef PO +#undef TO +#undef AN +#undef ET + +static const int format_array_count = sizeof(format_array)/sizeof(format_struct); + + +/****************************** Macro formats *******************************/ +/* First X field may be NR, which is p->start_code>>26 printed with %2ld */ +/* That seems useless though, and Debian already killed it. */ +/* The ones marked "Digital" have the name defined, not just the data. */ +static const macro_struct macro_array[] = { +{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */ +{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */ +{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */ +{"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */ +{"END_SYS5", "state,tname,time,command"}, /* trailer for -O */ +{"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */ + +{"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */ +{"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */ +{"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */ +{"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */ +{"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */ + +{"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */ +{"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */ +{"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */ +{"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */ +{"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */ +{"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */ + +{"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */ + +{"FLASK_context", "pid,context,command"}, /* Flask Linux context, --context */ + +{"HP_", "pid,tty,time,comm"}, /* HP default */ +{"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */ +{"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */ +{"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */ + +{"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */ +{"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */ +{"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */ +{"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */ + +{"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */ +{"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */ +{"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */ +{"OL_m", "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */ +{"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */ +{"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */ +{"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */ + +{"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */ +{"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */ +{"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */ +{"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */ +{"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */ +{"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */ + +{"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */ +{"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */ +{"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */ + +{"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */ +{"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */ +{"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */ + +{"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */ +{"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */ +{"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */ +{"~", "~"} /* NULL would ruin alphabetical order */ +}; + +static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct); + + +/*************************** AIX formats ********************/ +/* Convert AIX format codes to normal format specifiers. */ +static const aix_struct aix_array[] = { +{'C', "pcpu", "%CPU"}, +{'G', "group", "GROUP"}, +{'P', "ppid", "PPID"}, +{'U', "user", "USER"}, +{'a', "args", "COMMAND"}, +{'c', "comm", "COMMAND"}, +{'g', "rgroup", "RGROUP"}, +{'n', "nice", "NI"}, +{'p', "pid", "PID"}, +{'r', "pgid", "PGID"}, +{'t', "etime", "ELAPSED"}, +{'u', "ruser", "RUSER"}, +{'x', "time", "TIME"}, +{'y', "tty", "TTY"}, +{'z', "vsz", "VSZ"}, +{'~', "~", "~"} /* NULL would ruin alphabetical order */ +}; +static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct); + + +/********************* sorting ***************************/ +/* Convert short sorting codes to normal format specifiers. */ +static const shortsort_struct shortsort_array[] = { +{'C', "pcpu" }, +{'G', "tpgid" }, +{'J', "cstime" }, +/* {'K', "stime" }, */ /* conflict, system vs. start time */ +{'M', "maj_flt" }, +{'N', "cmaj_flt" }, +{'P', "ppid" }, +{'R', "resident" }, +{'S', "share" }, +{'T', "start_time" }, +{'U', "uid" }, /* euid */ +{'c', "cmd" }, +{'f', "flags" }, +{'g', "pgrp" }, +{'j', "cutime" }, +{'k', "utime" }, +{'m', "min_flt" }, +{'n', "cmin_flt" }, +{'o', "session" }, +{'p', "pid" }, +{'r', "rss" }, +{'s', "size" }, +{'t', "tty" }, +{'u', "user" }, +{'v', "vsize" }, +{'y', "priority" }, /* nice */ +{'~', "~" } /* NULL would ruin alphabetical order */ +}; +static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct); + + +/*********** print format_array **********/ +/* called by the parser in another file */ +void print_format_specifiers(void){ + const format_struct *walk = format_array; + while(*(walk->spec) != '~'){ + if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head); + walk++; + } +} + +/************ comparison functions for bsearch *************/ + +static int compare_format_structs(const void *a, const void *b){ + return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec); +} + +static int compare_macro_structs(const void *a, const void *b){ + return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec); +} + +/******** look up structs as needed by the sort & format parsers ******/ + +const shortsort_struct *search_shortsort_array(const int findme){ + const shortsort_struct *walk = shortsort_array; + while(walk->desc != '~'){ + if(walk->desc == findme) return walk; + walk++; + } + return NULL; +} + +const aix_struct *search_aix_array(const int findme){ + const aix_struct *walk = aix_array; + while(walk->desc != '~'){ + if(walk->desc == findme) return walk; + walk++; + } + return NULL; +} + +const format_struct *search_format_array(const char *findme){ + format_struct key; + key.spec = findme; + return bsearch(&key, format_array, format_array_count, + sizeof(format_struct), compare_format_structs + ); +} + +const macro_struct *search_macro_array(const char *findme){ + macro_struct key; + key.spec = findme; + return bsearch(&key, macro_array, macro_array_count, + sizeof(macro_struct), compare_macro_structs + ); +} + +static unsigned int active_cols; /* some multiple of screen_cols */ + +/***** Last chance, avoid needless trunctuation. */ +static void check_header_width(void){ + format_node *walk = format_list; + unsigned int total = 0; + int was_normal = 0; + unsigned int i = 0; + unsigned int sigs = 0; + while(walk){ + switch((walk->flags) & CF_JUST_MASK){ + default: + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case CF_SIGNAL: + sigs++; + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case CF_UNLIMITED: /* could chop this a bit */ + if(walk->next) total += walk->width; + else total += 3; /* not strlen(walk->name) */ + total += was_normal; + was_normal = 1; + break; + case 0: /* AIX */ + total += walk->width; + was_normal = 0; + break; + } + walk = walk->next; + } + for(;;){ + i++; + active_cols = screen_cols * i; + if(active_cols>=total) break; + if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */ + } + wide_signals = (total+sigs*7 <= active_cols); +} + + +/********** show one process (NULL proc prints header) **********/ + +//#define SPACE_AMOUNT page_size +#define SPACE_AMOUNT 144 + +static char *saved_outbuf; + +void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){ + /* unknown: maybe set correct & actual to 1, remove +/- 1 below */ + int correct = 0; /* screen position we should be at */ + int actual = 0; /* screen position we are at */ + int amount = 0; /* amount of text that this data is */ + int leftpad = 0; /* amount of space this column _could_ need */ + int space = 0; /* amount of space we actually need to print */ + int dospace = 0; /* previous column determined that we need a space */ + int legit = 0; /* legitimately stolen extra space */ + int sz = 0; /* real size of data in outbuffer */ + int tmpspace = 0; + char *restrict const outbuf = saved_outbuf; + static int did_stuff = 0; /* have we ever printed anything? */ + + if(unlikely(-1==(long)p)){ /* true only once, at the end */ + if(did_stuff) return; + /* have _never_ printed anything, but might need a header */ + if(!--lines_to_next_header){ + lines_to_next_header = header_gap; + show_one_proc(NULL,fmt); + } + /* fprintf(stderr, "No processes available.\n"); */ /* legal? */ + exit(1); + } + if(likely(p)){ /* not header, maybe we should call ourselves for it */ + if(unlikely(!--lines_to_next_header)){ + lines_to_next_header = header_gap; + show_one_proc(NULL,fmt); + } + } + did_stuff = 1; + if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n"); + + /* print row start sequence */ + for(;;){ + legit = 0; + /* set width suggestion which might be ignored */ +// if(likely(fmt->next)) max_rightward = fmt->width; +// else max_rightward = active_cols-((correct>actual) ? correct : actual); + + if(likely(fmt->next)){ + max_rightward = fmt->width; + tmpspace = 0; + }else{ + tmpspace = correct-actual; + if (tmpspace<1){ + tmpspace = dospace; + max_rightward = active_cols-actual-tmpspace; + }else{ + max_rightward = active_cols - ( (correct>actual) ? correct : actual ); + } + } + max_leftward = fmt->width + actual - correct; /* TODO check this */ + +// fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n", +// active_cols, max_rightward, max_leftward, actual, correct); + + /* prepare data and calculate leftpad */ + if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p); + else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */ + + switch((fmt->flags) & CF_JUST_MASK){ + case 0: /* for AIX, assigned outside this file */ + leftpad = 0; + break; + case CF_LEFT: /* bad */ + leftpad = 0; + break; + case CF_RIGHT: /* OK */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + case CF_SIGNAL: + /* if the screen is wide enough, use full 16-character output */ + if(wide_signals){ + leftpad = 16 - amount; + legit = 7; + }else{ + leftpad = 9 - amount; + } + if(leftpad < 0) leftpad = 0; + break; + case CF_USER: /* bad */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + if(!user_is_number) leftpad = 0; + break; + case CF_WCHAN: /* bad */ + if(wchan_is_number){ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + }else{ + if ((active_cols-actual-tmpspace)<1) + outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */ + leftpad = 0; + break; + } + case CF_UNLIMITED: + { + if(active_cols-actual-tmpspace < 1) + outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */ + leftpad = 0; + break; + } + default: + fprintf(stderr, "bad alignment code\n"); + break; + } + /* At this point: + * + * correct from previous column + * actual from previous column + * amount not needed (garbage due to chopping) + * leftpad left padding for this column alone (not make-up or gap) + * space not needed (will recalculate now) + * dospace if we require space between this and the prior column + * legit space we were allowed to steal, and thus did steal + */ + space = correct - actual + leftpad; + if(space<1) space=dospace; + if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT; // only so much available + + /* real size -- don't forget in 'amount' is number of cells */ + sz = strlen(outbuf); + + /* print data, set x position stuff */ + if(unlikely(!fmt->next)){ + /* Last column. Write padding + data + newline all together. */ + outbuf[sz] = '\n'; + fwrite(outbuf-space, space+sz+1, 1, stdout); + break; + } + /* Not the last column. Write padding + data together. */ + fwrite(outbuf-space, space+sz, 1, stdout); + actual += space+amount; + correct += fmt->width; + correct += legit; /* adjust for SIGNAL expansion */ + if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */ + correct++; + dospace = 1; + }else{ + dospace = 0; + } + fmt = fmt->next; + /* At this point: + * + * correct screen position we should be at + * actual screen position we are at + * amount not needed + * leftpad not needed + * space not needed + * dospace if have determined that we need a space next time + * legit not needed + */ + } +} + + +#ifdef TESTING +static void sanity_check(void){ + format_struct *fs = format_array; + while((fs->spec)[0] != '~'){ + if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec); + fs++; + } +} +#endif + + +void init_output(void){ + int outbuf_pages; + char *outbuf; + + switch(page_size){ + case 65536: page_shift = 16; break; + case 32768: page_shift = 15; break; + case 16384: page_shift = 14; break; + case 8192: page_shift = 13; break; + default: fprintf(stderr, "Unknown page size! (assume 4096)\n"); + case 4096: page_shift = 12; break; + case 2048: page_shift = 11; break; + case 1024: page_shift = 10; break; + } + + // add page_size-1 to round up + outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size; + outbuf = mmap( + 0, + page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0 + ); + memset(outbuf, ' ', SPACE_AMOUNT); + if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ); + mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page + saved_outbuf = outbuf + SPACE_AMOUNT; + // available space: page_size*outbuf_pages-SPACE_AMOUNT + + seconds_since_1970 = time(NULL); + time_of_boot = seconds_since_1970 - seconds_since_boot; + + meminfo(); + + check_header_width(); +} diff --git a/ps/p b/ps/p new file mode 100755 index 0000000..10064fa --- /dev/null +++ b/ps/p @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with: ps "pid,user pcpu,pmem" +# The "$@" won't break that into 2 arguments. +# +LD_LIBRARY_PATH=../proc exec ./ps "$@" diff --git a/ps/parser.c b/ps/parser.c new file mode 100644 index 0000000..5ad9035 --- /dev/null +++ b/ps/parser.c @@ -0,0 +1,1246 @@ +/* + * Copyright 1998-2003 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* Ought to have debug print stuff like this: + * #define Print(fmt, args...) printf("Debug: " fmt, ## args) + */ + +#include +#include +#include + +/* username lookups */ +#include +#include +#include + +#include +#include + +#include "common.h" +#include "../proc/version.h" + +#define ARG_GNU 0 +#define ARG_END 1 +#define ARG_PGRP 2 +#define ARG_SYSV 3 +#define ARG_PID 4 +#define ARG_BSD 5 +#define ARG_FAIL 6 +#define ARG_SESS 7 + +static int w_count = 0; + +static int ps_argc; /* global argc */ +static char **ps_argv; /* global argv */ +static int thisarg; /* index into ps_argv */ +static char *flagptr; /* current location in ps_argv[thisarg] */ +static int not_pure_unix = 0; /* set by BSD and GNU options */ +static int force_bsd = 0; /* set when normal parsing fails */ + +#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x))\ + return "The " x " option is exclusive." + + +/********** utility functions **********/ + +/* + * Both "-Oppid" and "-O ppid" should be legal, though Unix98 + * does not require it. BSD and Digital Unix allow both. + * Return the argument or NULL; + */ +static const char *get_opt_arg(void){ + if(*(flagptr+1)){ /* argument is part of ps_argv[thisarg] */ + not_pure_unix = 1; + return flagptr+1; + } + if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */ + /* argument follows ps_argv[thisarg] */ + if(*(ps_argv[thisarg+1]) == '\0') return NULL; + return ps_argv[++thisarg]; +} + +/********** parse lists (of UID, tty, GID, PID...) **********/ + +static const char *parse_pid(char *str, sel_union *ret){ + char *endp; + unsigned long num; + static const char pidrange[] = "Process ID out of range."; + static const char pidsyntax[] = "Process ID list syntax error."; + num = strtoul(str, &endp, 0); + if(*endp != '\0') return pidsyntax; + if(num<1) return pidrange; + if(num > 0x7fffffffUL) return pidrange; + ret->pid = num; + return 0; +} + +static const char *parse_uid(char *str, sel_union *ret){ + struct passwd *passwd_data; + char *endp; + unsigned long num; + static const char uidrange[] = "User ID out of range."; + static const char uidexist[] = "User name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + passwd_data = getpwnam(str); + if(!passwd_data) return uidexist; + num = passwd_data->pw_uid; + } + if(num > 0xfffffffeUL) return uidrange; + ret->uid = num; + return 0; +} + +static const char *parse_gid(char *str, sel_union *ret){ + struct group *group_data; + char *endp; + unsigned long num; + static const char gidrange[] = "Group ID out of range."; + static const char gidexist[] = "Group name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + group_data = getgrnam(str); + if(!group_data) return gidexist; + num = group_data->gr_gid; + } + if(num > 0xfffffffeUL) return gidrange; + ret->gid = num; + return 0; +} + +static const char *parse_cmd(char *str, sel_union *ret){ + strncpy(ret->cmd, str, sizeof ret->cmd); // strncpy pads to end + return 0; +} + +static const char *parse_tty(char *str, sel_union *ret){ + struct stat sbuf; + static const char missing[] = "TTY could not be found."; + static const char not_tty[] = "List member was not a TTY."; + char path[4096]; + if(str[0]=='/'){ + if(stat(str, &sbuf) >= 0) goto found_it; + return missing; + } +#define lookup(p) \ + snprintf(path,4096,p,str); \ + if(stat(path, &sbuf) >= 0) goto found_it + + lookup("/dev/pts/%s"); /* New Unix98 ptys go first */ + lookup("/dev/%s"); + lookup("/dev/tty%s"); + lookup("/dev/pty%s"); + lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */ + if(!strcmp(str,"-")){ /* "-" means no tty (from AIX) */ + ret->tty = 0; /* processes w/o tty */ + return 0; + } + if(!strcmp(str,"?")){ /* "?" means no tty, which bash eats (Reno BSD?) */ + ret->tty = 0; /* processes w/o tty */ + return 0; + } + if(!*(str+1) && (stat(str,&sbuf)>=0)){ /* Kludge! Assume bash ate '?'. */ + ret->tty = 0; /* processes w/o tty */ + return 0; + } +#undef lookup + return missing; +found_it: + if(!S_ISCHR(sbuf.st_mode)) return not_tty; + ret->tty = sbuf.st_rdev; + return 0; +} + +/* + * Used to parse lists in a generic way. (function pointers) + */ +static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){ + selection_node *node; + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t," */ + char *walk; + int items; + int need_item; + const char *err; /* error code that could or did happen */ + /*** prepare to operate ***/ + node = malloc(sizeof(selection_node)); + node->u = malloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */ + node->n = 0; + buf = malloc(strlen(arg)+1); + strcpy(buf, arg); + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + err = "Improper list."; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\0': + if(need_item) goto parse_error; + need_item=1; + break; + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); + if(need_item) goto parse_error; + node->n = items; + /*** actually parse the list ***/ + walk = buf; + while(items--){ + sep_loc = strpbrk(walk," ,\t"); + if(sep_loc) *sep_loc = '\0'; + if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + node->next = selection_list; + selection_list = node; + return NULL; +parse_error: + free(buf); + free(node->u); + free(node); + return err; +} + +/***************** parse SysV options, including Unix98 *****************/ +static const char *parse_sysv_option(void){ + const char *arg; + const char *err; + + flagptr = ps_argv[thisarg]; + while(*++flagptr){ + // Find any excuse to ignore stupid Unix98 misfeatures. + // + // This list of options is ONLY for those defined by the + // "IEEE Std 1003.1, 2004 Edition", "ISO/IEC 9945:2003", + // or "Version 2 of the Single Unix Specification". + // + // It may be time to re-think the existance of this list. + // In the meantime, please do not add to it. The list is + // intended to ONLY contain flags defined by the POSIX and UNIX + // standards published by The Open Group, IEEE, and ISO. + if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX + + switch(*flagptr){ + case 'A': + trace("-A selects all processes.\n"); + all_processes = 1; + break; + case 'C': /* end */ + trace("-C select by process name.\n"); /* Why only HP/UX and us? */ + arg=get_opt_arg(); + if(!arg) return "List of command names must follow -C."; + err=parse_list(arg, parse_cmd); + if(err) return err; + selection_list->typecode = SEL_COMM; + return NULL; /* can't have any more options */ + case 'F': /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */ + trace("-F does fuller listing\n"); + format_modifiers |= FM_F; + format_flags |= FF_Uf; + unix_f_option = 1; /* does this matter? */ + break; + case 'G': /* end */ + trace("-G select by RGID (supports names)\n"); + arg=get_opt_arg(); + if(!arg) return "List of real groups must follow -G."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_RGID; + return NULL; /* can't have any more options */ + case 'H': /* another nice HP/UX feature */ + trace("-H Process hierarchy (like ASCII art forest option)\n"); + forest_type = 'u'; + break; +#if 0 + case 'J': // specify list of job IDs in hex (IRIX) -- like HP "-R" maybe? + trace("-J select by job ID\n"); // want a JID ("jid") for "-j" too + arg=get_opt_arg(); + if(!arg) return "List of jobs must follow -J."; + err=parse_list(arg, parse_jid); + if(err) return err; + selection_list->typecode = SEL_JID; + return NULL; /* can't have any more options */ +#endif + case 'L': /* */ + /* In spite of the insane 2-level thread system, Sun appears to + * have made this option Linux-compatible. If a process has N + * threads, ps will produce N lines of output. (not N+1 lines) + * Zombies are the only exception, with NLWP==0 and 1 output line. + * SCO UnixWare uses -L too. + */ + trace("-L Print LWP (thread) info.\n"); + thread_flags |= TF_U_L; +// format_modifiers |= FM_L; + break; + case 'M': // typically the SE Linux context + trace("-M Print security label for Mandatory Access Control.\n"); + format_modifiers |= FM_M; + break; + case 'N': + trace("-N negates.\n"); + negate_selection = 1; + break; + case 'O': /* end */ + trace("-O is preloaded -o.\n"); + arg=get_opt_arg(); + if(!arg) return "Format or sort specification must follow -O."; + defer_sf_option(arg, SF_U_O); + return NULL; /* can't have any more options */ + case 'P': /* SunOS 5 "psr" or unknown HP/UX feature */ + trace("-P adds columns of PRM info (HP-UX), PSR (SunOS), or capabilities (IRIX)\n"); + format_modifiers |= FM_P; + break; +#if 0 + case 'R': // unknown HP/UX feature, like IRIX "-J" maybe? + trace("-R select by PRM group\n"); + arg=get_opt_arg(); + if(!arg) return "List of PRM groups must follow -R."; + err=parse_list(arg, parse_prm); + if(err) return err; + selection_list->typecode = SEL_PRM; + return NULL; /* can't have any more options */ +#endif + case 'T': + /* IRIX 6.5 docs suggest POSIX threads get shown individually. + * This would make -T be like -L, -m, and m. (but an extra column) + * Testing (w/ normal processes) shows 1 line/process, not 2. + * Also, testing shows PID==SPID for all normal processes. + */ + trace("-T adds strange SPID column (old sproc() threads?)\n"); + thread_flags |= TF_U_T; +// format_modifiers |= FM_T; + break; + case 'U': /* end */ + trace("-U select by RUID (supports names).\n"); + arg=get_opt_arg(); + if(!arg) return "List of real groups must follow -U."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_RUID; + return NULL; /* can't have any more options */ + case 'V': /* single */ + trace("-V prints version.\n"); + exclusive("-V"); + display_version(); + exit(0); + // This must be verified against SVR4-MP. (UnixWare or Powermax) + // Leave it undocumented until that problem is solved. + case 'Z': /* full Mandatory Access Control level info */ + trace("-Z shows full MAC info\n"); + format_modifiers |= FM_M; + break; + case 'a': + trace("-a select all with a tty, but omit session leaders.\n"); + simple_select |= SS_U_a; + break; + case 'c': + /* HP-UX and SunOS 5 scheduling info modifier */ + trace("-c changes scheduling info.\n"); + format_modifiers |= FM_c; + break; + case 'd': + trace("-d select all, but omit session leaders.\n"); + simple_select |= SS_U_d; + break; + case 'e': + trace("-e selects all processes.\n"); + all_processes = 1; + break; + case 'f': + trace("-f does full listing\n"); + format_flags |= FF_Uf; + unix_f_option = 1; /* does this matter? */ + break; + case 'g': /* end */ + trace("-g selects by session leader OR by group name\n"); + arg=get_opt_arg(); + if(!arg) return "List of session leaders OR effective group names must follow -g."; + err=parse_list(arg, parse_pid); + if(!err){ + selection_list->typecode = SEL_SESS; + return NULL; /* can't have any more options */ + } + err=parse_list(arg, parse_gid); + if(!err){ + selection_list->typecode = SEL_EGID; + return NULL; /* can't have any more options */ + } + return "List of session leaders OR effective group IDs was invalid."; + case 'j': + trace("-j jobs format.\n"); + /* old Debian used RD_j and Digital uses JFMT */ + if(sysv_j_format) format_flags |= FF_Uj; + else format_modifiers |= FM_j; + break; + case 'l': + trace("-l long format.\n"); + format_flags |= FF_Ul; + break; + case 'm': + trace("-m shows threads.\n"); + /* note that AIX shows 2 lines for a normal process */ + thread_flags |= TF_U_m; + break; + case 'n': /* end */ + trace("-n sets namelist file.\n"); + arg=get_opt_arg(); + if(!arg) return "Alternate System.map file must follow -n."; + namelist_file = arg; + return NULL; /* can't have any more options */ + case 'o': /* end */ + /* Unix98 has gross behavior regarding this. From the following: */ + /* ps -o pid,nice=NICE,tty=TERMINAL,comm */ + /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm" */ + /* Yes, the second column has the name "NICE,tty=TERMINAL,comm" */ + /* This parser looks for any excuse to ignore that braindamage. */ + trace("-o user-defined format.\n"); + arg=get_opt_arg(); + if(!arg) return "Format specification must follow -o."; + not_pure_unix |= defer_sf_option(arg, SF_U_o); + return NULL; /* can't have any more options */ + case 'p': /* end */ + trace("-p select by PID.\n"); + arg=get_opt_arg(); + if(!arg) return "List of process IDs must follow -p."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ +#if 0 + case 'r': + trace("-r some Digital Unix thing about warnings...\n"); + trace(" or SCO's option to chroot() for new /proc and /dev.\n"); + return "The -r option is reserved."; + break; +#endif + case 's': /* end */ + trace("-s Select processes belonging to the sessions given.\n"); + arg=get_opt_arg(); + if(!arg) return "List of session IDs must follow -s."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_SESS; + return NULL; /* can't have any more options */ + case 't': /* end */ + trace("-t select by tty.\n"); + arg=get_opt_arg(); + if(!arg) return "List of terminals (pty, tty...) must follow -t."; + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; /* can't have any more options */ + case 'u': /* end */ + trace("-u select by user ID (the EUID?) (supports names).\n"); + arg=get_opt_arg(); + if(!arg) return "List of users must follow -u."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; /* can't have any more options */ + case 'w': + trace("-w wide output.\n"); + w_count++; + break; + case 'x': /* behind personality until "ps -ax" habit is uncommon */ + if(personality & PER_SVR4_x){ + // Same as -y, but for System V Release 4 MP + trace("-x works like Sun Solaris & SCO Unixware -y option\n"); + format_modifiers |= FM_y; + break; + } + if(personality & PER_HPUX_x){ + trace("-x extends the command line\n"); + w_count += 2; + unix_f_option = 1; + break; + } + return "Must set personality to get -x option."; + case 'y': /* Sun's -l hack (also: Irix "lnode" resource control info) */ + trace("-y Print lnone info in UID/USER column or do Sun -l hack.\n"); + format_modifiers |= FM_y; + break; +#if 0 + // This must be verified against SVR4-MP (UnixWare or Powermax) + case 'z': /* alias of Mandatory Access Control level info */ + trace("-z shows aliased MAC info\n"); + format_modifiers |= FM_M; + break; + // Solaris 10 does this + case 'z': /* select by zone */ + trace("-z secects by zone\n"); + arg=get_opt_arg(); + if(!arg) return "List of zones (contexts, labels, whatever?) must follow -z."; + err=parse_list(arg, parse_zone); + if(err) return err; + selection_list->typecode = SEL_ZONE; + return NULL; /* can't have any more options */ +#endif + case '-': + return "Embedded '-' among SysV options makes no sense."; + break; + case '\0': + return "Please report the \"SysV \\0 can't happen\" bug."; + break; + default: + return "Unsupported SysV option."; + } /* switch */ + } /* while */ + return NULL; +} + +/************************* parse BSD options **********************/ +static const char *parse_bsd_option(void){ + const char *arg; + const char *err; + + flagptr = ps_argv[thisarg]; /* assume we _have_ a '-' */ + if(flagptr[0]=='-'){ + if(!force_bsd) return "Can't happen! Problem #1."; + }else{ + flagptr--; /* off beginning, will increment before use */ + if(personality & PER_FORCE_BSD){ + if(!force_bsd) return "Can't happen! Problem #2."; + }else{ + if(force_bsd) return "2nd chance parse failed, not BSD or SysV."; + } + } + + while(*++flagptr){ + switch(*flagptr){ + case '0' ... '9': /* end */ + trace("0..9 Old BSD-style select by process ID\n"); + arg=flagptr; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ +#if 0 + case 'A': + /* maybe this just does a larger malloc() ? */ + trace("A Increases the argument space (Digital Unix)\n"); + return "Option A is reserved."; + break; + case 'C': + /* should divide result by 1-(e**(foo*log(bar))) */ + trace("C Use raw CPU time for %%CPU instead of decaying ave\n"); + return "Option C is reserved."; + break; +#endif + case 'H': // The FreeBSD way (NetBSD:s OpenBSD:k FreeBSD:H -- NIH???) + trace("H Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n"); + thread_flags |= TF_B_H; + //format_modifiers |= FM_L; // FIXME: determine if we need something like this + break; + case 'L': /* single */ + trace("L List all format specifiers\n"); + exclusive("L"); + print_format_specifiers(); + exit(0); + case 'M': // undocumented for now: these are proliferating! + trace("M MacOS X thread display, like AIX/Tru64\n"); + thread_flags |= TF_B_m; + break; + case 'N': /* end */ + trace("N Specify namelist file\n"); + arg=get_opt_arg(); + if(!arg) return "Alternate System.map file must follow N."; + namelist_file = arg; + return NULL; /* can't have any more options */ + case 'O': /* end */ + trace("O Like o + defaults, add new columns after PID. Also sort.\n"); + arg=get_opt_arg(); + if(!arg) return "Format or sort specification must follow O."; + defer_sf_option(arg, SF_B_O); + return NULL; /* can't have any more options */ + break; + case 'S': + trace("S include dead kids in sum\n"); + include_dead_children = 1; + break; + case 'T': + trace("T Select all processes on this terminal\n"); + /* put our tty on a tiny list */ + { + selection_node *node; + node = malloc(sizeof(selection_node)); + node->u = malloc(sizeof(sel_union)); + node->u[0].tty = cached_tty; + node->typecode = SEL_TTY; + node->n = 1; + node->next = selection_list; + selection_list = node; + } + break; + case 'U': /* end */ + trace("U Select processes for specified users.\n"); + arg=get_opt_arg(); + if(!arg) return "List of users must follow U."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; /* can't have any more options */ + case 'V': /* single */ + trace("V show version info\n"); + exclusive("V"); + display_version(); + exit(0); + case 'W': + trace("W N/A get swap info from ... not /dev/drum.\n"); + return "Obsolete W option not supported. (You have a /dev/drum?)"; + break; + case 'X': + trace("X Old Linux i386 register format\n"); + format_flags |= FF_LX; + break; + case 'Z': /* FreeBSD does MAC like SGI's Irix does it */ + trace("Z Print security label for Mandatory Access Control.\n"); + format_modifiers |= FM_M; + break; + case 'a': + trace("a Select all w/tty, including other users\n"); + simple_select |= SS_B_a; + break; + case 'c': + trace("c true command name\n"); + bsd_c_option = 1; + break; + case 'e': + trace("e environment\n"); + bsd_e_option = 1; + break; + case 'f': + trace("f ASCII art forest\n"); + forest_type = 'b'; + break; + case 'g': + trace("g _all_, even group leaders!.\n"); + simple_select |= SS_B_g; + break; + case 'h': + trace("h Repeat header... yow.\n"); + if(header_type) return "Only one heading option may be specified."; + if(personality & PER_BSD_h) header_type = HEAD_MULTI; + else header_type = HEAD_NONE; + break; + case 'j': + trace("j job control format\n"); + format_flags |= FF_Bj; + break; + case 'k': + // OpenBSD: don't hide "kernel threads" -- like the swapper? + // trace("k Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n"); + + // NetBSD, and soon (?) FreeBSD: sort-by-keyword + trace("k Specify sorting keywords.\n"); + arg=get_opt_arg(); + if(!arg) return "Long sort specification must follow 'k'."; + defer_sf_option(arg, SF_G_sort); + return NULL; /* can't have any more options */ + case 'l': + trace("l Display long format\n"); + format_flags |= FF_Bl; + break; + case 'm': + trace("m all threads, sort on mem use, show mem info\n"); + if(personality & PER_OLD_m){ + format_flags |= FF_Lm; + break; + } + if(personality & PER_BSD_m){ + defer_sf_option("pmem", SF_B_m); + break; + } + thread_flags |= TF_B_m; + break; + case 'n': + trace("n Numeric output for WCHAN, and USER replaced by UID\n"); + wchan_is_number = 1; + user_is_number = 1; + /* TODO add tty_is_number too? */ + break; + case 'o': /* end */ + trace("o Specify user-defined format\n"); + arg=get_opt_arg(); + if(!arg) return "Format specification must follow o."; + defer_sf_option(arg, SF_B_o); + return NULL; /* can't have any more options */ + case 'p': /* end */ + trace("p Select by process ID\n"); + arg=get_opt_arg(); + if(!arg) return "List of process IDs must follow p."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ + case 'r': + trace("r Select running processes\n"); + running_only = 1; + break; + case 's': + trace("s Display signal format\n"); + format_flags |= FF_Bs; + break; + case 't': /* end */ + trace("t Select by tty.\n"); + /* List of terminals (tty, pty...) _should_ follow t. */ + arg=get_opt_arg(); + if(!arg){ + /* Wow, obsolete BSD syntax. Put our tty on a tiny list. */ + selection_node *node; + node = malloc(sizeof(selection_node)); + node->u = malloc(sizeof(sel_union)); + node->u[0].tty = cached_tty; + node->typecode = SEL_TTY; + node->n = 1; + node->next = selection_list; + selection_list = node; + return NULL; + } + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; /* can't have any more options */ + case 'u': + trace("u Display user-oriented\n"); + format_flags |= FF_Bu; + break; + case 'v': + trace("v Display virtual memory\n"); + format_flags |= FF_Bv; + break; + case 'w': + trace("w wide output\n"); + w_count++; + break; + case 'x': + trace("x Select processes without controlling ttys\n"); + simple_select |= SS_B_x; + break; + case '-': + return "Embedded '-' among BSD options makes no sense."; + break; + case '\0': + return "Please report the \"BSD \\0 can't happen\" bug."; + break; + default: + return "Unsupported option (BSD syntax)"; + } /* switch */ + } /* while */ + return NULL; +} + +/*************** gnu long options **********************/ + +/* + * Return the argument or NULL + */ +static const char *grab_gnu_arg(void){ + switch(*flagptr){ /* argument is part of ps_argv[thisarg] */ + default: + return NULL; /* something bad */ + case '=': case ':': + if(*++flagptr) return flagptr; /* found it */ + return NULL; /* empty '=' or ':' */ + case '\0': /* try next argv[] */ + ; + } + if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */ + /* argument follows ps_argv[thisarg] */ + if(*(ps_argv[thisarg+1]) == '\0') return NULL; + return ps_argv[++thisarg]; +} + +typedef struct gnu_table_struct { + const char *name; /* long option name */ + const void *jump; /* See gcc extension info. :-) */ +} gnu_table_struct; + +static int compare_gnu_table_structs(const void *a, const void *b){ + return strcmp(((const gnu_table_struct*)a)->name,((const gnu_table_struct*)b)->name); +} + +/* Option arguments are after ':', after '=', or in argv[n+1] */ +static const char *parse_gnu_option(void){ + const char *arg; + const char *err; + char *s; + size_t sl; + char buf[16]; + gnu_table_struct findme = { buf, NULL}; + gnu_table_struct *found; + static const gnu_table_struct gnu_table[] = { + {"Group", &&case_Group}, /* rgid */ + {"User", &&case_User}, /* ruid */ + {"cols", &&case_cols}, + {"columns", &&case_columns}, + {"context", &&case_context}, + {"cumulative", &&case_cumulative}, + {"deselect", &&case_deselect}, /* -N */ + {"forest", &&case_forest}, /* f -H */ + {"format", &&case_format}, + {"group", &&case_group}, /* egid */ + {"header", &&case_header}, + {"headers", &&case_headers}, + {"heading", &&case_heading}, + {"headings", &&case_headings}, + {"help", &&case_help}, + {"info", &&case_info}, + {"lines", &&case_lines}, + {"no-header", &&case_no_header}, + {"no-headers", &&case_no_headers}, + {"no-heading", &&case_no_heading}, + {"no-headings", &&case_no_headings}, + {"noheader", &&case_noheader}, + {"noheaders", &&case_noheaders}, + {"noheading", &&case_noheading}, + {"noheadings", &&case_noheadings}, + {"pid", &&case_pid}, + {"ppid", &&case_ppid}, + {"rows", &&case_rows}, + {"sid", &&case_sid}, + {"sort", &&case_sort}, + {"tty", &&case_tty}, + {"user", &&case_user}, /* euid */ + {"version", &&case_version}, + {"width", &&case_width}, + }; + const int gnu_table_count = sizeof(gnu_table)/sizeof(gnu_table_struct); + + s = ps_argv[thisarg]+2; + sl = strcspn(s,":="); + if(sl > 15) return "Unknown gnu long option."; + strncpy(buf, s, sl); + buf[sl] = '\0'; + flagptr = s+sl; + + found = bsearch(&findme, gnu_table, gnu_table_count, + sizeof(gnu_table_struct), compare_gnu_table_structs + ); + + if(!found) return "Unknown gnu long option."; + + goto *(found->jump); /* See gcc extension info. :-) */ + + case_Group: + trace("--Group\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of real groups must follow --Group."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_RGID; + return NULL; + case_User: + trace("--User\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of real users must follow --User."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_RUID; + return NULL; + case_cols: + case_width: + case_columns: + trace("--cols\n"); + arg = grab_gnu_arg(); + if(arg && *arg){ + long t; + char *endptr; + t = strtol(arg, &endptr, 0); + if(!*endptr && (t>0) && (t<2000000000)){ + screen_cols = (int)t; + return NULL; + } + } + return "Number of columns must follow --cols, --width, or --columns."; + case_cumulative: + trace("--cumulative\n"); + if(s[sl]) return "Option --cumulative does not take an argument."; + include_dead_children = 1; + return NULL; + case_deselect: + trace("--deselect\n"); + if(s[sl]) return "Option --deselect does not take an argument."; + negate_selection = 1; + return NULL; + case_no_header: + case_no_headers: + case_no_heading: + case_no_headings: + case_noheader: + case_noheaders: + case_noheading: + case_noheadings: + trace("--noheaders\n"); + if(s[sl]) return "Option --no-heading does not take an argument."; + if(header_type) return "Only one heading option may be specified."; + header_type = HEAD_NONE; + return NULL; + case_header: + case_headers: + case_heading: + case_headings: + trace("--headers\n"); + if(s[sl]) return "Option --heading does not take an argument."; + if(header_type) return "Only one heading option may be specified."; + header_type = HEAD_MULTI; + return NULL; + case_forest: + trace("--forest\n"); + if(s[sl]) return "Option --forest does not take an argument."; + forest_type = 'g'; + return NULL; + case_format: + trace("--format\n"); + arg=grab_gnu_arg(); + if(!arg) return "Format specification must follow --format."; + defer_sf_option(arg, SF_G_format); + return NULL; + case_group: + trace("--group\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of effective groups must follow --group."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_EGID; + return NULL; + case_help: + trace("--help\n"); + exclusive("--help"); + fwrite(help_message,1,strlen(help_message),stdout); + exit(0); + return NULL; + case_info: + trace("--info\n"); + exclusive("--info"); + self_info(); + exit(0); + return NULL; + case_pid: + trace("--pid\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of process IDs must follow --pid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; + case_ppid: + trace("--ppid\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of process IDs must follow --ppid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PPID; + return NULL; + case_rows: + case_lines: + trace("--rows\n"); + arg = grab_gnu_arg(); + if(arg && *arg){ + long t; + char *endptr; + t = strtol(arg, &endptr, 0); + if(!*endptr && (t>0) && (t<2000000000)){ + screen_rows = (int)t; + return NULL; + } + } + return "Number of rows must follow --rows or --lines."; + case_sid: + trace("--sid\n"); + arg = grab_gnu_arg(); + if(!arg) return "Some sid thing(s) must follow --sid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_SESS; + return NULL; + case_sort: + trace("--sort\n"); + arg=grab_gnu_arg(); + if(!arg) return "Long sort specification must follow --sort."; + defer_sf_option(arg, SF_G_sort); + return NULL; + case_tty: + trace("--tty\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of ttys must follow --tty."; + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; + case_user: + trace("--user\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of effective users must follow --user."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; + case_version: + trace("--version\n"); + exclusive("--version"); + display_version(); + exit(0); + return NULL; + case_context: + trace("--context\n"); + format_flags |= FF_Fc; + return NULL; +} + +/*************** process trailing PIDs **********************/ +static const char *parse_trailing_pids(void){ + selection_node *pidnode; /* pid */ + selection_node *grpnode; /* process group */ + selection_node *sidnode; /* session */ + char **argp; /* pointer to pointer to text of PID */ + const char *err; /* error code that could or did happen */ + int i; + + i = ps_argc - thisarg; /* how many trailing PIDs, SIDs, PGRPs?? */ + argp = ps_argv + thisarg; + thisarg = ps_argc - 1; /* we must be at the end now */ + + pidnode = malloc(sizeof(selection_node)); + pidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + pidnode->n = 0; + + grpnode = malloc(sizeof(selection_node)); + grpnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + grpnode->n = 0; + + sidnode = malloc(sizeof(selection_node)); + sidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + sidnode->n = 0; + + while(i--){ + char *data; + data = *(argp++); + switch(*data){ + default: err = parse_pid( data, pidnode->u + pidnode->n++); break; + case '-': err = parse_pid(++data, grpnode->u + grpnode->n++); break; + case '+': err = parse_pid(++data, sidnode->u + sidnode->n++); break; + } + if(err) return err; /* the node gets freed with the list */ + } + + if(pidnode->n){ + pidnode->next = selection_list; + selection_list = pidnode; + selection_list->typecode = SEL_PID; + } /* else free both parts */ + + if(grpnode->n){ + grpnode->next = selection_list; + selection_list = grpnode; + selection_list->typecode = SEL_PGRP; + } /* else free both parts */ + + if(sidnode->n){ + sidnode->next = selection_list; + selection_list = sidnode; + selection_list->typecode = SEL_SESS; + } /* else free both parts */ + + return NULL; +} + +/************** misc stuff ***********/ + +static void reset_parser(void){ + w_count = 0; +} + +static int arg_type(const char *str){ + int tmp = str[0]; + if((tmp>='a') && (tmp<='z')) return ARG_BSD; + if((tmp>='A') && (tmp<='Z')) return ARG_BSD; + if((tmp>='0') && (tmp<='9')) return ARG_PID; + if(tmp=='+') return ARG_SESS; + if(tmp!='-') return ARG_FAIL; + tmp = str[1]; + if((tmp>='a') && (tmp<='z')) return ARG_SYSV; + if((tmp>='A') && (tmp<='Z')) return ARG_SYSV; + if((tmp>='0') && (tmp<='9')) return ARG_PGRP; + if(tmp!='-') return ARG_FAIL; + tmp = str[2]; + if((tmp>='a') && (tmp<='z')) return ARG_GNU; + if((tmp>='A') && (tmp<='Z')) return ARG_GNU; + if(tmp=='\0') return ARG_END; + return ARG_FAIL; +} + +/* First assume sysv, because that is the POSIX and Unix98 standard. */ +static const char *parse_all_options(void){ + const char *err = NULL; + int at; + while(++thisarg < ps_argc){ + trace("parse_all_options calling arg_type for \"%s\"\n", ps_argv[thisarg]); + at = arg_type(ps_argv[thisarg]); + trace("ps_argv[thisarg] is %s\n", ps_argv[thisarg]); + if(at != ARG_SYSV) not_pure_unix = 1; + switch(at){ + case ARG_GNU: + err = parse_gnu_option(); + break; + case ARG_SYSV: + if(!force_bsd){ /* else go past case ARG_BSD */ + err = parse_sysv_option(); + break; + case ARG_BSD: + if(force_bsd && !(personality & PER_FORCE_BSD)) return "way bad"; + } + prefer_bsd_defaults = 1; + err = parse_bsd_option(); + break; + case ARG_PGRP: + case ARG_SESS: + case ARG_PID: + prefer_bsd_defaults = 1; + err = parse_trailing_pids(); + break; + case ARG_END: + case ARG_FAIL: + trace(" FAIL/END on [%s]\n",ps_argv[thisarg]); + return "Garbage option."; + break; + default: + printf(" ? %s\n",ps_argv[thisarg]); + return "Something broke."; + } /* switch */ + if(err) return err; + } /* while */ + return NULL; +} + +static void choose_dimensions(void){ + if(w_count && (screen_cols<132)) screen_cols=132; + if(w_count>1) screen_cols=OUTBUF_SIZE; + /* perhaps --html and --null should set unlimited width */ +} + +static const char *thread_option_check(void){ + if(!thread_flags){ + thread_flags = TF_show_proc; + return NULL; + } + + if(forest_type){ + return "Thread display conflicts with forest display."; + } + //thread_flags |= TF_no_forest; + + if((thread_flags&TF_B_H) && (thread_flags&(TF_B_m|TF_U_m))) + return "Thread flags conflict; can't use H with m or -m."; + if((thread_flags&TF_B_m) && (thread_flags&TF_U_m)) + return "Thread flags conflict; can't use both m and -m."; + if((thread_flags&TF_U_L) && (thread_flags&TF_U_T)) + return "Thread flags conflict; can't use both -L and -T."; + + if(thread_flags&TF_B_H) thread_flags |= (TF_show_proc|TF_loose_tasks); + if(thread_flags&(TF_B_m|TF_U_m)) thread_flags |= (TF_show_proc|TF_show_task|TF_show_both); + + if(thread_flags&(TF_U_T|TF_U_L)){ + if(thread_flags&(TF_B_m|TF_U_m|TF_B_H)){ + // Got a thread style, so format modification is a requirement? + // Maybe -T/-L has H thread style though. (sorting interaction?) + //return "Huh? Tell procps-feedback@lists.sf.net what you expected."; + thread_flags |= TF_must_use; + }else{ + // using -L/-T thread style, so format from elsewhere is OK + thread_flags |= TF_show_task; // or like the H option? + //thread_flags |= TF_no_sort; + } + } + + return NULL; +} + +int arg_parse(int argc, char *argv[]){ + const char *err = NULL; + const char *err2 = NULL; + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + + if(personality & PER_FORCE_BSD) goto try_bsd; + + err = parse_all_options(); + if(err) goto try_bsd; + err = thread_option_check(); + if(err) goto try_bsd; + err = process_sf_options(!not_pure_unix); + if(err) goto try_bsd; + err = select_bits_setup(); + if(err) goto try_bsd; + + choose_dimensions(); + return 0; + +try_bsd: + trace("--------- now try BSD ------\n"); + + reset_global(); + reset_parser(); + reset_sortformat(); + format_flags = 0; + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + /* no need to reset flagptr */ + not_pure_unix=1; + force_bsd=1; + prefer_bsd_defaults=1; + if(!( (PER_OLD_m|PER_BSD_m) & personality )) /* if default m setting... */ + personality |= PER_OLD_m; /* Prefer old Linux over true BSD. */ + /* Do not set PER_FORCE_BSD! It is tested below. */ + + err2 = parse_all_options(); + if(err2) goto total_failure; + err2 = thread_option_check(); + if(err2) goto total_failure; + err2 = process_sf_options(!not_pure_unix); + if(err2) goto total_failure; + err2 = select_bits_setup(); + if(err2) goto total_failure; + + // Feel a need to patch this out? First of all, read the FAQ. + // Second of all, talk to me. Without this warning, people can + // get seriously confused. Ask yourself if users would freak out + // about "ps -aux" suddenly changing behavior if a user "x" were + // added to the system. + // + // Also, a "-x" option is coming. It's already there in fact, + // for some non-default personalities. So "ps -ax" will parse + // as SysV options... and you're screwed if you've been patching + // out the friendly warning. Cut-over is likely to be in 2005. + if(!(personality & PER_FORCE_BSD)) + fprintf(stderr, "Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html\n"); + // Remember: contact albert@users.sf.net or procps-feedback@lists.sf.net + // if you should feel tempted. Be damn sure you understand all + // the issues. The same goes for other stuff too, BTW. Please ask. + // I'm happy to justify various implementation choices. + + choose_dimensions(); + return 0; + +total_failure: + reset_parser(); + if(personality & PER_FORCE_BSD) fprintf(stderr, "ERROR: %s\n", err2); + else fprintf(stderr, "ERROR: %s\n", err); + fwrite(help_message,1,strlen(help_message),stderr); + exit(1); + /* return 1; */ /* useless */ +} diff --git a/ps/ps.1 b/ps/ps.1 new file mode 100644 index 0000000..64953d5 --- /dev/null +++ b/ps/ps.1 @@ -0,0 +1,1521 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" Man page for ps. +.\" Quick hack conversion by Albert Cahalan, 1998. +.\" Licensed under version 2 of the Gnu General Public License. +.\" +.TH PS 1 "July 28, 2004" "Linux" "Linux User's Manual" +.\" +.\" To render this page: +.\" groff -t -b -man -X -P-resolution -P100 -Tps ps.1 & +.\" groff -t -b -man -X -TX100 ps.1 & +.\" tbl ps.1 | troff -Ww -man -z +.\" groff -t -man -Tps ps.1 | ps2pdf - - > ps.pdf +.\" +.\" The '70s called. They want their perfect justification, +.\" hyphenation, and double-spaced sentences back. +.na +.nh +.if n .ss 12 0 +.\" +.\" See /usr/share/groff/current/tmac/an-old.tmac for what these do. +.\" Setting them to zero provides extra space, but only do that for +.\" plain text output. PostScript and such will remain indented. +.if n .nr IN 0n +.if n .nr an-prevailing-indent 0n +.\" +.\" +.\" ColSize is used for the format spec table. +.\" It's the left margin, minus the right, minus +.\" the space needed for the 1st two columns. +.\" Making it messy: inches, ens, points, scaled points... +.\" +.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-20u)n) +.\" +.\" This is for command options +.nr OptSize (16u) +.\" +.\" l=\n(.l +.\" i=\n(.i +.\" o=\n(.o +.\" H=\n(.H +.\" s=\n(.s +.\" ColSize=\n[ColSize] +.\" +.\" Macro for easy option formatting: .opt \-x +.de opt +. TP \\n[OptSize] +. BI \\$* +.. +.\" +.SH NAME +ps \- report a snapshot of the current processes. +.SH SYNOPSIS +\fBps\fR [\fIoptions\fR] +.PP +.PP +.SH DESCRIPTION +.B ps +displays information about a selection of the active processes. +If you want a repetitive update of the selection and the +displayed information, use\ \fItop\fR(1) instead. +.P +This version of \fBps\fR accepts several kinds of options: +.PD 0 +.IP 1 4 +UNIX options, which may be grouped and must be preceded by a dash. +.IP 2 4 +BSD options, which may be grouped and must not be used with a dash. +.IP 3 4 +GNU long options, which are preceded by two dashes. +.PD +.PP +Options of different types may be freely mixed, but conflicts can appear. +There are some synonymous options, which are functionally identical, due +to the many standards and \fBps\fR implementations that this \fBps\fR is +compatible with. +.P +Note that "\fBps\ \-aux\fR" is distinct from "\fBps\ aux\fR". +The POSIX and UNIX standards require that "\fBps\ \-aux\fR" print all +processes owned by a user named "x", as well as printing all processes +that would be selected by the \fB\-a\fR option. If the user named "x" does +not exist, this \fBps\fR may interpret the command as "\fBps\ aux\fR" +instead and print a warning. This behavior is intended to aid in +transitioning old scripts and habits. It is fragile, subject to change, +and thus should not be relied upon. +.P +By default, \fBps\fR selects all processes +with the same effective user ID (euid=EUID) as the current user +and +associated with the same terminal as the invoker. +It displays the process ID (pid=PID), +the terminal associated with the process (tname=TTY), +the cumulated CPU time in [dd\-]hh:mm:ss format (time=TIME), +and the executable name (ucmd=CMD). +Output is unsorted by default. +.P +The use of BSD\-style options will add process state (stat=STAT) to the +default display and show the command args (args=COMMAND) instead of the +executable name. You can override this with the \fBPS_FORMAT\fR +environment variable. The use of BSD\-style options will also change the +process selection to include processes on other terminals (TTYs) that +are owned by you; alternately, this may be described as setting the +selection to be the set of all processes filtered to exclude +processes owned by other users or not on a terminal. These effects +are not considered when options are described as being "identical" below, +so \fB\-M\fR will be considered identical to \fBZ\fR and so on. +.P +Except as described below, process selection options are additive. +The default selection is discarded, and then the selected processes +are added to the set of processes to be displayed. +A\ process will thus be shown if it meets any of the given +selection criteria. +.PP +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.SH "EXAMPLES" +.TP 3 +To see every process on the system using standard syntax: +.B ps\ \-e +.br +.B ps\ \-ef +.br +.B ps\ \-eF +.br +.B ps\ \-ely +.TP +To see every process on the system using BSD syntax: +.B ps\ ax +.br +.B ps\ axu +.TP +To print a process tree: +.B ps\ -ejH +.br +.B ps\ axjf +.TP +To get info about threads: +.B ps\ -eLf +.br +.B ps\ axms +.TP +To get security info: +.B ps\ -eo euser,ruser,suser,fuser,f,comm,label +.br +.B ps\ axZ +.br +.B ps\ -eM +.TP +To see every process running as root (real\ &\ effective\ ID) in user format: +.B ps\ \-U\ root\ \-u\ root\ u +.TP +To see every process with a user\-defined format: +.B ps\ \-eo\ pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm +.br +.B ps\ axo\ stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm +.br +.B ps\ \-eopid,tt,user,fname,tmout,f,wchan +.TP +Print only the process IDs of syslogd: +.B ps\ \-C\ syslogd\ \-o\ pid= +.TP +Print only the name of PID 42: +.B ps\ \-p\ 42\ \-o\ comm= +.PP +.PP +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.SH "SIMPLE PROCESS SELECTION" +.opt \-A +Select all processes. Identical to \fB\-e\fR. + +.opt \-N +Select all processes except those that fulfill the specified conditions. +(negates the selection) Identical to \fB\-\-deselect\fR. + +.opt T +Select all processes associated with this terminal. Identical to the +\fBt\fR option without any argument. + +.opt \-a +Select all processes except both session leaders (see \fIgetsid\fR(2)) and +processes not associated with a terminal. + +.opt a +Lift the BSD\-style "only yourself" restriction, which is imposed upon +the set of all processes when some BSD\-style (without\ "\-") options +are used or when the \fBps\fR personality setting is BSD\-like. +The set of processes selected in this manner is +in addition to the set of processes selected by other means. +An alternate description is that this option causes \fBps\fR to +list all processes with a terminal (tty), +or to list all processes when used together with the \fBx\fR option. + +.opt \-d +Select all processes except session leaders. + +.opt \-e +Select all processes. Identical to \fB\-A\fR. + +.\" Current "g" behavior: add in the session leaders, which would +.\" be excluded in the sunos4 personality. Supposed "g" behavior: +.\" add in the group leaders -- at least according to the SunOS 4 +.\" man page on the FreeBSD site. Uh oh. I think I had tested SunOS +.\" though, so maybe the code is correct. +.opt g +Really all, even session leaders. This flag is obsolete and may be +discontinued in a future release. It is normally implied by the \fBa\fR flag, +and is only useful when operating in the sunos4 personality. + +.opt r +Restrict the selection to only running processes. + +.opt x +Lift the BSD\-style "must have a tty" restriction, which is imposed upon +the set of all processes when some BSD\-style (without\ "\-") options +are used or when the \fBps\fR personality setting is BSD\-like. +The set of processes selected in this manner is +in addition to the set of processes selected by other means. +An alternate description is that this option causes \fBps\fR to +list all processes owned by you (same EUID as \fBps\fR), +or to list all processes when used together with the \fBa\fR option. + +.opt \-\-deselect +Select all processes except those that fulfill the specified conditions. +(negates the selection) Identical to \fB\-N\fR. + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "PROCESS SELECTION BY LIST" +These options accept a single argument in the form of a blank\-separated +or comma\-separated list. They can be used multiple times. +For\ example:\ \fBps\ \-p\ "1\ 2"\ \-p\ 3,4\fR +.P +.opt \-C \ cmdlist +Select by command name. +.br +This selects the processes whose executable name is given in +\fIcmdlist\fR. + +.opt \-G \ grplist +Select by real group ID (RGID) or name. +.br +This selects the processes whose real group name or ID is in the +\fIgrplist\fR list. The real group ID identifies the group of the user +who created the process, see \fIgetgid\fR(2). + +.opt U \ userlist +Select by effective user ID (EUID) or name. +.br +This selects the processes whose effective user name +or ID is in \fIuserlist\fR. +The effective user\ ID describes the user whose file +access permissions are used by the process +(see\ \fIgeteuid\fR(2)). +Identical to \fB\-u\fR and\ \fB\-\-user\fR. + +.opt \-U \ userlist +select by real user ID (RUID) or name. +.br +It selects the processes whose real user name or ID is in the +\fIuserlist\fR list. +The real user ID identifies the user who created the process, +see\ \fIgetuid\fR(2). + +.opt \-g \ grplist +Select by session OR by effective group name. +.br +Selection by session is specified by many standards, +but selection by effective group is the logical behavior that +several other operating systems use. +This \fBps\fR will select by session when the list +is completely numeric (as\ sessions\ are). +Group ID numbers will work only when some group names are also specified. +See the \fB\-s\fR and \fB\-\-group\fR options. + +.opt p \ pidlist +Select by process ID. Identical to \fB\-p\fR and\ \fB\-\-pid\fR. + +.opt \-p \ pidlist +Select by PID. +.br +This selects the processes whose process ID numbers appear in +\fIpidlist\fR. Identical to \fBp\fR and\ \fB\-\-pid\fR. + +.opt \-s \ sesslist +Select by session ID. +.br +This selects the processes +with a session ID specified in\ \fIsesslist\fR. + +.opt t \ ttylist +Select by tty. Nearly identical to \fB\-t\fR and \fB\-\-tty\fR, +but can also be used with an empty \fIttylist\fR to indicate +the terminal associated with \fBps\fR. +Using the \fBT\fR option is considered cleaner than using \fBT\fR with +an\ empty\ \fIttylist\fR. + +.opt \-t \ ttylist +Select by tty. +.br +This selects the processes associated with the terminals +given in \fIttylist\fR. +Terminals (ttys, or screens for text output) can be specified in several +forms: /dev/ttyS1, ttyS1, S1. +A\ plain "\-" may be used to select processes not attached to any terminal. + +.opt \-u \ userlist +Select by effective user ID (EUID) or name. +.br +This selects the processes whose effective user name or ID is in +\fIuserlist\fR. The effective user ID describes the user whose file +access permissions are used by the process (see\ \fIgeteuid\fR(2)). +Identical to \fBU\fR and \fB\-\-user\fR. + +.opt \-\-Group \ grplist +Select by real group ID (RGID) or name. Identical to \fB\-G\fR. + +.opt \-\-User \ userlist +Select by real user ID (RUID) or name. Identical to \fB\-U\fR. + +.opt \-\-group \ grplist +Select by effective group ID (EGID) or name. +.br +This selects the processes whose effective group name or ID is in +\fIgrouplist\fR. The effective group ID describes the group whose file +access permissions are used by the process (see\ \fIgeteuid\fR(2)). +The \fB\-g\fR option is often an alternative to\ \fB\-\-group\fR. + +.opt \-\-pid \ pidlist +Select by process\ ID. Identical to \fB\-p\fR\ and\ \fBp\fR. + +.opt \-\-ppid \ pidlist +Select by parent process\ ID. +This selects the processes +with a parent\ process\ ID in \fRpidlist\fR. +That\ is, it selects processes that are children +of those listed in \fRpidlist\fR. + +.opt \-\-sid \ sesslist +Select by session\ ID. Identical to\ \fB\-s\fR. + +.opt \-\-tty \ ttylist +Select by terminal. Identical to \fB\-t\fR and\ \fBt\fR. + +.opt \-\-user \ userlist +Select by effective user ID (EUID) or name. +Identical to \fB\-u\fR and\ \fBU\fR. + +.opt \-\fI123\fR +Identical to \fB\-\-sid\ \fI123\fR. + +.opt \fI123\fR +Identical to \fB\-\-pid\ \fI123\fR. + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "OUTPUT FORMAT CONTROL" +These options are used to choose the information displayed by \fBps\fR. +The output may differ by personality. +.PP + +.opt \-F +extra full format. See the \fB\-f\fR option, which \fB\-F\fR implies. + +.opt \-O \ format +is like \fB\-o\fR, but preloaded with some default columns. +Identical to \fB\-o\ pid,\fIformat\fB,state,tname,time,command\fR +or \fB\-o\ pid,\fIformat\fB,tname,time,cmd\fR, see\ \fB\-o\fR\ below. + +.opt O \ format +is preloaded \fBo\fR (overloaded). +.br +The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output +format with some common fields predefined) or can be used to specify +sort order. Heuristics are used to determine the behavior of this +option. To ensure that the desired behavior is obtained (sorting or +formatting), specify the option in some other way +(e.g. with \fB\-O\fR or \fB\-\-sort\fR). +When used as a formatting option, it is identical to \fB\-O\fR, with the +BSD\ personality. + +.opt \-M +Add a column of security data. Identical to \fBZ\fR. (for\ SE\ Linux) + +.opt X +Register format. + +.opt Z +Add a column of security data. Identical to \fB\-M\fR. (for\ SE\ Linux) + +.opt \-c +Show different scheduler information for the \fB\-l\fR option. + +.opt \-f +does full\-format listing. This option can be combined with many +other UNIX\-style options to add additional columns. It also causes +the command arguments to be printed. When used with \fB\-L\fR, the +NLWP (number of threads) and LWP (thread ID) columns will be added. +See the \fBc\fR option, the format keyword \fBargs\fR, and the +format keyword \fBcomm\fR. + +.opt j +BSD job control format. + +.opt \-j +jobs format + +.opt l +display BSD long format. + +.opt \-l +long format. The \fB\-y\fR option is often useful with this. + +.opt o \ format +specify user\-defined format. Identical to \fB\-o\fR and +\fB\-\-format\fR. + +.opt \-o \ format +user\-defined format. +.br +\fIformat\fR is a single argument in the form of a +blank\-separated or comma\-separated list, which offers +a way to specify individual output columns. +The recognized keywords are described in the \fBSTANDARD FORMAT +SPECIFIERS\fR section below. +Headers may be +renamed (\fBps\ \-o\ pid,ruser=RealUser\ \-o\ comm=Command\fR) as desired. +If all column headers are empty (\fBps\ \-o\ pid=\ \-o\ comm=\fR) then the +header line will not be output. Column width will increase as +needed for wide headers; this may be used to widen up columns +such as WCHAN (\fBps\ \-o\ pid,wchan=WIDE\-WCHAN\-COLUMN\ \-o\ comm\fR). +Explicit width control (\fBps\ opid,wchan:42,cmd\fR) is offered too. +The behavior of \fBps\ \-o\ pid=X,comm=Y\fR varies with personality; +output may be one column named "X,comm=Y" or two columns +named "X" and "Y". Use multiple \fB\-o\fR options when in doubt. +Use the \fBPS_FORMAT\fR environment variable to specify a default +as desired; DefSysV and DefBSD are macros that may be used to +choose the default UNIX or BSD columns. + +.opt s +display signal format + +.opt u +display user\-oriented format + +.opt v +display virtual memory format + +.opt \-y +Do not show flags; show rss in place of addr. +This option can only be used with \fB\-l\fR. + +.opt \-\-format \ format +user\-defined format. Identical to \fB\-o\fR and \fBo\fR. + +.opt \-\-context +Display security context format. (for\ SE\ Linux) + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "OUTPUT MODIFIERS" + +.\" .TP +.\" .B C +.\" use raw CPU time for %CPU instead of decaying average + +.opt \-H +show process hierarchy (forest) + +.opt N \ namelist +Specify namelist file. Identical to \fB\-n\fR, see \fB\-n\fR above. + +.opt O \ order +Sorting order. (overloaded) +.br +The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output +format with some common fields predefined) or can be used to specify +sort order. Heuristics are used to determine the behavior of this +option. To ensure that the desired behavior is obtained (sorting or +formatting), specify the option in some other way (e.g. with \fB\-O\fR +or \fB\-\-sort\fR). + +For sorting, obsolete BSD \fBO\fR option syntax is +\fBO\fR[\fB+\fR|\fB\-\fR]\fIk1\fR[,[\fB+\fR|\fB\-\fR]\fIk2\fR[,...]]. +It orders the processes listing according to the multilevel sort specified by +the sequence of one\-letter short keys \fIk1\fR, \fIk2\fR, ... described +in the \fBOBSOLETE SORT KEYS\fR section below. +The\ "+" is currently optional, +merely re\-iterating the default direction on a key, +but may help to distinguish an \fBO\fR sort from an \fBO\fR format. +The\ "\-" reverses direction only on the key it precedes. + +.opt S +Sum up some information, such as CPU usage, from dead child processes +into their parent. This is useful for examining a system where a +parent process repeatedly forks off short\-lived children to do work. + +.opt c +Show the true command name. This is derived from the name of the +executable file, rather than from the argv value. Command arguments +and any modifications to them (see\ \fIsetproctitle\fR(3)) are +thus not shown. This option +effectively turns the \fBargs\fR format keyword into the \fBcomm\fR +format keyword; it is useful with the \fB\-f\fR format option and with +the various BSD\-style format options, which all normally +display the command arguments. +See the \fB\-f\fR option, the format keyword \fBargs\fR, and the +format keyword \fBcomm\fR. + +.opt e +Show the environment after the command. + +.opt f +ASCII\-art process hierarchy (forest) + +.opt h +No header. (or, one header per screen in the BSD personality) +.br +The \fBh\fR option is problematic. Standard BSD \fBps\fR uses +this option to print a header on each page of output, but older +Linux \fBps\fR uses this option to totally disable the header. +This version of \fBps\fR follows the Linux usage of not printing +the header unless the BSD personality has been selected, in which +case it prints a header on each page of output. Regardless of the +current personality, you can use the long options \fB\-\-headers\fR +and \fB\-\-no\-headers\fR to enable printing headers each page or +disable headers entirely, respectively. + +.opt k \ spec +specify sorting order. Sorting syntax is +[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]] +Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section. +The\ "+" is optional since default direction is increasing numerical or +lexicographic order. Identical to \fB\-\-sort\fR. Examples: +.br +\fBps\ jaxkuid,\-ppid,+pid\fR +.br +\fBps\ axk\ comm\ o\ comm,args\fR +.br +\fBps\ kstart_time\ \-ef\fR + +.opt \-n \ namelist +set namelist file. Identical to \fBN\fR. +.br +The namelist file is needed for a proper WCHAN display, and must match +the current Linux kernel exactly for correct output. +Without this option, the default search path for the namelist is: + + $PS_SYSMAP +.br + $PS_SYSTEM_MAP +.br + /proc/*/wchan +.br + /boot/System.map\-\`uname\ \-r\` +.br + /boot/System.map +.br + /lib/modules/\`uname\ \-r\`/System.map +.br + /usr/src/linux/System.map +.br + /System.map + +.opt n +Numeric output for WCHAN and USER. (including all types of UID and GID) + +.opt \-w +Wide output. Use this option twice for unlimited width. + +.opt w +Wide output. Use this option twice for unlimited width. + +.opt \-\-cols \ n +set screen width + +.opt \-\-columns \ n +set screen width + +.opt \-\-cumulative +include some dead child process data (as a sum with the parent) + +.opt \-\-forest +ASCII art process tree + +.opt \-\-headers +repeat header lines, one per page of output + +.opt \-\-no\-headers +print no header line at all + +.opt \-\-lines \ n +set screen height + +.opt \-\-rows \ n +set screen height + +.opt \-\-sort \ spec +specify sorting order. Sorting syntax is +[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]] +Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section. +The\ "+" is optional since default direction is increasing numerical or +lexicographic order. Identical to\ \fBk\fR. +For example: \fBps\ jax\ \-\-sort=uid,\-ppid,+pid\fR + +.opt \-\-width \ n +set screen width + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "THREAD DISPLAY" +.PD 0 + +.opt H +Show threads as if they were processes + +.opt \-L +Show threads, possibly with LWP and NLWP columns + +.opt \-T +Show threads, possibly with SPID column + +.opt m +Show threads after processes + +.opt \-m +Show threads after processes + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH "OTHER INFORMATION" +.PD 0 + +.opt L +List all format specifiers. + +.opt \-V +Print the procps version. + +.opt V +Print the procps version. + +.opt \-\-help +Print a help message. + +.opt \-\-info +Print debugging info. + +.opt \-\-version +Print the procps version. + +.\" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +.PD +.PP +.SH NOTES +This \fBps\fR works by reading the virtual files in\ /proc. +This \fBps\fR does not need to be setuid kmem or have any privileges to run. +Do not give this \fBps\fR any special permissions. + +This \fBps\fR needs access to namelist data for proper WCHAN display. +For kernels prior to 2.6, the System.map file must be installed. + +CPU usage is currently expressed as the percentage of time spent +running during the entire lifetime of a process. +This is not ideal, and\ it does not conform to the +standards that \fBps\fR otherwise conforms\ to. +CPU\ usage is unlikely to add up to exactly\ 100%. + +The SIZE and RSS fields don't count some parts of a process including the +page tables, kernel stack, struct thread_info, and struct task_struct. +This is usually at least 20\ KiB of memory that is always resident. +SIZE is the virtual size of the process (code+data+stack). + +Processes marked are dead processes (so\-called\ "zombies") that +remain because their parent has not destroyed them properly. These processes +will be destroyed by \fIinit\fR(8) if the parent process exits. + + +.SH "PROCESS FLAGS" +The sum of these values is displayed in the "F" column, +which is provided by the \fBflags\fR output specifier. +.PD 0 +.TP 5 +1 +forked but didn't exec +.TP +4 +used super\-user privileges +.PD +.PP +.SH "PROCESS STATE CODES" +Here are the different values that the \fBs\fR, \fBstat\fR and +\fBstate\fR output specifiers (header\ "STAT"\ or\ "S") will display to +describe the state of a process. +.PD 0 +.TP 5 +D +Uninterruptible sleep (usually\ IO) +.TP +R +Running or runnable (on\ run\ queue) +.TP +S +Interruptible sleep (waiting for an event to complete) +.TP +T +Stopped, either by a job control signal or because it is being traced. +.TP +W +paging (not valid since the 2.6.xx kernel) +.TP +X +dead (should never be seen) +.TP +Z +Defunct ("zombie") process, terminated but not reaped by its parent. +.PD +.PP +For BSD formats and when the \fBstat\fR keyword is used, additional +characters may be displayed: +.PD 0 +.TP 5 +< +high\-priority (not nice to other users) +.TP +N +low\-priority (nice to other users) +.TP +L +has pages locked into memory (for real\-time and custom\ IO) +.TP +s +is a session leader +.TP +l +is multi-threaded (using CLONE_THREAD, like NPTL pthreads\ do) +.TP ++ +is in the foreground process group +.PD +.PP +.PP +.SH "OBSOLETE SORT KEYS" +These keys are used by the BSD \fBO\fR option (when it is used for +sorting). The GNU \fB\-\-sort\fR option doesn't use these keys, but the +specifiers described below in the \fBSTANDARD FORMAT SPECIFIERS\fR +section. Note that the values used in sorting are the internal +values \fBps\fR uses and not the "cooked" values used in some of +the output format fields (e.g. sorting on tty will sort into +device number, not according to the terminal name displayed). +Pipe \fBps\fR output into the \fIsort\fR(1) command if you want +to sort the cooked values. + +.TS +l l lw(3i). +\fBKEY LONG DESCRIPTION\fR +c cmd simple name of executable +C pcpu cpu utilization +f flags flags as in long format F field +g pgrp process group ID +G tpgid controlling tty process group ID +j cutime cumulative user time +J cstime cumulative system time +k utime user time +m min_flt number of minor page faults +M maj_flt number of major page faults +n cmin_flt cumulative minor page faults +N cmaj_flt cumulative major page faults +o session session ID +p pid process ID +P ppid parent process ID +r rss resident set size +R resident resident pages +s size memory size in kilobytes +S share amount of shared pages +t tty the device number of the controlling tty +T start_time time process was started +U uid user ID number +u user user name +v vsize total VM size in kB +y priority kernel scheduling priority +.\"K stime system time (conflict, system vs. start time) +.TE +.PP +.PP +.SH "AIX FORMAT DESCRIPTORS" +This \fBps\fR supports AIX format descriptors, which work somewhat like the +formatting codes of \fIprintf\fR(1) and \fIprintf\fR(3). For example, the normal +default output can be produced with this: \fBps\ \-eo\ "%p\ %y\ %x\ %c"\fR. +The\ \fBNORMAL\fR codes are described in the next section. +.TS +l l l. +\fBCODE NORMAL HEADER\fR +%C pcpu %CPU +%G group GROUP +%P ppid PPID +%U user USER +%a args COMMAND +%c comm COMMAND +%g rgroup RGROUP +%n nice NI +%p pid PID +%r pgid PGID +%t etime ELAPSED +%u ruser RUSER +%x time TIME +%y tty TTY +%z vsz VSZ +.TE + +.SH "STANDARD FORMAT SPECIFIERS" +Here are the different keywords that may be used to control the output +format (e.g. with option \fB\-o\fR) or to sort the selected processes +with the GNU\-style \fB\-\-sort\fR option. + +For example: \fBps\ \-eo\ pid,user,args\ \-\-sort\ user\fR + +This version of \fBps\fR tries to recognize most of the keywords used in +other implementations of \fBps\fR. + +The following user\-defined format specifiers may contain +spaces: \fBargs\fR, \fBcmd\fR, \fBcomm\fR, \fBcommand\fR, \fBfname\fR, +\fBucmd\fR, \fBucomm\fR, +\fBlstart\fR, \fBbsdstart\fR, \fBstart\fR. + +Some keywords may not be available for sorting. + +.\" ####################################################################### +.\" lB1 lB1 lB1 lB1 s s s +.\" lB1 l1 l1 l1 s s s. +.\" +.\" lB1 lB1 lBw(5.5i) +.\" lB1 l1 l. +.\" +.TS +expand; +lB1 lB1 lBw(\n[ColSize]n) +lB1 l1 l. +CODE HEADER DESCRIPTION + +%cpu %CPU T{ +cpu utilization of the process in "##.#" format. Currently, it is the CPU time +used divided by the time the process has been running (cputime/realtime +ratio), expressed as a percentage. It will not add up to 100% unless you +are lucky. (alias\ \fBpcpu\fR). +T} + +%mem %MEM T{ +ratio of the process's resident set size to the physical memory on +the machine, expressed as a percentage. (alias\ \fBpmem\fR). +T} + +args COMMAND T{ +command with all its arguments as a string. Modifications to the arguments +may be shown. The output in this column may contain spaces. +A\ process marked is partly dead, waiting +to be fully destroyed by its parent. Sometimes the process args +will be unavailable; when this happens, \fBps\fR will instead +print the executable name in brackets. +(alias\ \fBcmd\fR,\ \fBcommand\fR). See also the \fBcomm\fR format +keyword, the \fB\-f\fR option, and the \fBc\fR option. +.br +When specified last, this column will extend to the edge of the display. +If \fBps\fR can not determine display width, as when output is redirected +(piped) into a file or another command, the output width is undefined. +(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on) +The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may +be used to exactly determine the width in this case. +The \fBw\fR or \fB\-w\fR option may be also be used to adjust width. +T} + +blocked BLOCKED T{ +mask of the blocked signals, see \fIsignal\fR(7). +According to the width of the field, +a\ 32\-bit or 64\-bit mask in hexadecimal format is displayed. +(alias\ \fBsig_block\fR,\ \fBsigmask\fR). +T} + +bsdstart START T{ +time the command started. If the process was started less +than 24 hours ago, the output format is "\ HH:MM", +else it is "mmm\ dd" +(where mmm is the three letters of the month). +See also \fBlstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR. +T} + +bsdtime TIME T{ +accumulated cpu time, user\ +\ system. The display format is usually +"MMM:SS", but can be shifted to the right if the process used more than 999 +minutes of cpu time. +T} + +c C T{ +processor utilization. Currently, this is the integer value of +the percent usage over the lifetime of the process. (see\ \fB%cpu\fR). +T} + +caught CAUGHT T{ +mask of the caught signals, see \fIsignal\fR(7). According to the +width of the field, a 32 or 64 bits mask in hexadecimal format is +displayed. (alias\ \fBsig_catch\fR,\ \fBsigcatch\fR). +T} + +class CLS T{ +scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBcls\fR). +Field's possible values are: +.br +\- not reported +.br +TS SCHED_OTHER +.br +FF SCHED_FIFO +.br +RR SCHED_RR +.br +B SCHED_BATCH +.br +ISO SCHED_ISO +.br +IDL SCHED_IDLE +.br +? unknown value +T} + +cls CLS T{ +scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBclass\fR). +Field's possible values are: +.br +\- not reported +.br +TS SCHED_OTHER +.br +FF SCHED_FIFO +.br +RR SCHED_RR +.br +B SCHED_BATCH +.br +ISO SCHED_ISO +.br +IDL SCHED_IDLE +.br +? unknown value +T} + +cmd CMD T{ +see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcommand\fR). +T} + +comm COMMAND T{ +command name (only\ the executable\ name). Modifications to the command +name will not be shown. A\ process marked is partly dead, waiting +to be fully destroyed by its parent. The output in this +column may contain spaces. (alias\ \fBucmd\fR,\ \fBucomm\fR). +See also the \fBargs\fR format +keyword, the \fB\-f\fR option, and the \fBc\fR option. +.br +When specified last, this column will extend to the edge of the display. +If \fBps\fR can not determine display width, as when output is redirected +(piped) into a file or another command, the output width is undefined. +(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on) +The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may +be used to exactly determine the width in this case. +The \fBw\fR or \fB\-w\fR option may be also be used to adjust width. +T} + +command COMMAND T{ +see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcmd\fR). +T} + +cp CP T{ +per\-mill (tenths of a percent) CPU usage. (see\ \fB%cpu\fR). +T} + +cputime TIME T{ +cumulative CPU time, "[dd\-]hh:mm:ss" format. (alias\ \fBtime\fR). +T} + +egid EGID T{ +effective group ID number of the process as a decimal integer. +(alias\ \fBgid\fR). +T} + +egroup EGROUP T{ +effective group ID of the process. This will be the textual group ID, +if it can be obtained and the field width permits, or a decimal +representation otherwise. (alias\ \fBgroup\fR). +T} + +eip EIP T{ +instruction pointer. +T} + +esp ESP T{ +stack pointer. +T} + +etime ELAPSED T{ +elapsed time since the process was started, +in\ the form\ [[dd\-]hh:]mm:ss. +T} + +euid EUID T{ +effective user\ ID. (alias\ \fBuid\fR). +T} + +euser EUSER T{ +effective user\ name. This will be the textual +user\ ID, if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +The\ \fBn\fR\ option can be used +to force the decimal representation. +(alias\ \fBuname\fR,\ \fBuser\fR). +T} + +f F T{ +flags associated with the process, see the \fBPROCESS FLAGS\fR section. +(alias\ \fBflag\fR,\ \fBflags\fR). +T} + +fgid FGID T{ +filesystem access group\ ID. (alias\ \fBfsgid\fR). +T} + +fgroup FGROUP T{ +filesystem access group\ ID. +This will be the textual user\ ID, if\ it can be obtained +and the field width permits, +or\ a\ decimal representation otherwise. +(alias\ \fBfsgroup\fR). +T} + +flag F T{ +see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflags\fR). +T} + +flags F T{ +see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflag\fR). +T} + +fname COMMAND T{ +first 8 bytes of the base name of the process's executable file. +The output in this column may contain spaces. +T} + +fuid FUID T{ +filesystem access user\ ID. (alias\ \fBfsuid\fR). +T} + +fuser FUSER T{ +filesystem access user\ ID. This will be the textual user\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +gid GID T{ +see\ \fBegid\fR. (alias\ \fBegid\fR). +T} + +group GROUP T{ +see\ \fBegroup\fR. (alias\ \fBegroup\fR). +T} + +ignored IGNORED T{ +mask of the ignored signals, see \fIsignal\fR(7). According to the +width of the field, a\ 32\-bit or 64\-bit mask in hexadecimal format +is displayed. (alias \fBsig_ignore\fR, \fBsigignore\fR). +T} + +label LABEL T{ +security label, most commonly used for SE\ Linux context data. +This is for the \fIMandatory Access Control\fR ("MAC") found on +high\-security systems. +T} + +lstart STARTED T{ +time the command started. +See also \fBbsdstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR. +T} + +lwp LWP T{ +lwp (light weight process, or thread) ID of the lwp being reported. +(alias\ \fBspid\fR,\ \fBtid\fR). +T} + +ni NI T{ +nice value. This ranges from 19 (nicest) to \-20 (not\ nice to\ others), +see\ \fInice\fR(1). (alias\ \fBnice\fR). +T} + +nice NI T{ +see\ \fBni\fR. (alias\ \fBni\fR). +T} + +nlwp NLWP T{ +number of lwps (threads) in the process. (alias\ \fBthcount\fR). +T} + +nwchan WCHAN T{ +address of the kernel function where the process is sleeping +(use \fBwchan\fR if you want the kernel function name). +Running tasks will display a dash ('\-') in this column. +T} + +pcpu %CPU T{ +see\ \fB%cpu\fR. (alias\ \fB%cpu\fR). +T} + +pending PENDING T{ +mask of the pending signals. See\ \fIsignal\fR(7). Signals pending on +the process are distinct from signals pending on individual threads. +Use the \fBm\fR option or the \fB\-m\fR option to see both. +According to the width of the field, a\ 32\-bit or 64\-bit mask in +hexadecimal format is displayed. (alias\ \fBsig\fR). +T} + +pgid PGID T{ +process group\ ID or, equivalently, the process\ ID of the +process group leader. (alias\ \fBpgrp\fR). +T} + +pgrp PGRP T{ +see\ \fBpgid\fR. (alias\ \fBpgid\fR). +T} + +pid PID T{ +process\ ID number of the process. +T} + +pmem %MEM T{ +see\ \fB%mem\fR. (alias\ \fB%mem\fR). +T} + +policy POL T{ +scheduling class of the process. (alias\ \fBclass\fR,\ \fBcls\fR). +Possible values are: +.br +\- not reported +.br +TS SCHED_OTHER +.br +FF SCHED_FIFO +.br +RR SCHED_RR +.br +B SCHED_BATCH +.br +ISO SCHED_ISO +.br +IDL SCHED_IDLE +.br +? unknown value +T} + +ppid PPID T{ +parent process ID. +T} + +psr PSR T{ +processor that process is currently assigned to. +T} + +rgid RGID T{ +real group ID. +T} + +rgroup RGROUP T{ +real group name. This will be the textual group\ ID, if\ it can be +obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +rss RSS T{ +resident set size, the non\-swapped physical memory that +a task has used (in\ kiloBytes). +(alias\ \fBrssize\fR,\ \fBrsz\fR). +T} + +rssize RSS T{ +see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrsz\fR). +T} + +rsz RSZ T{ +see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrssize\fR). +T} + +rtprio RTPRIO T{ +realtime priority. +T} + +ruid RUID T{ +real user\ ID. +T} + +ruser RUSER T{ +real user\ ID. This will be the textual user\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +s S T{ +minimal state display (one\ character). +See\ section \fBPROCESS STATE CODES\fR for the different values. +See\ also \fBstat\fR if you want additional +information displayed. (alias\ \fBstate\fR). +T} + +sched SCH T{ +scheduling policy of the process. The policies SCHED_OTHER (SCHED_NORMAL), +SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_ISO, and SCHED_IDLE are respectively +displayed as 0,\ 1,\ 2,\ 3,\ 4,\ and\ 5. +T} + +sess SESS T{ +session\ ID or, equivalently, the process\ ID of the session\ leader. +(alias\ \fBsession\fR,\ \fBsid\fR). +T} + +sgi_p P T{ +processor that the process is currently executing on. +Displays "*" if the process is not currently running or runnable. +T} + +sgid SGID T{ +saved group\ ID. +(alias\ \fBsvgid\fR). +T} + +sgroup SGROUP T{ +saved group\ name. This will be the textual group\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +T} + +sid SID T{ +see\ \fBsess\fR. (alias\ \fBsess\fR,\ \fBsession\fR). +T} + +sig PENDING T{ +see\ \fBpending\fR. (alias\ \fBpending\fR,\ \fBsig_pend\fR). +T} + +sigcatch CAUGHT T{ +see\ \fBcaught\fR. (alias\ \fBcaught\fR,\ \fBsig_catch\fR). +T} + +sigignore IGNORED T{ +see\ \fBignored\fR. (alias\ \fBignored\fR,\ \fBsig_ignore\fR). +T} + +sigmask BLOCKED T{ +see\ \fBblocked\fR. (alias\ \fBblocked\fR,\ \fBsig_block\fR). +T} + +size SZ T{ +approximate amount of swap space that would be required +if the process were to dirty all writable pages and then +be swapped out. +This number is very\ rough! +T} + +spid SPID T{ +see \fBlwp\fR. (alias\ \fBlwp\fR,\ \fBtid\fR). +T} + +stackp STACKP T{ +address of the bottom (start) of stack for the process. +T} + +start STARTED T{ +time the command started. +If the process was started less than 24 hours ago, +the output format is "HH:MM:SS", +else it is "\ \ mmm\ dd" +(where mmm is a three\-letter month\ name). +See also \fBlstart\fR, \fBbsdstart\fR, \fBstart_time\fR, and \fBstime\fR. +T} + +start_time START T{ +starting time or date of the process. +Only the year will be displayed if the process was not +started the same year \fBps\fR was invoked, +or\ "mmmdd" if\ it was not started the same day, +or\ "HH:MM" otherwise. +See also \fBbsdstart\fR, \fBstart\fR, \fBlstart\fR, and \fBstime\fR. +T} + +stat STAT T{ +multi\-character process state. +See\ section \fBPROCESS STATE CODES\fR +for the different values meaning. +See also \fBs\fR and \fBstate\fR if you just want +the first character displayed. +T} + +state S T{ +see\ \fBs\fR. (alias\ \fBs\fR). +T} + +suid SUID T{ +saved user\ ID. (alias\ \fBsvuid\fR). +T} + +suser SUSER T{ +saved user name. This will be the textual user\ ID, +if\ it can be obtained and the field width permits, +or\ a\ decimal representation otherwise. +(alias\ \fBsvuser\fR). +T} + +svgid SVGID T{ +see\ \fBsgid\fR. (alias\ \fBsgid\fR). +T} + +svuid SVUID T{ +see\ \fBsuid\fR. (alias\ \fBsuid\fR). +T} + +sz SZ T{ +size in physical pages of the core image of the process. +This includes text, data, and stack space. +Device mappings are currently excluded; this is subject to change. +See \fBvsz\fR and \fBrss\fR. +T} + +thcount THCNT T{ +see \fBnlwp\fR. (alias\ \fBnlwp\fR). +number of kernel threads owned by the process. +T} + +tid TID T{ +see\ \fBlwp\fR. (alias\ \fBlwp\fR). +T} + +time TIME T{ +cumulative CPU\ time, "[dd\-]hh:mm:ss" format. (alias\ \fBcputime\fR). +T} + +tname TTY T{ +controlling tty (terminal). +(alias\ \fBtt\fR,\ \fBtty\fR). +T} + +tpgid TPGID T{ +ID of the foreground process group on the tty (terminal) that +the process is connected to, or \-1 if the process is not connected +to a tty. +T} + +tt TT T{ +controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtty\fR). +T} + +tty TT T{ +controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtt\fR). +T} + +ucmd CMD T{ +see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucomm\fR). +T} + +ucomm COMMAND T{ +see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucmd\fR). +T} + +uid UID T{ +see \fBeuid\fR. (alias\ \fBeuid\fR). +T} + +uname USER T{ +see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuser\fR). +T} + +user USER T{ +see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuname\fR). +T} + +vsize VSZ T{ +see \fBvsz\fR. (alias\ \fBvsz\fR). +T} + +vsz VSZ T{ +virtual memory size of the process in KiB (1024\-byte\ units). +Device mappings are currently excluded; this is subject to change. +(alias\ \fBvsize\fR). +T} + +wchan WCHAN T{ +name of the kernel function in which the process is sleeping, +a\ "\-"\ if the process is running, +or a "*"\ if the process is multi\-threaded and +\fBps\fR is not displaying threads. +T} +.TE +.\" ####################################################################### +.PP +.PP +.SH "ENVIRONMENT VARIABLES" +The following environment variables could affect \fBps\fR: +.TP 3 +.B COLUMNS +Override default display width. +.TP +.B LINES +Override default display height. +.TP +.B PS_PERSONALITY +Set to one of posix, old, linux, bsd, sun, digital... +(see\ section\ \fBPERSONALITY\fR\ below). +.TP +.B CMD_ENV +Set to one of posix, old, linux, bsd, sun, digital... +(see\ section\ \fBPERSONALITY\fR\ below). +.TP +.B I_WANT_A_BROKEN_PS +Force obsolete command line interpretation. +.TP +.B LC_TIME +Date format. +.TP +.B PS_COLORS +Not currently supported. +.TP +.B PS_FORMAT +Default output format override. You may set this to a format +string of the type used for the \fB\-o\fR option. +The \fBDefSysV\fR and \fBDefBSD\fR values are particularly useful. +.TP +.B PS_SYSMAP +Default namelist (System.map) location. +.TP +.B PS_SYSTEM_MAP +Default namelist (System.map) location. +.TP +.B POSIXLY_CORRECT +Don't find excuses to ignore bad "features". +.TP +.B POSIX2 +When set to "on", acts as \fBPOSIXLY_CORRECT\fR. +.TP +.B UNIX95 +Don't find excuses to ignore bad "features". +.TP +.B _XPG +Cancel \fBCMD_ENV\fI=irix\fR non\-standard behavior. +.PP +In general, it\ is a bad idea to set these variables. +The one exception is \fBCMD_ENV\fR or \fBPS_PERSONALITY\fR, +which could be set to Linux for normal systems. +Without that setting, +\fBps\fR follows the useless and bad parts of the Unix98 standard. +.PP +.PP +.SH "PERSONALITY" +.TS +l l. +390 like the S/390 OpenEdition \fBps\fR +aix like AIX \fBps\fR +bsd like FreeBSD \fBps\fR (totally\ non\-standard) +compaq like Digital Unix \fBps\fR +debian like the old Debian \fBps\fR +digital like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR +gnu like the old Debian \fBps\fR +hp like HP\-UX \fBps\fR +hpux like HP\-UX \fBps\fR +irix like Irix \fBps\fR +linux ***** RECOMMENDED ***** +old like the original Linux \fBps\fR (totally\ non\-standard) +os390 like OS/390 Open Edition \fBps\fR +posix standard +s390 like OS/390 Open Edition \fBps\fR +sco like SCO \fBps\fR +sgi like Irix \fBps\fR +solaris2 like Solaris 2+ (SunOS 5) \fBps\fR +sunos4 like SunOS 4 (Solaris 1) \fBps\fR (totally\ non\-standard) +svr4 standard +sysv standard +tru64 like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR +unix standard +unix95 standard +unix98 standard +.TE +.PP +.PP +.SH "SEE ALSO" +\fItop\fR(1), \fIpgrep\fR(1), \fIpstree\fR(1), \fIproc\fR(5). +.PP +.PP +.SH STANDARDS +This \fBps\fR conforms to: +.PP +.PD 0 +.IP 1 4 +Version 2 of the Single Unix Specification +.IP 2 4 +The Open Group Technical Standard Base Specifications, Issue\ 6 +.IP 3 4 +IEEE Std 1003.1, 2004\ Edition +.IP 4 4 +X/Open System Interfaces Extension [UP\ XSI] +.IP 5 4 +ISO/IEC 9945:2003 +.PD +.PP +.SH AUTHOR +\fBps\fR was originally written by Branko Lankester . Michael +K. Johnson re\-wrote it significantly to use the proc +filesystem, changing a few things in the process. Michael Shields + added the pid\-list feature. Charles Blake + added multi\-level sorting, the dirent\-style library, the +device name\-to\-number mmaped database, the approximate binary search +directly on System.map, and many code and documentation cleanups. David +Mossberger\-Tang wrote the generic BFD support for psupdate. Albert Cahalan + rewrote ps for full Unix98 and BSD support, along with +some ugly hacks for obsolete and foreign syntax. + +Please send bug reports to . +No\ subscription is required or suggested. diff --git a/ps/regression b/ps/regression new file mode 100644 index 0000000..c71c826 --- /dev/null +++ b/ps/regression @@ -0,0 +1,26 @@ +-u 500 -o pid,ppid,fname,comm,args # right margin trouble +-u 500 -o pid,ppid,fname,comm,args,wchan,wchan,wchan,wchan,wchan,nice,wchan +-u 500 -o pid,pid,pid,pid,user,user,user,args # had trouble +-u 500 -o user,user,user,pid,pid,pid,pid,args # no trouble! + +Test with each type of field (RIGHT,LEFT,UNLIMITED...) hanging off the +edge of the screen and each type of field to the left of the one that +hangs off the edge. + +Test "ps ef" as _both_ normal user and root. Especially after su! + +On a 108-col screen, try "ps alx" and "ps alx | cat" + +These ought to be the same: +CMD_ENV=old ps -m +CMD_ENV=old ps m + +These ought to be the same: +CMD_ENV=old ps -X +CMD_ENV=old ps X +ps X +ps -X # needs to be a non-SysV option + +This should fail: +ps x -x + diff --git a/ps/select.c b/ps/select.c new file mode 100644 index 0000000..2c52d02 --- /dev/null +++ b/ps/select.c @@ -0,0 +1,146 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "../proc/readproc.h" +#include "../proc/procps.h" + +//#define process_group_leader(p) ((p)->pgid == (p)->tgid) +//#define some_other_user(p) ((p)->euid != cached_euid) +#define has_our_euid(p) ((unsigned)(p)->euid == (unsigned)cached_euid) +#define on_our_tty(p) ((unsigned)(p)->tty == (unsigned)cached_tty) +#define running(p) (((p)->state=='R')||((p)->state=='D')) +#define session_leader(p) ((p)->session == (p)->tgid) +#define without_a_tty(p) (!(p)->tty) + +static unsigned long select_bits = 0; + +/***** prepare select_bits for use */ +const char *select_bits_setup(void){ + int switch_val = 0; + /* don't want a 'g' screwing up simple_select */ + if(!simple_select && !prefer_bsd_defaults){ + select_bits = 0xaa00; /* the STANDARD selection */ + return NULL; + } + /* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */ + if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) ) + switch_val = simple_select|SS_B_g; + else + switch_val = simple_select; + switch(switch_val){ + /* UNIX options */ + case SS_U_a | SS_U_d: select_bits = 0x3f3f; break; /* 3333 or 3f3f */ + case SS_U_a: select_bits = 0x0303; break; /* 0303 or 0f0f */ + case SS_U_d: select_bits = 0x3333; break; + /* SunOS 4 only (others have 'g' enabled all the time) */ + case 0: select_bits = 0x0202; break; + case SS_B_a: select_bits = 0x0303; break; + case SS_B_x : select_bits = 0x2222; break; + case SS_B_x | SS_B_a: select_bits = 0x3333; break; + /* General BSD options */ + case SS_B_g : select_bits = 0x0a0a; break; + case SS_B_g | SS_B_a: select_bits = 0x0f0f; break; + case SS_B_g | SS_B_x : select_bits = 0xaaaa; break; + case SS_B_g | SS_B_x | SS_B_a: /* convert to -e instead of using 0xffff */ + all_processes = 1; + simple_select = 0; + break; + default: + return "Process selection options conflict."; + break; + } + return NULL; +} + +/***** selected by simple option? */ +static int table_accept(proc_t *buf){ + unsigned proc_index; + proc_index = (has_our_euid(buf) <<0) + | (session_leader(buf) <<1) + | (without_a_tty(buf) <<2) + | (on_our_tty(buf) <<3); + return (select_bits & (1<typecode){ + default: + printf("Internal error in ps! Please report this bug.\n"); + +#define return_if_match(foo,bar) \ + i=sn->n; while(i--) \ + if((unsigned)(buf->foo) == (unsigned)(*(sn->u+i)).bar) \ + return 1 + + break; case SEL_RUID: return_if_match(ruid,uid); + break; case SEL_EUID: return_if_match(euid,uid); + break; case SEL_SUID: return_if_match(suid,uid); + break; case SEL_FUID: return_if_match(fuid,uid); + + break; case SEL_RGID: return_if_match(rgid,gid); + break; case SEL_EGID: return_if_match(egid,gid); + break; case SEL_SGID: return_if_match(sgid,gid); + break; case SEL_FGID: return_if_match(fgid,gid); + + break; case SEL_PGRP: return_if_match(pgrp,pid); + break; case SEL_PID : return_if_match(tgid,pid); + break; case SEL_PPID: return_if_match(ppid,ppid); + break; case SEL_TTY : return_if_match(tty,tty); + break; case SEL_SESS: return_if_match(session,pid); + + break; case SEL_COMM: i=sn->n; while(i--) + if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 15 )) return 1; + + + +#undef return_if_match + + } + sn = sn->next; + } + return 0; +} + + +/***** This must satisfy Unix98 and as much BSD as possible */ +int want_this_proc(proc_t *buf){ + int accepted_proc = 1; /* assume success */ + /* elsewhere, convert T to list, U sets x implicitly */ + + /* handle -e -A */ + if(all_processes) goto finish; + + /* use table for -a a d g x */ + if((simple_select || !selection_list)) + if(table_accept(buf)) goto finish; + + /* search lists */ + if(proc_was_listed(buf)) goto finish; + + /* fail, fall through to loose ends */ + accepted_proc = 0; + + /* do r N */ +finish: + if(running_only && !running(buf)) accepted_proc = 0; + if(negate_selection) return !accepted_proc; + return accepted_proc; +} diff --git a/ps/sortformat.c b/ps/sortformat.c new file mode 100644 index 0000000..80427e5 --- /dev/null +++ b/ps/sortformat.c @@ -0,0 +1,955 @@ +/* + * Copyright 1998-2004 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include + +/* username lookups */ +#include +#include +#include + +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "common.h" + +static sf_node *sf_list = NULL; /* deferred sorting and formatting */ +static int broken; /* use gross Unix98 parsing? */ +static int have_gnu_sort = 0; /* if true, "O" must be format */ +static int already_parsed_sort = 0; /* redundantly set in & out of fn */ +static int already_parsed_format = 0; + + +/**************** Parse single format specifier *******************/ +static format_node *do_one_spec(const char *spec, const char *override){ + const format_struct *fs; + const macro_struct *ms; + + fs = search_format_array(spec); + if(fs){ + int w1, w2; + format_node *thisnode; + thisnode = malloc(sizeof(format_node)); + if(fs->flags & CF_PIDMAX){ + w1 = (int)get_pid_digits(); + w2 = strlen(fs->head); + if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing + }else{ + w1 = fs->width; + } + if(override){ + w2 = strlen(override); + thisnode->width = (w1>w2)?w1:w2; + thisnode->name = malloc(strlen(override)+1); + strcpy(thisnode->name, override); + }else{ + thisnode->width = w1; + thisnode->name = malloc(strlen(fs->head)+1); + strcpy(thisnode->name, fs->head); + } + thisnode->pr = fs->pr; + thisnode->need = fs->need; + thisnode->vendor = fs->vendor; + thisnode->flags = fs->flags; + thisnode->next = NULL; + return thisnode; + } + + /* That failed, so try it as a macro. */ + ms = search_macro_array(spec); + if(ms){ + format_node *list = NULL; + format_node *newnode; + const char *walk; + int dist; + char buf[16]; /* trust strings will be short (from above, not user) */ + walk = ms->head; + while(*walk){ + dist = strcspn(walk, ", "); + strncpy(buf,walk,dist); + buf[dist] = '\0'; + newnode = do_one_spec(buf,override); /* call self, assume success */ + newnode->next = list; + list = newnode; + walk += dist; + if(*walk) walk++; + } + return list; + } + return NULL; /* bad, spec not found */ +} + + +/************ must wrap user format in default *************/ +static void O_wrap(sf_node *sfn, int otype){ + format_node *fnode; + format_node *endp; + const char *trailer; + + trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ; + + fnode = do_one_spec("pid",NULL); + if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n"); + endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */ + endp->next = fnode; + + fnode = do_one_spec(trailer,NULL); + if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n"); + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; +} + +/****************************************************************** + * Used to parse option AIX field descriptors. + * Put each completed format_node onto the list starting at ->f_cooked + */ +static const char *aix_format_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *walk; + int items; + + /*** sanity check and count items ***/ + items = 0; + walk = sfn->sf; + /* state machine */ { + int c; + initial: + c = *walk++; + if(c=='%') goto get_desc; + if(!c) goto looks_ok; + /* get_text: */ + items++; + get_more_text: + c = *walk++; + if(c=='%') goto get_desc; + if(c) goto get_more_text; + goto looks_ok; + get_desc: + items++; + c = *walk++; + if(c) goto initial; + return "Improper AIX field descriptor."; + looks_ok: + ; + } + + /*** sanity check passed ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + walk = sfn->sf; + + while(items--){ + format_node *fnode; /* newly allocated */ + format_node *endp; /* for list manipulation */ + + if(*walk == '%'){ + const aix_struct *aix; + walk++; + if(*walk == '%') goto double_percent; + aix = search_aix_array(*walk); + walk++; + if(!aix){ + free(buf); + return "Unknown AIX field descriptor."; + } + fnode = do_one_spec(aix->spec, aix->head); + if(!fnode){ + free(buf); + return "AIX field descriptor processing bug."; + } + } else { + int len; + len = strcspn(walk, "%"); + memcpy(buf,walk,len); + if(0){ +double_percent: + len = 1; + buf[0] = '%'; + } + buf[len] = '\0'; + walk += len; + fnode = malloc(sizeof(format_node)); + fnode->width = len; + fnode->name = malloc(len+1); + strcpy(fnode->name, buf); + fnode->pr = NULL; /* checked for */ + fnode->need = 0; + fnode->vendor = AIX; + fnode->flags = CF_PRINT_EVERY_TIME; + fnode->next = NULL; + } + + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; + } + free(buf); + already_parsed_format = 1; + return NULL; +} + +/*************************************************************** + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + * Put each completed format_node onto the list starting at ->f_cooked + */ +static const char *format_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t,\n" */ + char *walk; + const char *err; /* error code that could or did happen */ + format_node *fnode; + int items; + int need_item; + static char errbuf[80]; /* for variable-text error message */ + + /*** prepare to operate ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\n': case '\0': + /* Linux extension: allow \t and \n as delimiters */ + if(need_item){ + free(buf); + goto improper; + } + need_item=1; + break; + case '=': + if(broken) goto out; + /* fall through */ + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); +out: + if(!items){ + free(buf); + goto empty; + } +#ifdef STRICT_LIST + if(need_item){ /* can't have trailing deliminator */ + free(buf); + goto improper; + } +#else + if(need_item){ /* allow 1 trailing deliminator */ + *--walk='\0'; /* remove the trailing deliminator */ + } +#endif + /*** actually parse the list ***/ + walk = buf; + while(items--){ + format_node *endp; + char *equal_loc; + char *colon_loc; + sep_loc = strpbrk(walk," ,\t\n"); + /* if items left, then sep_loc is not in header override */ + if(items && sep_loc) *sep_loc = '\0'; + equal_loc = strpbrk(walk,"="); + if(equal_loc){ /* if header override */ + *equal_loc = '\0'; + equal_loc++; + } + colon_loc = strpbrk(walk,":"); + if(colon_loc){ /* if width override */ + *colon_loc = '\0'; + colon_loc++; + if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc){ + free(buf); + goto badwidth; + } + } + fnode = do_one_spec(walk,equal_loc); + if(!fnode){ + if(!*errbuf){ /* if didn't already create an error string */ + snprintf( + errbuf, + sizeof(errbuf), + "Unknown user-defined format specifier \"%s\".", + walk + ); + } + free(buf); + goto unknown; + } + if(colon_loc){ + if(fnode->next){ + free(buf); + goto notmacro; + } + // FIXME: enforce signal width to 8, 9, or 16 (grep: SIGNAL wide_signals) + fnode->width = atoi(colon_loc); // already verified to be a number + } + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + already_parsed_format = 1; + return NULL; + + /* errors may cause a retry looking for AIX format codes */ + if(0) unknown: err=errbuf; + if(0) empty: err="Empty format list."; + if(0) improper: err="Improper format list."; + if(0) badwidth: err="Column widths must be unsigned decimal numbers."; + if(0) notmacro: err="Can't set width for a macro (multi-column) format specifier."; + if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn); + return err; +} + +/**************** Parse single sort specifier *******************/ +static sort_node *do_one_sort_spec(const char *spec){ + const format_struct *fs; + int reverse = 0; + if(*spec == '-'){ + reverse = 1; + spec++; + } else if(*spec == '+'){ + spec++; + } + fs = search_format_array(spec); + if(fs){ + sort_node *thisnode; + thisnode = malloc(sizeof(sort_node)); + thisnode->sr = fs->sr; + thisnode->need = fs->need; + thisnode->reverse = reverse; + thisnode->next = NULL; + return thisnode; + } + return NULL; /* bad, spec not found */ +} + + +/************************************************************** + * Used to parse long sorting options. + * Put each completed sort_node onto the list starting at ->s_cooked + */ +static const char *long_sort_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t,\n" */ + char *walk; + sort_node *snode; + int items; + int need_item; + + /*** prepare to operate ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\n': case '\0': + if(need_item){ + free(buf); + return "Improper sort list"; + } + need_item=1; + break; + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); + if(!items){ + free(buf); + return "Empty sort list."; + } +#ifdef STRICT_LIST + if(need_item){ /* can't have trailing deliminator */ + free(buf); + return "Improper sort list."; + } +#else + if(need_item){ /* allow 1 trailing deliminator */ + *--walk='\0'; /* remove the trailing deliminator */ + } +#endif + /*** actually parse the list ***/ + walk = buf; + while(items--){ + sort_node *endp; + sep_loc = strpbrk(walk," ,\t\n"); + if(sep_loc) *sep_loc = '\0'; + snode = do_one_sort_spec(walk); + if(!snode){ + free(buf); + return "Unknown sort specifier."; + } + endp = snode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->s_cooked; + sfn->s_cooked = snode; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + already_parsed_sort = 1; + return NULL; +} + + + + + + +/************ pre-parse short sorting option *************/ +/* Errors _must_ be detected so that the "O" option can try to + * reparse as formatting codes. + */ +static const char *verify_short_sort(const char *arg){ + const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-"; + char checkoff[256]; + int i; + const char *walk; + int tmp; + if(strspn(arg,all) != strlen(arg)) return "Bad sorting code."; + for(i=256; i--;) checkoff[i] = 0; + walk = arg; + for(;;){ + tmp = *walk; + switch(tmp){ + case '\0': + return NULL; /* looks good */ + case '+': + case '-': + tmp = *(walk+1); + if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code."; + break; + case 'P': + if(forest_type) return "PPID sort and forest output conflict."; + /* fall through */ + default: + if(checkoff[tmp]) return "Bad sorting code."; /* repeated */ + /* ought to check against already accepted sort options */ + checkoff[tmp] = 1; + break; + } + walk++; + } +} + + + +/************ parse short sorting option *************/ +static const char *short_sort_parse(sf_node *sfn){ + int direction = 0; + const char *walk; + int tmp; + sort_node *snode; + sort_node *endp; + const struct shortsort_struct *ss; + walk = sfn->sf; + for(;;){ + tmp = *walk; + switch(tmp){ + case '\0': + already_parsed_sort = 1; + return NULL; + case '+': + direction = 0; + break; + case '-': + direction = 1; + break; + default: + ss = search_shortsort_array(tmp); + if(!ss) return "Unknown sort specifier."; + snode = do_one_sort_spec(ss->spec); + if(!snode) return "Unknown sort specifier."; + snode->reverse = direction; + endp = snode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->s_cooked; + sfn->s_cooked = snode; + direction = 0; + break; + } + walk++; + } +} + +/******************* high-level below here *********************/ + + +/* + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + * Recursion is to preserve original order. + */ +static const char *parse_O_option(sf_node *sfn){ + const char *err; /* error code that could or did happen */ + + if(sfn->next){ + err = parse_O_option(sfn->next); + if(err) return err; + } + + switch(sfn->sf_code){ + case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/ + err = format_parse(sfn); + if(!err) already_parsed_format = 1; + break; + case SF_U_O: /*** format ***/ + /* Can have -l -f f u... set already_parsed_format like DEC does */ + if(already_parsed_format) return "option -O can not follow other format options."; + err = format_parse(sfn); + if(err) return err; + already_parsed_format = 1; + O_wrap(sfn,'u'); /* must wrap user format in default */ + break; + case SF_B_O: /*** both ***/ + if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options."; + else err = verify_short_sort(sfn->sf); + if(!err){ /* success as sorting code */ + short_sort_parse(sfn); + already_parsed_sort = 1; + return NULL; + } + if(already_parsed_format){ + err = "option O is neither first format nor sort order."; + break; + } + if(!format_parse(sfn)){ /* if success as format code */ + already_parsed_format = 1; + O_wrap(sfn,'b'); /* must wrap user format in default */ + return NULL; + } + break; + case SF_G_sort: case SF_B_m: /*** sort ***/ + if(already_parsed_sort) err = "Multiple sort options."; + else err = long_sort_parse(sfn); + already_parsed_sort = 1; + break; + default: /*** junk ***/ + return "Bug: parse_O_option got weirdness!"; + } + return err; /* could be NULL */ +} + + +/************ Main parser calls this to save lists for later **********/ +/* store data for later and return 1 if arg looks non-standard */ +int defer_sf_option(const char *arg, int source){ + sf_node *sfn; + char buf[16]; + int dist; + const format_struct *fs; + int need_item = 1; + + sfn = malloc(sizeof(sf_node)); + sfn->sf = malloc(strlen(arg)+1); + strcpy(sfn->sf, arg); + sfn->sf_code = source; + sfn->s_cooked = NULL; + sfn->f_cooked = NULL; + sfn->next = sf_list; + sf_list = sfn; + + if(source == SF_G_sort) have_gnu_sort = 1; + + /* Now try to find an excuse to ignore broken Unix98 parsing. */ + if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */ + do{ + switch(*arg){ + case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */ + if(need_item) return 1; /* something wrong */ + need_item=1; + break; + case '=': + if(need_item) return 1; /* something wrong */ + return 0; /* broken Unix98 parsing is required */ + default: + if(!need_item) break; + need_item=0; + dist = strcspn(arg,", ="); + if(dist>15) return 1; /* something wrong, sort maybe? */ + strncpy(buf,arg,dist); /* no '\0' on end */ + buf[dist] = '\0'; /* fix that problem */ + fs = search_format_array(buf); + if(!fs) return 1; /* invalid spec, macro or sort maybe? */ + if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */ + } + } while (*++arg); + + return 0; /* boring, Unix98 is no change */ +} + +/***** Since ps is not long-lived, the memory leak can be ignored. ******/ +void reset_sortformat(void){ + sf_list = NULL; /* deferred sorting and formatting */ + format_list = NULL; /* digested formatting options */ + sort_list = NULL; /* digested sorting options (redundant?) */ + have_gnu_sort = 0; + already_parsed_sort = 0; + already_parsed_format = 0; +} + + +/***** Search format_list for findme, then insert putme after findme. ****/ +static int fmt_add_after(const char *findme, format_node *putme){ + format_node *walk; + if(!strcmp(format_list->name, findme)){ + putme->next = format_list->next; + format_list->next = putme; + return 1; /* success */ + } + walk = format_list; + while(walk->next){ + if(!strcmp(walk->next->name, findme)){ + putme->next = walk->next->next; + walk->next->next = putme; + return 1; /* success */ + } + walk = walk->next; + } + return 0; /* fail */ +} + +/******* Search format_list for findme, then delete it. ********/ +static int fmt_delete(const char *findme){ + format_node *walk; + format_node *old; + if(!strcmp(format_list->name, findme)){ + old = format_list; + format_list = format_list->next; + free(old); + return 1; /* success */ + } + walk = format_list; + while(walk->next){ + if(!strcmp(walk->next->name, findme)){ + old = walk->next; + walk->next = walk->next->next; + free(old); + return 1; /* success */ + } + walk = walk->next; + } + return 0; /* fail */ +} + + +/************ Build a SysV format backwards. ***********/ +#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn) +static const char *generate_sysv_list(void){ + format_node *fn; + if((format_modifiers & FM_y) && !(format_flags & FF_Ul)) + return "Modifier -y without format -l makes no sense."; + if(prefer_bsd_defaults){ + if(format_flags) PUSH("cmd"); + else PUSH("args"); + PUSH("bsdtime"); + if(!(format_flags & FF_Ul)) PUSH("stat"); + }else{ + if(format_flags & FF_Uf) PUSH("cmd"); + else PUSH("ucmd"); + PUSH("time"); + } + PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */ + if(format_flags & FF_Uf) PUSH("stime"); + /* avoid duplicate columns from -FP and -Fly */ + if(format_modifiers & FM_F){ + /* if -FP take the Sun-style column instead (sorry about "sgi_p") */ + if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */ + /* if -Fly take the ADDR-replacement RSS instead */ + if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss"); + } + if(format_flags & FF_Ul){ + PUSH("wchan"); + } + /* since FM_y adds RSS anyway, don't do this hack when that is true */ + if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){ + if(personality & PER_IRIX_l){ /* add "rss" then ':' here */ + PUSH("sgi_rss"); + fn = malloc(sizeof(format_node)); + fn->width = 1; + fn->name = malloc(2); + strcpy(fn->name, ":"); + fn->pr = NULL; /* checked for */ + fn->need = 0; + fn->vendor = AIX; /* yes, for SGI weirdness */ + fn->flags = CF_PRINT_EVERY_TIME; + fn->next = format_list; + format_list=fn; + } + } + if((format_modifiers & FM_F) || (format_flags & FF_Ul)){ + PUSH("sz"); + } + if(format_flags & FF_Ul){ + if(format_modifiers & FM_y) PUSH("rss"); + else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p"); + else PUSH("addr_1"); + } + if(format_modifiers & FM_c){ + PUSH("pri"); PUSH("class"); + }else if(format_flags & FF_Ul){ + PUSH("ni"); + if(personality & PER_IRIX_l) PUSH("priority"); + else /* is this good? */ PUSH("opri"); + } + + // FIXME TODO XXX -- this is a serious problem + // These somehow got flipped around. + // The bug is in procps-3.1.1, procps-990211, prior too? + if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp"); + if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c"); + + if(format_modifiers & FM_P) PUSH("psr"); + if(thread_flags & TF_U_L) PUSH("lwp"); + if(format_modifiers & FM_j){ + PUSH("sid"); + PUSH("pgid"); + } + if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid"); + if(thread_flags & TF_U_T) PUSH("spid"); + PUSH("pid"); + if(format_flags & FF_Uf){ + if(personality & PER_SANE_USER) PUSH("user"); + else PUSH("uid_hack"); + }else if(format_flags & FF_Ul){ + PUSH("uid"); + } + if(format_flags & FF_Ul){ + PUSH("s"); + if(!(format_modifiers & FM_y)) PUSH("f"); + } + if(format_modifiers & FM_M){ + PUSH("label"); /* Mandatory Access Control */ + } + return NULL; +} + + +/************************************************************************** + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + */ +const char *process_sf_options(int localbroken){ + sf_node *sf_walk; + + if(personality & PER_BROKEN_o) localbroken = 1; + if(personality & PER_GOOD_o) localbroken = 0; + broken = localbroken; + if(sf_list){ + const char *err; + err = parse_O_option(sf_list); + if(err) return err; + } + + if(format_list) printf("Bug: must reset the list first!\n"); + + /* merge formatting info of sf_list into format_list here */ + sf_walk = sf_list; + while(sf_walk){ + format_node *fmt_walk; + fmt_walk = sf_walk->f_cooked; + sf_walk->f_cooked = NULL; + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + sf_walk = sf_walk->next; + } + + /* merge sorting info of sf_list into sort_list here */ + sf_walk = sf_list; + while(sf_walk){ + sort_node *srt_walk; + srt_walk = sf_walk->s_cooked; + sf_walk->s_cooked = NULL; + while(srt_walk){ /* put any nodes onto sort_list in opposite way */ + sort_node *travler; + travler = srt_walk; + srt_walk = srt_walk->next; + travler->next = sort_list; + sort_list = travler; + } + sf_walk = sf_walk->next; + } + + // Get somebody to explain how -L/-T is supposed to interact + // with sorting. Do the threads remain grouped, with sorting + // by process, or do the threads get sorted by themselves? + if(sort_list && (thread_flags&TF_no_sort)){ + return "Tell procps-feedback@lists.sf.net what you expected."; + } + + // If nothing else, try to use $PS_FORMAT before the default. + if(!format_flags && !format_modifiers && !format_list){ + char *tmp; + tmp = getenv("PS_FORMAT"); /* user override kills default */ + if(tmp && *tmp){ + const char *err; + sf_node sfn; + if(thread_flags&TF_must_use) return "Tell procps-feedback@sf.net what you want. (-L/-T, -m/m/H, and $PS_FORMAT)"; + sfn.sf = tmp; + sfn.f_cooked = NULL; + err = format_parse(&sfn); + if(!err){ + format_node *fmt_walk; + fmt_walk = sfn.f_cooked; + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + return NULL; + } + // FIXME: prove that this won't be hit on valid bogus-BSD options + fprintf(stderr, "Warning: $PS_FORMAT ignored. (%s)\n", err); + } + } + + if(format_list){ + if(format_flags) return "Conflicting format options."; + if(format_modifiers) return "Can't use output modifiers with user-defined output"; + if(thread_flags&TF_must_use) return "-L/-T with H/m/-m and -o/-O/o/O is nonsense"; + return NULL; + } + + do{ + const char *spec; + switch(format_flags){ + + default: return "Conflicting format options."; + + /* These can be NULL, which enables SysV list generation code. */ + case 0: spec=NULL; break; + case FF_Uf | FF_Ul: spec=sysv_fl_format; break; + case FF_Uf: spec=sysv_f_format; break; + case FF_Ul: spec=sysv_l_format; break; + + /* These are NOT REACHED for normal -j processing. */ + case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */ + case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */ + case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */ + + /* These are true BSD options. */ + case FF_Bj: spec=bsd_j_format; break; + case FF_Bl: spec=bsd_l_format; break; + case FF_Bs: spec=bsd_s_format; break; + case FF_Bu: spec=bsd_u_format; break; + case FF_Bv: spec=bsd_v_format; break; + + /* These are old Linux options. Option m is overloaded. */ + case FF_LX: spec="OL_X"; break; + case FF_Lm: spec="OL_m"; break; + + /* This is the sole FLASK security option. */ + case FF_Fc: spec="FLASK_context"; break; + + } /* end switch(format_flags) */ + + // not just for case 0, since sysv_l_format and such may be NULL + if(!spec) return generate_sysv_list(); + + do{ + format_node *fmt_walk; + fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */ + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + }while(0); + }while(0); + + do{ + format_node *fn; + if(format_modifiers & FM_j){ + fn = do_one_spec("pgid", NULL); + if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn)) + return "Internal error, no PID or PPID for -j option."; + fn = do_one_spec("sid", NULL); + if(!fmt_add_after("PGID", fn)) return "Lost my PGID!"; + } + if(format_modifiers & FM_y){ + /* TODO: check for failure to do something, and complain if so */ + fmt_delete("F"); + fn = do_one_spec("rss", NULL); + if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR"); + } + if(format_modifiers & FM_c){ + fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C"); + fmt_delete("NI"); + fn = do_one_spec("class", NULL); + if(!fmt_add_after("PRI", fn)) + return "Internal error, no PRI for -c option."; + fmt_delete("PRI"); /* we want a different one */ + fn = do_one_spec("pri", NULL); + if(!fmt_add_after("CLS", fn)) return "Lost my CLS!"; + } + if(thread_flags & TF_U_T){ + fn = do_one_spec("spid", NULL); + if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use)) + return "-T with H/-m/m but no PID for SPID to follow"; + } + if(thread_flags & TF_U_L){ + fn = do_one_spec("lwp", NULL); + if(fmt_add_after("SID", fn)) goto did_lwp; + if(fmt_add_after("SESS", fn)) goto did_lwp; + if(fmt_add_after("PGID", fn)) goto did_lwp; + if(fmt_add_after("PGRP", fn)) goto did_lwp; + if(fmt_add_after("PPID", fn)) goto did_lwp; + if(fmt_add_after("PID", fn)) goto did_lwp; + if(thread_flags&TF_must_use) + return "-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow"; +did_lwp: + fn = do_one_spec("nlwp", NULL); + fmt_add_after("%CPU", fn); + } + if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style + fn = do_one_spec("label", NULL); + fn->next=format_list; + format_list=fn; + } + /* Do personality-specific translations not covered by format_flags. + * Generally, these only get hit when personality overrides unix output. + * That (mostly?) means the Digital and Debian personalities. + */ + if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){ + fn = do_one_spec("sgi_p", NULL); + if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR"); + } + if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){ + fn = do_one_spec("user", NULL); + if(fmt_add_after("UID", fn)) fmt_delete("UID"); + } + }while(0); + + return NULL; +} + diff --git a/pwdx.1 b/pwdx.1 new file mode 100644 index 0000000..728157d --- /dev/null +++ b/pwdx.1 @@ -0,0 +1,37 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for pwdx +.\" Licensed under version 2 of the GNU General Public License. +.\" Copyright 2004 Nicholas Miell. +.\" Based on the pmap(1) man page by Albert Cahalan. +.\" +.TH PWDX 1 "September 8, 2004" "Linux" "Linux User's Manual" +.SH NAME +pwdx \- report current working directory of a process + +.SH SYNOPSIS +.nf +pwdx pids... +pwdx -V +.fi + +.SH DESCRIPTION +The pwdx command reports the current working directory of a process or +processes. + +.SH "GENERAL OPTIONS" +.TS +l l l. +-V show version Displays version of program. +.TE + +.SH "SEE ALSO" +ps(1) pgrep(1) + +.SH STANDARDS +No standards apply, but pwdx looks an awful lot like a SunOS command. + +.SH AUTHOR +Nicholas Miell wrote pwdx in 2004. Please send bug +reports to . diff --git a/pwdx.c b/pwdx.c new file mode 100644 index 0000000..cb96a52 --- /dev/null +++ b/pwdx.c @@ -0,0 +1,95 @@ +// Copyright 2004 Nicholas Miell +// +// This file may be used subject to the terms and conditions of the +// GNU Library General Public License Version 2 as published by the +// Free Software Foundation.This program is distributed in the hope +// that it will be useful, but WITHOUT ANY WARRANTY; without even the +// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the GNU Library General Public License for more +// details. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/version.h" + +static void die(const char *msg) NORETURN; +static void die(const char *msg) +{ + fputs(msg, stderr); + exit(1); +} + +static void version(void) NORETURN; +static void version(void) +{ + printf("pwdx (%s)\n", procps_version); + exit(0); +} + +int main(int argc, char* argv[]) +{ + char buf[PATH_MAX+1]; + regex_t re; + int i; + + if (argc < 2) + die("Usage: pwdx pid...\n"); + + // Allowed on the command line: + // + // --version + // -V + // /proc/nnnn + // nnnn + // + // where nnnn is any number that doesn't begin with 0. + // + // If --version or -V are present, further arguments are ignored + // completely. + + regcomp(&re, "^((/proc/+)?[1-9][0-9]*|-V|--version)$", + REG_EXTENDED|REG_NOSUB); + + for (i = 1; i < argc; i++) { + if (regexec(&re, argv[i], 0, NULL, 0) != 0) { + snprintf(buf, sizeof buf, "pwdx: invalid process id: %s\n", argv[i]); + die(buf); + } + if (!strcmp("-V", argv[i]) || !strcmp("--version", argv[i])) + version(); + } + + regfree(&re); + + for (i = 1; i < argc; i++) { + char * s = buf; + int len; + + // At this point, all arguments are in the form /proc/nnnn + // or nnnn, so a simple check based on the first char is + // possible + if (argv[i][0] != '/') + snprintf(buf, sizeof buf, "/proc/%s/cwd", argv[i]); + else + snprintf(buf, sizeof buf, "%s/cwd", argv[i]); + + // buf contains /proc/nnnn/cwd symlink name on entry, the + // target of that symlink on return + if ((len = readlink(buf, buf, PATH_MAX)) < 0) { + s = strerror(errno == ENOENT ? ESRCH : errno); + } else { + buf[len] = 0; + } + + printf("%s: %s\n", argv[i], s); + } + + return 0; +} diff --git a/skill.1 b/skill.1 new file mode 100644 index 0000000..a4fc274 --- /dev/null +++ b/skill.1 @@ -0,0 +1,123 @@ +'\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for skill and snice. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan, converted to a man page by +.\" Michael K. Johnson +.\" +.TH SKILL 1 "March 12, 1999" "Linux" "Linux User's Manual" +.SH NAME +skill, snice \- send a signal or report process status + +.SH SYNOPSIS +.nf +skill [signal to send] [options] process selection criteria +snice [new priority] [options] process selection criteria +.fi + +.SH DESCRIPTION +These tools are probably obsolete and unportable. The command +syntax is poorly defined. Consider using the killall, pkill, +and pgrep commands instead. + +The default signal for skill is TERM. Use -l or -L to list available signals. +Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0. +Alternate signals may be specified in three ways: -9 -SIGKILL -KILL. + +The default priority for snice is +4. (snice +4 ...) +Priority numbers range from +20 (slowest) to -20 (fastest). +Negative priority numbers are restricted to administrative users. + +.SH "GENERAL OPTIONS" +.TS +l l l. +-f fast mode This is not currently useful. +-i interactive use T{ +You will be asked to approve each action. +T} +-v verbose output T{ +Display information about selected processes. +T} +-w warnings enabled This is not currently useful. +-n no action This only displays the process ID. +-V show version Displays version of program. +.TE + +.SH "PROCESS SELECTION OPTIONS" +Selection criteria can be: terminal, user, pid, command. +The options below may be used to ensure correct interpretation. +Do not blame Albert for this interesting interface. +.TS +l l. +-t The next argument is a terminal (tty or pty). +-u The next argument is a username. +-p The next argument is a process ID number. +-c The next argument is a command name. +.TE + +.SH SIGNALS +The signals listed below may be available for use with skill. +When known, numbers and default behavior are shown. +.TS +lB rB lB lB +lfCW r l l. +Name Num Action Description +.TH +0 0 n/a exit code indicates if a signal may be sent +ALRM 14 exit +HUP 1 exit +INT 2 exit +KILL 9 exit this signal may not be blocked +PIPE 13 exit +POLL exit +PROF exit +TERM 15 exit +USR1 exit +USR2 exit +VTALRM exit +STKFLT exit may not be implemented +PWR ignore may exit on some systems +WINCH ignore +CHLD ignore +URG ignore +TSTP stop may interact with the shell +TTIN stop may interact with the shell +TTOU stop may interact with the shell +STOP stop this signal may not be blocked +CONT restart continue if stopped, otherwise ignore +ABRT 6 core +FPE 8 core +ILL 4 core +QUIT 3 core +SEGV 11 core +TRAP 5 core +SYS core may not be implemented +EMT core may not be implemented +BUS core core dump may fail +XCPU core core dump may fail +XFSZ core core dump may fail +.TE + +.SH EXAMPLES +.TS +lB lB +lfCW l. +Command Description +.TC +snice seti crack +7 Slow down seti and crack +skill -KILL -v /dev/pts/* Kill users on new-style PTY devices +skill -STOP viro lm davem Stop 3 users +snice -17 root bash Give priority to root's shell +.TE + +.SH "SEE ALSO" +killall(1) pkill(1) kill(1) renice(1) nice(1) signal(7) kill(2) + +.SH STANDARDS +No standards apply. + +.SH AUTHOR +Albert Cahalan wrote skill and snice in 1999 as a +replacement for a non-free version, and is the current maintainer of the +procps collection. Please send bug reports to . diff --git a/skill.c b/skill.c new file mode 100644 index 0000000..96cc386 --- /dev/null +++ b/skill.c @@ -0,0 +1,585 @@ +/* + * Copyright 1998-2002 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/pwcache.h" +#include "proc/sig.h" +#include "proc/devname.h" +#include "proc/procps.h" /* char *user_from_uid(uid_t uid) */ +#include "proc/version.h" /* procps_version */ + +static int f_flag, i_flag, v_flag, w_flag, n_flag; + +static int tty_count, uid_count, cmd_count, pid_count; +static int *ttys; +static uid_t *uids; +static const char **cmds; +static int *pids; + +#define ENLIST(thing,addme) do{ \ +if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \ +if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \ +thing##s[thing##_count++] = addme; \ +}while(0) + +static int my_pid; +static int saved_argc; + +static int sig_or_pri; + +static int program; +#define PROG_GARBAGE 0 /* keep this 0 */ +#define PROG_KILL 1 +#define PROG_SKILL 2 +/* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */ +#define PROG_SNICE 4 + + +/********************************************************************/ + +static void display_kill_version(void){ + switch(program) { + case PROG_KILL: + fprintf(stdout, "kill (%s)\n",procps_version); + return; + case PROG_SKILL: + fprintf(stdout, "skill (%s)\n",procps_version); + return; + case PROG_SNICE: + fprintf(stdout, "snice (%s)\n",procps_version); + return; + default: + fprintf(stdout, "unknown (%s)\n",procps_version); + return; + } +} + +/***** kill or nice a process */ +static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd){ + int failed; + int saved_errno; + char dn_buf[1000]; + dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV); + if(i_flag){ + char buf[8]; + fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + if(!fgets(buf,7,stdin)){ + printf("\n"); + exit(0); + } + if(*buf!='y' && *buf!='Y') return; + } + /* do the actual work */ + if(program==PROG_SKILL) failed=kill(pid,sig_or_pri); + else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri); + saved_errno = errno; + if(w_flag && failed){ + fprintf(stderr, "%-8s %-8s %5d %-16.16s ", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + errno = saved_errno; + perror(""); + return; + } + if(i_flag) return; + if(v_flag){ + printf("%-8s %-8s %5d %-16.16s\n", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + return; + } + if(n_flag){ + printf("%d\n",pid); + return; + } +} + + +/***** check one process */ +static void check_proc(int pid){ + char buf[128]; + struct stat statbuf; + char *tmp; + int tty; + int fd; + int i; + if(pid==my_pid) return; + sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */ + fd = open(buf,O_RDONLY); + if(fd==-1){ /* process exited maybe */ + if(pids && w_flag) printf("WARNING: process %d could not be found.",pid); + return; + } + fstat(fd, &statbuf); + if(uids){ /* check the EUID */ + i=uid_count; + while(i--) if(uids[i]==statbuf.st_uid) break; + if(i==-1) goto closure; + } + read(fd,buf,128); + buf[127] = '\0'; + tmp = strrchr(buf, ')'); + *tmp++ = '\0'; + i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */ + tty = atoi(tmp); + if(ttys){ + i=tty_count; + while(i--) if(ttys[i]==tty) break; + if(i==-1) goto closure; + } + tmp = strchr(buf, '(') + 1; + if(cmds){ + i=cmd_count; + /* fast comparison trick -- useful? */ + while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break; + if(i==-1) goto closure; + } + /* This is where we kill/nice something. */ +/* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n", + pid, statbuf.st_uid, tty>>8, tty&0xf, tmp + ); +*/ + hurt_proc(tty, statbuf.st_uid, pid, tmp); +closure: + close(fd); /* kill/nice _first_ to avoid PID reuse */ +} + + +/***** debug function */ +#if 0 +static void show_lists(void){ + int i; + + fprintf(stderr, "%d TTY: ", tty_count); + if(ttys){ + i=tty_count; + while(i--){ + fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n'); + } + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d UID: ", uid_count); + if(uids){ + i=uid_count; + while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d PID: ", pid_count); + if(pids){ + i=pid_count; + while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d CMD: ", cmd_count); + if(cmds){ + i=cmd_count; + while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); +} +#endif + + +/***** iterate over all PIDs */ +static void iterate(void){ + int pid; + DIR *d; + struct dirent *de; + if(pids){ + pid = pid_count; + while(pid--) check_proc(pids[pid]); + return; + } +#if 0 + /* could setuid() and kill -1 to have the kernel wipe out a user */ + if(!ttys && !cmds && !pids && !i_flag){ + } +#endif + d = opendir("/proc"); + if(!d){ + perror("/proc"); + exit(1); + } + while(( de = readdir(d) )){ + if(de->d_name[0] > '9') continue; + if(de->d_name[0] < '1') continue; + pid = atoi(de->d_name); + if(pid) check_proc(pid); + } + closedir (d); +} + +/***** kill help */ +static void kill_usage(void) NORETURN; +static void kill_usage(void){ + fprintf(stderr, + "Usage:\n" + " kill pid ... Send SIGTERM to every process listed.\n" + " kill signal pid ... Send a signal to every process listed.\n" + " kill -s signal pid ... Send a signal to every process listed.\n" + " kill -l List all signal names.\n" + " kill -L List all signal names in a nice table.\n" + " kill -l signal Convert between signal numbers and names.\n" + ); + exit(1); +} + +/***** kill */ +static void kill_main(int argc, const char *restrict const *restrict argv) NORETURN; +static void kill_main(int argc, const char *restrict const *restrict argv){ + const char *sigptr; + int signo = SIGTERM; + int exitvalue = 0; + if(argc<2) kill_usage(); + if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){ + display_kill_version(); + exit(0); + } + if(argv[1][0]!='-'){ + argv++; + argc--; + goto no_more_args; + } + + /* The -l option prints out signal names. */ + if(argv[1][1]=='l' && argv[1][2]=='\0'){ + if(argc==2){ + unix_print_signals(); + exit(0); + } + /* at this point, argc must be 3 or more */ + if(argc>128 || argv[2][0] == '-') kill_usage(); + exit(print_given_signals(argc-2, argv+2, 80)); + } + + /* The -L option prints out signal names in a nice table. */ + if(argv[1][1]=='L' && argv[1][2]=='\0'){ + if(argc==2){ + pretty_print_signals(); + exit(0); + } + kill_usage(); + } + if(argv[1][1]=='-' && argv[1][2]=='\0'){ + argv+=2; + argc-=2; + goto no_more_args; + } + if(argv[1][1]=='-') kill_usage(); /* likely --help */ + // FIXME: "kill -sWINCH $$" not handled + if(argv[1][2]=='\0' && (argv[1][1]=='s' || argv[1][1]=='n')){ + sigptr = argv[2]; + argv+=3; + argc-=3; + }else{ + sigptr = argv[1]+1; + argv+=2; + argc-=2; + } + signo = signal_name_to_number(sigptr); + if(signo<0){ + fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr); + kill_usage(); + } +no_more_args: + if(!argc) kill_usage(); /* nothing to kill? */ + while(argc--){ + long pid; + char *endp; + pid = strtol(argv[argc],&endp,10); + if(!*endp){ + if(!kill((pid_t)pid,signo)) continue; + // The UNIX standard contradicts itself. If at least one process + // is matched for each PID (as if processes could share PID!) and + // "the specified signal was successfully processed" (the systcall + // returned zero?) for at least one of those processes, then we must + // exit with zero. Note that an error might have also occured. + // The standard says we return non-zero if an error occurs. Thus if + // killing two processes gives 0 for one and EPERM for the other, + // we are required to return both zero and non-zero. Quantum kill??? + exitvalue = 1; + continue; + } + fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]); + kill_usage(); + } + exit(exitvalue); +} + +/***** skill/snice help */ +static void skillsnice_usage(void) NORETURN; +static void skillsnice_usage(void){ + if(program==PROG_SKILL){ + fprintf(stderr, + "Usage: skill [signal to send] [options] process selection criteria\n" + "Example: skill -KILL -v pts/*\n" + "\n" + "The default signal is TERM. Use -l or -L to list available signals.\n" + "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n" + "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n" + ); + }else{ + fprintf(stderr, + "Usage: snice [new priority] [options] process selection criteria\n" + "Example: snice netscape crack +7\n" + "\n" + "The default priority is +4. (snice +4 ...)\n" + "Priority numbers range from +20 (slowest) to -20 (fastest).\n" + "Negative priority numbers are restricted to administrative users.\n" + ); + } + fprintf(stderr, + "\n" + "General options:\n" + "-f fast mode This is not currently useful.\n" + "-i interactive use You will be asked to approve each action.\n" + "-v verbose output Display information about selected processes.\n" + "-w warnings enabled This is not currently useful.\n" + "-n no action This only displays the process ID.\n" + "\n" + "Selection criteria can be: terminal, user, pid, command.\n" + "The options below may be used to ensure correct interpretation.\n" + "-t The next argument is a terminal (tty or pty).\n" + "-u The next argument is a username.\n" + "-p The next argument is a process ID number.\n" + "-c The next argument is a command name.\n" + ); + exit(1); +} + +#if 0 +static void _skillsnice_usage(int line){ + fprintf(stderr,"Something at line %d.\n", line); + skillsnice_usage(); +} +#define skillsnice_usage() _skillsnice_usage(__LINE__) +#endif + +#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL) + +/***** common skill/snice argument parsing code */ +#define NO_PRI_VAL ((int)0xdeafbeef) +static void skillsnice_parse(int argc, const char *restrict const *restrict argv){ + int signo = -1; + int prino = NO_PRI_VAL; + int force = 0; + int num_found = 0; + const char *restrict argptr; + if(argc<2) skillsnice_usage(); + if(argc==2 && argv[1][0]=='-'){ + if(!strcmp(argv[1],"-L")){ + pretty_print_signals(); + exit(0); + } + if(!strcmp(argv[1],"-l")){ + unix_print_signals(); + exit(0); + } + if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){ + display_kill_version(); + exit(0); + } + skillsnice_usage(); + } + NEXTARG; + /* Time for serious parsing. What does "skill -int 123 456" mean? */ + while(argc){ + if(force && !num_found){ /* if forced, _must_ find something */ + fprintf(stderr,"ERROR: -%c used with bad data.\n", force); + skillsnice_usage(); + } + force = 0; + if(program==PROG_SKILL && signo<0 && *argptr=='-'){ + signo = signal_name_to_number(argptr+1); + if(signo>=0){ /* found a signal */ + if(!NEXTARG) break; + continue; + } + } + if(program==PROG_SNICE && prino==NO_PRI_VAL + && (*argptr=='+' || *argptr=='-') && argptr[1]){ + long val; + char *endp; + val = strtol(argptr,&endp,10); + if(!*endp && val<=999 && val>=-999){ + prino=val; + if(!NEXTARG) break; + continue; + } + } + /* If '-' found, collect any flags. (but lone "-" is a tty) */ + if(*argptr=='-' && argptr[1]){ + argptr++; + do{ + switch(( force = *argptr++ )){ + default: skillsnice_usage(); + case 't': + case 'u': + case 'p': + case 'c': + if(!*argptr){ /* nothing left here, *argptr is '\0' */ + if(!NEXTARG){ + fprintf(stderr,"ERROR: -%c with nothing after it.\n", force); + skillsnice_usage(); + } + } + goto selection_collection; + case 'f': f_flag++; break; + case 'i': i_flag++; break; + case 'v': v_flag++; break; + case 'w': w_flag++; break; + case 'n': n_flag++; break; + case 0: + NEXTARG; + /* + * If no more arguments, all the "if(argc)..." tests will fail + * and the big loop will exit. + */ + } /* END OF SWITCH */ + }while(force); + } /* END OF IF */ +selection_collection: + num_found = 0; /* we should find at least one thing */ + switch(force){ /* fall through each data type */ + default: skillsnice_usage(); + case 0: /* not forced */ + case 't': + if(argc){ + struct stat sbuf; + char path[32]; + if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */ + snprintf(path,32,"/dev/%s",argptr); + if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){ + num_found++; + ENLIST(tty,sbuf.st_rdev); + if(!NEXTARG) break; + }else if(!(argptr[1])){ /* if only 1 character */ + switch(*argptr){ + default: + if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */ + case '-': + case '?': + num_found++; + ENLIST(tty,0); + if(!NEXTARG) break; + } + } + } + if(force) continue; + case 'u': + if(argc){ + struct passwd *passwd_data; + passwd_data = getpwnam(argptr); + if(passwd_data){ + num_found++; + ENLIST(uid,passwd_data->pw_uid); + if(!NEXTARG) break; + } + } + if(force) continue; + case 'p': + if(argc && *argptr>='0' && *argptr<='9'){ + char *endp; + int num; + num = strtol(argptr, &endp, 0); + if(*endp == '\0'){ + num_found++; + ENLIST(pid,num); + if(!NEXTARG) break; + } + } + if(force) continue; + if(num_found) continue; /* could still be an option */ + case 'c': + if(argc){ + num_found++; + ENLIST(cmd,argptr); + if(!NEXTARG) break; + } + } /* END OF SWITCH */ + } /* END OF WHILE */ + /* No more arguments to process. Must sanity check. */ + if(!tty_count && !uid_count && !cmd_count && !pid_count){ + fprintf(stderr,"ERROR: no process selection criteria.\n"); + skillsnice_usage(); + } + if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){ + fprintf(stderr,"ERROR: general flags may not be repeated.\n"); + skillsnice_usage(); + } + if(i_flag && (v_flag|f_flag|n_flag)){ + fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n"); + skillsnice_usage(); + } + if(v_flag && (i_flag|f_flag)){ + fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n"); + skillsnice_usage(); + } + /* OK, set up defaults */ + if(prino==NO_PRI_VAL) prino=4; + if(signo<0) signo=SIGTERM; + if(n_flag){ + program=PROG_SKILL; + signo=0; /* harmless */ + } + if(program==PROG_SKILL) sig_or_pri = signo; + else sig_or_pri = prino; +} + +/***** main body */ +int main(int argc, const char *argv[]){ + const char *tmpstr; + my_pid = getpid(); + saved_argc = argc; + if(!argc){ + fprintf(stderr,"ERROR: could not determine own name.\n"); + exit(1); + } + tmpstr=strrchr(*argv,'/'); + if(tmpstr) tmpstr++; + if(!tmpstr) tmpstr=*argv; + program = PROG_GARBAGE; + if(*tmpstr=='s'){ + setpriority(PRIO_PROCESS,my_pid,-20); + if(!strcmp(tmpstr,"snice")) program = PROG_SNICE; + if(!strcmp(tmpstr,"skill")) program = PROG_SKILL; + }else{ + if(!strcmp(tmpstr,"kill")) program = PROG_KILL; + } + switch(program){ + case PROG_SNICE: + case PROG_SKILL: + skillsnice_parse(argc, argv); +/* show_lists(); */ + iterate(); /* this is it, go get them */ + break; + case PROG_KILL: + kill_main(argc, argv); + break; + default: + fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr); + } + return 0; +} + + diff --git a/slabtop.1 b/slabtop.1 new file mode 100644 index 0000000..11f277e --- /dev/null +++ b/slabtop.1 @@ -0,0 +1,124 @@ +.\" slabtop.1 - manpage for the slabtop(1) utility, part of procps +.\" +.\" Copyright (C) 2003 Chris Rivera +.\" Licensed under the terms of the GNU Library General Public License, v2 +.TH SLABTOP 1 "13 Sep 2003" "Linux" "Linux User's Manual" +.SH NAME +slabtop \- display kernel slab cache information in real time + +.SH SYNOPSIS +.BI "slabtop [ " options " ] " + +.SH DESCRIPTION +.BR slabtop (1) +displays detailed kernel slab cache information in real time. It displays a +listing of the top caches sorted by one of the listed sort criteria. It also +displays a statistics header filled with slab layer information. + +.SH OPTIONS +Normal invocation of +.BR slabtop (1) +does not require any options. The behavior, however, can be fine-tuned by +specifying one or more of the following flags: +.TP +.B \-\^\-delay=n, \-d n +Refresh the display every n seconds. By default, +.BR slabtop (1) +refreshes the display every three seconds. To exit the program, hit +.BR q. +.TP +.B \-\^\-sort=S, \-s S +Sort by S, where S is one of the sort criteria. +.TP +.B \-\^\-once, \-o +Display the output once and then exit. +.TP +.B \-\^\-version, \-V +Display version information and exit. +.TP +.B \-\^\-help +Display usage information and exit. + +.SH SORT CRITERIA +The following are valid sort criteria used to sort the individual slab caches +and thereby determine what are the "top" slab caches to display. The default +sort criteria is to sort by the number of objects ("o"). + +The sort criteria can also be changed while slabtop is running by pressing +the associated character. +.TP +.BR a: +sort by number of active objects +.TP +.BR b: +sort by objects per slab +.TP +.BR c: +sort by cache size +.TP +.BR l: +sort by number of slabs +.TP +.BR v +sort by number of active slabs +.TP +.BR n: +sort by name +.TP +.BR o: +sort by number of objects +.TP +.BR p: +sort by pages per slab +.TP +.BR s: +sort by object size +.TP +.BR u: +sort by cache utilization + +.SH COMMANDS +.BR slabtop (1) +accepts keyboard commands from the user during use. The following are +supported. In the case of letters, both cases are accepted. + +Each of the valid sort characters are also accepted, to change the sort +routine. See the section +.IR "SORT CRITERIA" . + +.TP +.BR +Refresh the screen. +.TP +.BR Q +Quit the program. + +.SH FILES +.IR /proc/slabinfo " \-\- slab information" + +.SH "SEE ALSO" +.BR free (1), +.BR ps (1), +.BR top (1), +.BR vmstat (8) + +.SH NOTES +Currently, +.BR slabtop (1) +requires a 2.4 or later kernel (specifically, a version 1.1 or later +.IR /proc/slabinfo ). +Kernel 2.2 should be supported in the future. + +The slabtop statistic header is tracking how many bytes of slabs are being used +and it not a measure of physical memory. The 'Slab' field in the /proc/meminfo +file is tracking information about used slab physical memory. + +.SH AUTHORS +Written by Chris Rivera and Robert Love. + +.BR slabtop (1) +was inspired by Martin Bligh's perl script, +.BR vmtop . +The procps package is maintained by Albert Cahalan . + +Please send bug reports to . diff --git a/slabtop.c b/slabtop.c new file mode 100644 index 0000000..36025e2 --- /dev/null +++ b/slabtop.c @@ -0,0 +1,397 @@ +/* + * slabtop.c - utility to display kernel slab information. + * + * Chris Rivera + * Robert Love + * + * This program is licensed under the GNU Library General Public License, v2 + * + * Copyright (C) 2003 Chris Rivera + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "proc/slab.h" +#include "proc/version.h" + +#define DEF_SORT_FUNC sort_nr_objs +#define SLAB_STAT_ZERO { nr_objs: 0 } + +static unsigned short cols, rows; +static struct termios saved_tty; +static long delay = 3; +static int (*sort_func)(const struct slab_info *, const struct slab_info *); + +static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b) +{ + struct slab_info sorted_list; + struct slab_info *curr = &sorted_list; + + while ((a != NULL) && (b != NULL)) { + if (sort_func(a, b)) { + curr->next = a; + curr = a; + a = a->next; + } else { + curr->next = b; + curr = b; + b = b->next; + } + } + + curr->next = (a == NULL) ? b : a; + return sorted_list.next; +} + +/* + * slabsort - merge sort the slab_info linked list based on sort_func + */ +static struct slab_info *slabsort(struct slab_info *list) +{ + struct slab_info *a, *b; + + if ((list == NULL) || (list->next == NULL)) + return list; + + a = list; + b = list->next; + + while ((b != NULL) && (b->next != NULL)) { + list = list->next; + b = b->next->next; + } + + b = list->next; + list->next = NULL; + + return merge_objs(slabsort(a), slabsort(b)); +} + +/* + * Sort Routines. Each of these should be associated with a command-line + * search option. The functions should fit the prototype: + * + * int sort_foo(const struct slab_info *a, const struct slab_info *b) + * + * They return one if the first parameter is larger than the second + * Otherwise, they return zero. + */ + +static int sort_name(const struct slab_info *a, const struct slab_info *b) +{ + return (strcmp(a->name, b->name) < 0) ? 1 : 0; +} + +static int sort_nr_objs(const struct slab_info *a, const struct slab_info *b) +{ + return (a->nr_objs > b->nr_objs); +} + +static int sort_nr_active_objs(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->nr_active_objs > b->nr_active_objs); +} + +static int sort_obj_size(const struct slab_info *a, const struct slab_info *b) +{ + return (a->obj_size > b->obj_size); +} + +static int sort_objs_per_slab(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->objs_per_slab > b->objs_per_slab); +} + +static int sort_pages_per_slab(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->pages_per_slab > b->pages_per_slab); +} + +static int sort_nr_slabs(const struct slab_info *a, const struct slab_info *b) +{ + return (a->nr_slabs > b->nr_slabs); +} + +static int sort_nr_active_slabs(const struct slab_info *a, + const struct slab_info *b) +{ + return (a->nr_active_slabs > b->nr_active_slabs); +} + + +static int sort_use(const struct slab_info *a, const struct slab_info *b) +{ + return (a->use > b->use); +} + +static int sort_cache_size(const struct slab_info *a, const struct slab_info *b) +{ + return (a->cache_size > b->cache_size); +} + +/* + * term_size - set the globals 'cols' and 'rows' to the current terminal size + */ +static void term_size(int unused) +{ + struct winsize ws; + (void) unused; + + if ((ioctl(1, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) { + cols = ws.ws_col; + rows = ws.ws_row; + } else { + cols = 80; + rows = 24; + } +} + +static void sigint_handler(int unused) +{ + (void) unused; + + delay = 0; +} + +static void usage(const char *cmd) +{ + fprintf(stderr, "usage: %s [options]\n\n", cmd); + fprintf(stderr, "options:\n"); + fprintf(stderr, " --delay=n, -d n " + "delay n seconds between updates\n"); + fprintf(stderr, " --once, -o " + "only display once, then exit\n"); + fprintf(stderr, " --sort=S, -s S " + "specify sort criteria S (see below)\n"); + fprintf(stderr, " --version, -V " + "display version information and exit\n"); + fprintf(stderr, " --help display this help and exit\n\n"); + fprintf(stderr, "The following are valid sort criteria:\n"); + fprintf(stderr, " a: sort by number of active objects\n"); + fprintf(stderr, " b: sort by objects per slab\n"); + fprintf(stderr, " c: sort by cache size\n"); + fprintf(stderr, " l: sort by number of slabs\n"); + fprintf(stderr, " v: sort by number of active slabs\n"); + fprintf(stderr, " n: sort by name\n"); + fprintf(stderr, " o: sort by number of objects\n"); + fprintf(stderr, " p: sort by pages per slab\n"); + fprintf(stderr, " s: sort by object size\n"); + fprintf(stderr, " u: sort by cache utilization\n"); +} + +/* + * set_sort_func - return the slab_sort_func that matches the given key. + * On unrecognizable key, DEF_SORT_FUNC is returned. + */ +static void * set_sort_func(char key) +{ + switch (key) { + case 'n': + return sort_name; + case 'o': + return sort_nr_objs; + case 'a': + return sort_nr_active_objs; + case 's': + return sort_obj_size; + case 'b': + return sort_objs_per_slab; + case 'p': + return sort_pages_per_slab; + case 'l': + return sort_nr_slabs; + case 'v': + return sort_nr_active_slabs; + case 'c': + return sort_cache_size; + case 'u': + return sort_use; + default: + return DEF_SORT_FUNC; + } +} + +static void parse_input(char c) +{ + c = toupper(c); + switch(c) { + case 'A': + sort_func = sort_nr_active_objs; + break; + case 'B': + sort_func = sort_objs_per_slab; + break; + case 'C': + sort_func = sort_cache_size; + break; + case 'L': + sort_func = sort_nr_slabs; + break; + case 'V': + sort_func = sort_nr_active_slabs; + break; + case 'N': + sort_func = sort_name; + break; + case 'O': + sort_func = sort_nr_objs; + break; + case 'P': + sort_func = sort_pages_per_slab; + break; + case 'S': + sort_func = sort_obj_size; + break; + case 'U': + sort_func = sort_use; + break; + case 'Q': + delay = 0; + break; + } +} + +int main(int argc, char *argv[]) +{ + int o; + unsigned short old_rows; + struct slab_info *slab_list = NULL; + + struct option longopts[] = { + { "delay", 1, NULL, 'd' }, + { "sort", 1, NULL, 's' }, + { "once", 0, NULL, 'o' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + sort_func = DEF_SORT_FUNC; + + while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) { + int ret = 1; + + switch (o) { + case 'd': + errno = 0; + delay = strtol(optarg, NULL, 10); + if (errno) { + perror("strtoul"); + return 1; + } + if (delay < 0) { + fprintf(stderr, "error: can't have a "\ + "negative delay\n"); + exit(1); + } + break; + case 's': + sort_func = set_sort_func(optarg[0]); + break; + case 'o': + delay = 0; + break; + case 'V': + display_version(); + return 0; + case 'h': + ret = 0; + default: + usage(argv[0]); + return ret; + } + } + + if (tcgetattr(0, &saved_tty) == -1) + perror("tcgetattr"); + + initscr(); + term_size(0); + old_rows = rows; + resizeterm(rows, cols); + signal(SIGWINCH, term_size); + signal(SIGINT, sigint_handler); + + do { + struct slab_info *curr; + struct slab_stat stats = SLAB_STAT_ZERO; + struct timeval tv; + fd_set readfds; + char c; + int i; + + if (get_slabinfo(&slab_list, &stats)) + break; + + if (old_rows != rows) { + resizeterm(rows, cols); + old_rows = rows; + } + + move(0,0); + printw( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n" + " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n" + " Minimum / Average / Maximum Object : %.2fK / %.2fK / %.2fK\n\n", + stats.nr_active_objs, stats.nr_objs, 100.0 * stats.nr_active_objs / stats.nr_objs, + stats.nr_active_slabs, stats.nr_slabs, 100.0 * stats.nr_active_slabs / stats.nr_slabs, + stats.nr_active_caches, stats.nr_caches, 100.0 * stats.nr_active_caches / stats.nr_caches, + stats.active_size / 1024.0, stats.total_size / 1024.0, 100.0 * stats.active_size / stats.total_size, + stats.min_obj_size / 1024.0, stats.avg_obj_size / 1024.0, stats.max_obj_size / 1024.0 + ); + + slab_list = slabsort(slab_list); + + attron(A_REVERSE); + printw( "%6s %6s %4s %8s %6s %8s %10s %-23s\n", + "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS", + "OBJ/SLAB", "CACHE SIZE", "NAME"); + attroff(A_REVERSE); + + curr = slab_list; + for (i = 0; i < rows - 8 && curr->next; i++) { + printw("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n", + curr->nr_objs, curr->nr_active_objs, curr->use, + curr->obj_size / 1024.0, curr->nr_slabs, + curr->objs_per_slab, (unsigned)(curr->cache_size / 1024), + curr->name); + curr = curr->next; + } + + refresh(); + put_slabinfo(slab_list); + + FD_ZERO(&readfds); + FD_SET(0, &readfds); + tv.tv_sec = delay; + tv.tv_usec = 0; + if (select(1, &readfds, NULL, NULL, &tv) > 0) { + if (read(0, &c, 1) != 1) + break; + parse_input(c); + } + } while (delay); + + tcsetattr(0, TCSAFLUSH, &saved_tty); + free_slabinfo(slab_list); + endwin(); + return 0; +} diff --git a/snice.1 b/snice.1 new file mode 100644 index 0000000..1595a80 --- /dev/null +++ b/snice.1 @@ -0,0 +1 @@ +.so man1/skill.1 diff --git a/sysctl.8 b/sysctl.8 new file mode 100644 index 0000000..e26c4fb --- /dev/null +++ b/sysctl.8 @@ -0,0 +1,85 @@ +.\" Copyright 1999, George Staikos (staikos@0wned.org) +.\" This file may be used subject to the terms and conditions of the +.\" GNU General Public License Version 2, or any later version +.\" at your option, as published by the Free Software Foundation. +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details." +.TH SYSCTL 8 "21 Sep 1999" "" "" +.SH NAME +sysctl \- configure kernel parameters at runtime +.SH SYNOPSIS +.B "sysctl [-n] [-e] variable ..." +.br +.B "sysctl [-n] [-e] [-q] -w variable=value ..." +.br +.B "sysctl [-n] [-e] [-q] -p " +.br +.B "sysctl [-n] [-e] -a" +.br +.B "sysctl [-n] [-e] -A" +.SH DESCRIPTION +.B sysctl +is used to modify kernel parameters at runtime. The parameters available +are those listed under /proc/sys/. Procfs is required for +.B sysctl(8) +support in Linux. You can use +.B sysctl(8) +to both read and write sysctl data. +.SH PARAMETERS +.TP +.B "variable" +The name of a key to read from. An example is kernel.ostype. The '/' +separator is also accepted in place of a '.'. +.TP +.B "variable=value" +To set a key, use the form variable=value, where variable is the key and +value is the value to set it to. If the value contains quotes or characters +which are parsed by the shell, you may need to enclose the value in double +quotes. This requires the -w parameter to use. +.TP +.B "-n" +Use this option to disable printing of the key name when printing values. +.TP +.B "-e" +Use this option to ignore errors about unknown keys. +.TP +.B "-N" +Use this option to only print the names. It may be useful with shells that +have programmable completion. +.TP +.B "-q" +Use this option to not display the values set to stdout. +.TP +.B "-w" +Use this option when you want to change a sysctl setting. +.TP +.B "-p" +Load in sysctl settings from the file specified or /etc/sysctl.conf if none given. +Specifying \- as filename means reading data from standard input. +.TP +.B "-a" +Display all values currently available. +.TP +.B "-A" +Display all values currently available in table form. +.SH EXAMPLES +.TP +/sbin/sysctl -a +.TP +/sbin/sysctl -n kernel.hostname +.TP +/sbin/sysctl -w kernel.domainname="example.com" +.TP +/sbin/sysctl -p /etc/sysctl.conf +.SH FILES +.I /proc/sys +.I /etc/sysctl.conf +.SH SEE ALSO +.BR sysctl.conf (5) +.SH BUGS +The -A parameter behaves just as -a does. +.SH AUTHOR +George Staikos, + diff --git a/sysctl.c b/sysctl.c new file mode 100644 index 0000000..1470df9 --- /dev/null +++ b/sysctl.c @@ -0,0 +1,516 @@ + +/* + * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters + * + * + * "Copyright 1999 George Staikos + * This file may be used subject to the terms and conditions of the + * GNU General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details." + * + * Changelog: + * v1.01: + * - added -p to preload values from a file + * Horms: + * - added -q to be quiet when modifying values + * + * Changes by Albert Cahalan, 2002. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/procps.h" +#include "proc/version.h" + + +// Proof that C++ causes brain damage: +typedef int bool; +static bool true = 1; +static bool false = 0; + +/* + * Globals... + */ + +static const char PROC_PATH[] = "/proc/sys/"; +static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf"; +static bool NameOnly; +static bool PrintName; +static bool PrintNewline; +static bool IgnoreError; +static bool Quiet; + +/* error messages */ +static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter \"%s\"\n"; +static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting \"%s\"\n"; +static const char ERR_NO_EQUALS[] = "error: \"%s\" must be of the form name=value\n"; +static const char ERR_INVALID_KEY[] = "error: \"%s\" is an unknown key\n"; +static const char ERR_UNKNOWN_WRITING[] = "error: \"%s\" setting key \"%s\"\n"; +static const char ERR_UNKNOWN_READING[] = "error: \"%s\" reading key \"%s\"\n"; +static const char ERR_PERMISSION_DENIED[] = "error: permission denied on key '%s'\n"; +static const char ERR_OPENING_DIR[] = "error: unable to open directory \"%s\"\n"; +static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n"; +static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n"; + + +static void slashdot(char *restrict p, char old, char new){ + p = strpbrk(p,"/."); + if(!p) return; /* nothing -- can't be, but oh well */ + if(*p==new) return; /* already in desired format */ + while(p){ + char c = *p; + if(c==old) *p=new; + if(c==new) *p=old; + p = strpbrk(p+1,"/."); + } +} + + + +/* + * Display the usage format + * + */ +static int Usage(const char *restrict const name) { + printf("usage: %s [-n] [-e] variable ... \n" + " %s [-n] [-e] [-q] -w variable=value ... \n" + " %s [-n] [-e] -a \n" + " %s [-n] [-e] [-q] -p (default /etc/sysctl.conf) \n" + " %s [-n] [-e] -A\n", name, name, name, name, name); + return -1; +} + + +/* + * Strip the leading and trailing spaces from a string + * + */ +static char *StripLeadingAndTrailingSpaces(char *oneline) { + char *t; + + if (!oneline || !*oneline) + return oneline; + + t = oneline; + t += strlen(oneline)-1; + + while ((*t==' ' || *t=='\t' || *t=='\n' || *t=='\r') && t!=oneline) + *t-- = 0; + + t = oneline; + + while ((*t==' ' || *t=='\t') && *t!=0) + t++; + + return t; +} + +static int DisplayAll(const char *restrict const path); + +/* + * Read a sysctl setting + * + */ +static int ReadSetting(const char *restrict const name) { + int rc = 0; + char *restrict tmpname; + char *restrict outname; + char inbuf[1025]; + FILE *restrict fp; + + if (!name || !*name) { + fprintf(stderr, ERR_INVALID_KEY, name); + return -1; + } + + /* used to open the file */ + tmpname = malloc(strlen(name)+strlen(PROC_PATH)+2); + strcpy(tmpname, PROC_PATH); + strcat(tmpname, name); + slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */ + + /* used to display the output */ + outname = strdup(name); + slashdot(outname,'/','.'); /* change / to . */ + + fp = fopen(tmpname, "r"); + + if (!fp) { + switch(errno) { + case ENOENT: + if (!IgnoreError) { + fprintf(stderr, ERR_INVALID_KEY, outname); + rc = -1; + } + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; + default: + fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname); + rc = -1; + break; + } + } else { + if(fgets(inbuf, sizeof inbuf - 1, fp)) { + // this loop is required, see + // /sbin/sysctl -a | egrep -6 dev.cdrom.info + do { + if (NameOnly) { + fprintf(stdout, "%s\n", outname); + } else { + /* already has the \n in it */ + if (PrintName) { + fprintf(stdout, "%s = %s", outname, inbuf); + } else { + if (!PrintNewline) { + char *nlptr = strchr(inbuf,'\n'); + if(nlptr) *nlptr='\0'; + } + fprintf(stdout, "%s", inbuf); + } + } + } while(fgets(inbuf, sizeof inbuf - 1, fp)); + } else { + switch(errno) { + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; + case EISDIR:{ + size_t len; + len = strlen(tmpname); + tmpname[len] = '/'; + tmpname[len+1] = '\0'; + rc = DisplayAll(tmpname); + break; + } + default: + fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname); + rc = -1; + break; + } + } + fclose(fp); + } + + free(tmpname); + free(outname); + return rc; +} + + + +/* + * Display all the sysctl settings + * + */ +static int DisplayAll(const char *restrict const path) { + int rc = 0; + int rc2; + DIR *restrict dp; + struct dirent *restrict de; + struct stat ts; + + dp = opendir(path); + + if (!dp) { + fprintf(stderr, ERR_OPENING_DIR, path); + rc = -1; + } else { + readdir(dp); // skip . + readdir(dp); // skip .. + while (( de = readdir(dp) )) { + char *restrict tmpdir; + tmpdir = (char *restrict)malloc(strlen(path)+strlen(de->d_name)+2); + sprintf(tmpdir, "%s%s", path, de->d_name); + rc2 = stat(tmpdir, &ts); + if (rc2 != 0) { + perror(tmpdir); + } else { + if (S_ISDIR(ts.st_mode)) { + strcat(tmpdir, "/"); + DisplayAll(tmpdir); + } else { + rc |= ReadSetting(tmpdir+strlen(PROC_PATH)); + } + } + free(tmpdir); + } + closedir(dp); + } + return rc; +} + + +/* + * Write a sysctl setting + * + */ +static int WriteSetting(const char *setting) { + int rc = 0; + const char *name = setting; + const char *value; + const char *equals; + char *tmpname; + FILE *fp; + char *outname; + + if (!name) { /* probably don't want to display this err */ + return 0; + } /* end if */ + + equals = index(setting, '='); + + if (!equals) { + fprintf(stderr, ERR_NO_EQUALS, setting); + return -1; + } + + value = equals + 1; /* point to the value in name=value */ + + if (!*name || !*value || name == equals) { + fprintf(stderr, ERR_MALFORMED_SETTING, setting); + return -2; + } + + /* used to open the file */ + tmpname = malloc(equals-name+1+strlen(PROC_PATH)); + strcpy(tmpname, PROC_PATH); + strncat(tmpname, name, (int)(equals-name)); + tmpname[equals-name+strlen(PROC_PATH)] = 0; + slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */ + + /* used to display the output */ + outname = malloc(equals-name+1); + strncpy(outname, name, (int)(equals-name)); + outname[equals-name] = 0; + slashdot(outname,'/','.'); /* change / to . */ + + fp = fopen(tmpname, "w"); + + if (!fp) { + switch(errno) { + case ENOENT: + if (!IgnoreError) { + fprintf(stderr, ERR_INVALID_KEY, outname); + rc = -1; + } + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + rc = -1; + break; + default: + fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname); + rc = -1; + break; + } + } else { + rc = fprintf(fp, "%s\n", value); + if (rc < 0) { + fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname); + fclose(fp); + } else { + rc=fclose(fp); + if (rc != 0) + fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname); + } + if (rc==0 && !Quiet) { + if (NameOnly) { + fprintf(stdout, "%s\n", outname); + } else { + if (PrintName) { + fprintf(stdout, "%s = %s\n", outname, value); + } else { + if (PrintNewline) + fprintf(stdout, "%s\n", value); + else + fprintf(stdout, "%s", value); + } + } + } + } + + free(tmpname); + free(outname); + return rc; +} + + + +/* + * Preload the sysctl's from the conf file + * - we parse the file and then reform it (strip out whitespace) + * + */ +static int Preload(const char *restrict const filename) { + char oneline[256]; + char buffer[256]; + FILE *fp; + char *t; + int n = 0; + int rc = 0; + char *name, *value; + + fp = (filename[0]=='-' && !filename[1]) + ? stdin + : fopen(filename, "r") + ; + + if (!fp) { + fprintf(stderr, ERR_PRELOAD_FILE, filename); + return -1; + } + + while (fgets(oneline, sizeof oneline, fp)) { + n++; + t = StripLeadingAndTrailingSpaces(oneline); + + if (strlen(t) < 2) + continue; + + if (*t == '#' || *t == ';') + continue; + + name = strtok(t, "="); + if (!name || !*name) { + fprintf(stderr, WARN_BAD_LINE, filename, n); + continue; + } + + StripLeadingAndTrailingSpaces(name); + + value = strtok(NULL, "\n\r"); + if (!value || !*value) { + fprintf(stderr, WARN_BAD_LINE, filename, n); + continue; + } + + while ((*value == ' ' || *value == '\t') && *value != 0) + value++; + + // should NameOnly affect this? + sprintf(buffer, "%s=%s", name, value); + rc |= WriteSetting(buffer); + } + + fclose(fp); + return rc; +} + + + +/* + * Main... + * + */ +int main(int argc, char *argv[]) { + const char *me = (const char *)basename(argv[0]); + bool SwitchesAllowed = true; + bool WriteMode = false; + int ReturnCode = 0; + const char *preloadfile = DEFAULT_PRELOAD; + + PrintName = true; + PrintNewline = true; + IgnoreError = false; + Quiet = false; + + if (argc < 2) { + return Usage(me); + } + + argv++; + + for (; argv && *argv && **argv; argv++) { + if (SwitchesAllowed && **argv == '-') { /* we have a switch */ + if ((*argv)[1] && (*argv)[2]){ // don't yet handle "sysctl -ew" + if (!strcmp("--help",*argv)) { + Usage(me); + exit(0); + } + if (!strcmp("--version",*argv)) { + fprintf(stdout, "sysctl (%s)\n",procps_version); + exit(0); + } + fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv); + return Usage(me); + } + switch((*argv)[1]) { + case 'b': + /* This is "binary" format, which means more for BSD. */ + PrintNewline = false; + /* FALL THROUGH */ + case 'n': + PrintName = false; + break; + case 'e': + // For FreeBSD, -e means a "%s=%s\n" format. ("%s: %s\n" default) + // We (and NetBSD) use "%s = %s\n" always, and -e to ignore errors. + IgnoreError = true; + break; + case 'N': + NameOnly = true; + break; + case 'w': + SwitchesAllowed = false; + WriteMode = true; + break; + case 'f': // the NetBSD way + case 'p': + argv++; + if (argv && *argv && **argv) { + preloadfile = *argv; + } + return Preload(preloadfile); + case 'q': + Quiet = true; + break; + case 'o': // BSD: binary values too, 1st 16 bytes in hex + case 'x': // BSD: binary values too, whole thing in hex + /* does nothing */ ; + break; + case 'a': // string and integer values (for Linux, all of them) + case 'A': // same as -a -o + case 'X': // same as -a -x + SwitchesAllowed = false; + return DisplayAll(PROC_PATH); + case 'V': + fprintf(stdout, "sysctl (%s)\n",procps_version); + exit(0); + case 'd': // BSD: print description ("vm.kvm_size: Size of KVM") + case 'h': // BSD: human-readable (did FreeBSD 5 make -e default?) + case '?': + return Usage(me); + default: + fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv); + return Usage(me); + } + } else { + if (NameOnly && Quiet) // nonsense + return Usage(me); + SwitchesAllowed = false; + if (WriteMode || index(*argv, '=')) + ReturnCode = WriteSetting(*argv); + else + ReturnCode = ReadSetting(*argv); + } + } + + return ReturnCode; +} + + diff --git a/sysctl.conf b/sysctl.conf new file mode 100644 index 0000000..720294f --- /dev/null +++ b/sysctl.conf @@ -0,0 +1,60 @@ +# /etc/sysctl.conf - Configuration file for setting system variables +# See sysctl.conf (5) for information. + +# you can have the CD-ROM close when you use it, and open +# when you are done. +#dev.cdrom.autoeject = 1 +#dev.cdrom.autoclose = 1 + +# protection from the SYN flood attack +net/ipv4/tcp_syncookies=1 + +# see the evil packets in your log files +net/ipv4/conf/all/log_martians=1 + +# makes you vulnerable or not :-) +net/ipv4/conf/all/accept_redirects=0 +net/ipv4/conf/all/accept_source_route=0 +net/ipv4/icmp_echo_ignore_broadcasts =1 + +# needed for routing, including masquerading or NAT +#net/ipv4/ip_forward=1 + +# sets the port range used for outgoing connections +#net.ipv4.ip_local_port_range = 32768 61000 + +# Broken routers and obsolete firewalls will corrupt the window scaling +# and ECN. Set these values to 0 to disable window scaling and ECN. +# This may, rarely, cause some performance loss when running high-speed +# TCP/IP over huge distances or running TCP/IP over connections with high +# packet loss and modern routers. This sure beats dropped connections. +#net.ipv4.tcp_default_win_scale = 0 +#net.ipv4.tcp_ecn = 0 + +# Swapping too much or not enough? Disks spinning up when you'd +# rather they didn't? Tweak these. +#vm.vfs_cache_pressure = 100 +#vm.laptop_mode = 0 +#vm.swappiness = 60 + +#kernel.printk_ratelimit_burst = 10 +#kernel.printk_ratelimit = 5 +#kernel.panic_on_oops = 0 + +# Reboot 600 seconds after a panic +#kernel.panic = 600 + +# enable SysRq key (note: console security issues) +#kernel.sysrq = 1 + +# Change name of core file to start with the command name +# so you get things like: emacs.core mozilla-bin.core X.core +#kernel.core_pattern = %e.core + +# NIS/YP domain (not always equal to DNS domain) +#kernel.domainname = example.com +#kernel.hostname = darkstar + +# This limits PID values to 4 digits, which allows tools like ps +# to save screen space. +kernel/pid_max=10000 diff --git a/sysctl.conf.5 b/sysctl.conf.5 new file mode 100644 index 0000000..0d8b073 --- /dev/null +++ b/sysctl.conf.5 @@ -0,0 +1,51 @@ +.\" Copyright 1999, George Staikos (staikos@0wned.org) +.\" This file may be used subject to the terms and conditions of the +.\" GNU General Public License Version 2, or any later version +.\" at your option, as published by the Free Software Foundation. +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details." +.TH SYSCTL.CONF 5 "21 Sep 1999" "" "" +.SH NAME +sysctl.conf \- sysctl(8) preload/configuration file +.SH DESCRIPTION +.I sysctl.conf +is a simple file containing sysctl values to be read in and set by sysctl(8). +The syntax is simply as follows: +.RS +.sp +.nf +.ne 7 +# comment +; comment + + token = value +.fi +.sp +.RE +.PP +Note that blank lines are ignored, and whitespace before and after a token or +value is ignored, although a value can contain whitespace within. Lines which +begin with a # or ; are considered comments and ignored. +.SH EXAMPLE +.RS +.sp +.nf +.ne 7 +# sysctl.conf sample +# + + kernel.domainname = example.com +; this one has a space which will be written to the sysctl! + kernel.modprobe = /sbin/mod probe + +.fi +.sp +.RE +.PP +.SH SEE ALSO +.BR sysctl(8) +.SH AUTHOR +George Staikos, + diff --git a/t b/t new file mode 100755 index 0000000..3842ee0 --- /dev/null +++ b/t @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with embedded spaces in arguments. +# The "$@" won't break that into 2 arguments. +# +LD_LIBRARY_PATH=proc exec ./top "$@" diff --git a/tload.1 b/tload.1 new file mode 100644 index 0000000..ef82b01 --- /dev/null +++ b/tload.1 @@ -0,0 +1,50 @@ +.\" -*-Nroff-*- +.\" This page Copyright (C) 1993 Matt Welsh, mdw@tc.cornell.edu. +.\" Freely distributable under the terms of the GPL +.TH TLOAD 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual" +.SH NAME +tload \- graphic representation of system load average +.SH SYNOPSIS +.B tload +.RB [ "\-V" "] [" "\-s" +.IR scale "] [" +.BI "\-d" " delay" +.RI "] [" tty ] +.SH DESCRIPTION +\fBtload\fP prints a graph of the current system load average to the +specified \fItty\fP (or the tty of the tload process if none is specified). +.SS Options +The +.BI "\-s" " scale" +option allows a vertical scale to be specified for the +display (in characters between graph ticks); thus, a smaller value +represents a larger scale, and vice versa. + +The +.BI "\-d" " delay" +sets the delay between graph updates in seconds. +.PP +.SH FILES +.I /proc/loadavg +load average information + +.SH "SEE ALSO" +.BR ps (1), +.BR top (1), +.BR uptime (1), +.BR w (1) + +.SH BUGS +The +.BI "\-d" " delay" +option sets the time argument for an +.BR alarm (2); +if -d 0 is specified, the alarm is set to 0, which will never send the +.B SIGALRM +and update the display. + +.SH AUTHORS +Branko Lankester, David Engel , and +Michael K. Johnson . + +Please send bug reports to diff --git a/tload.c b/tload.c new file mode 100644 index 0000000..1d346bb --- /dev/null +++ b/tload.c @@ -0,0 +1,156 @@ +/* + * tload.c - terminal version of xload + * + * Options: + * -s initial scaling exponent (default = 6) + * -d delay + * + * Copyright (c) 1992 Branko Lankester + * /proc changes by David Engel (david@ods.com) + * Made a little more efficient by Michael K. Johnson (johnsonm@sunsite.unc.edu) + */ +#include "proc/version.h" +#include "proc/sysinfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *screen; + +static int nrows = 25; +static int ncols = 80; +static int scr_size; +static int fd=1; +static int dly=5; +static jmp_buf jb; + +extern int optind; +extern char *optarg; + +static void alrm(int signo) +{ + (void)signo; + signal(SIGALRM, alrm); + alarm(dly); +} + +static void setsize(int i) +{ + struct winsize win; + + signal(SIGWINCH, setsize); + if (ioctl(fd, TIOCGWINSZ, &win) != -1) { + if (win.ws_col > 0) + ncols = win.ws_col; + if (win.ws_row > 0) + nrows = win.ws_row; + } + scr_size = nrows * ncols; + if (screen == NULL) + screen = (char *) malloc(scr_size); + else + screen = (char *) realloc(screen, scr_size); + + if (screen == NULL) { + perror(""); + exit(1); + } + memset(screen, ' ', scr_size-1); + *(screen + scr_size - 2) = '\0'; + if (i) + longjmp(jb, 0); +} + +int main(int argc, char **argv) +{ + int lines, row, col=0; + int i, opt; + double av[3]; + static double max_scale, scale_fact; + char *scale_arg = NULL; + + while ((opt = getopt(argc, argv, "s:d:V")) != -1) + switch (opt) { + case 's': scale_arg = optarg; break; + case 'd': dly = atoi(optarg); break; + case 'V': display_version(); exit(0); break; + default: + printf("usage: tload [-V] [-d delay] [-s scale] [tty]\n"); + exit(1); + } + + if (argc > optind) { + if ((fd = open(argv[optind], 1)) == -1) { + perror(argv[optind]); + exit(1); + } + } + + setsize(0); + + if (scale_arg) + max_scale = atof(scale_arg); + else + max_scale = nrows; + + scale_fact = max_scale; + + setjmp(jb); + col = 0; + alrm(0); + + while (1) { + + if (scale_fact < max_scale) + scale_fact *= 2.0; /* help it drift back up. */ + + loadavg(&av[0], &av[1], &av[2]); + + repeat: + lines = av[0] * scale_fact; + row = nrows-1; + + while (--lines >= 0) { + *(screen + row * ncols + col) = '*'; + if (--row < 0) { + scale_fact /= 2.0; + goto repeat; + } + } + while (row >= 0) + *(screen + row-- * ncols + col) = ' '; + + for (i = 1; ; ++i) { + char *p; + row = nrows - (i * scale_fact); + if (row < 0) + break; + if (*(p = screen + row * ncols + col) == ' ') + *p = '-'; + else + *p = '='; + } + + if (++col == ncols) { + --col; + memmove(screen, screen + 1, scr_size-1); + + for(row = nrows-2; row >= 0; --row) + *(screen + row * ncols + col) = ' '; + } + i = sprintf(screen, " %.2f, %.2f, %.2f", + av[0], av[1], av[2]); + if (i>0) + screen[i] = ' '; + + write(fd, "\033[H", 3); + write(fd, screen, scr_size - 1); + pause(); + } +} diff --git a/top.1 b/top.1 new file mode 100644 index 0000000..aeee3c9 --- /dev/null +++ b/top.1 @@ -0,0 +1,1617 @@ +.ig +. manual page for NEW top +. Copyright (c) 2002, by: JC Warner & Associates, Ltd. +. +. Permission is granted to copy, distribute and/or modify this document +. under the terms of the GNU Free Documentation License, Version 1.1 or +. any later version published by the Free Software Foundation; +. with no Front-Cover Texts, no Back-Cover Texts, and with the following +. Invariant Sections (and any sub-sections therein): +. all .ig sections, including this one +. STUPID TRICKS Sampler +. AUTHOR +. +. A copy of the Free Documentation License is included in the section +. entitled "GNU Free Documentation License". +. +. [ that section is found near the end of this document & ] +. [ can be made printable by disabling the .ig directive! ] +. +.. +.\" Setup //////////////////////////////////////////////////////////////// +\# ** Comment out '.nr' or set to 0 to eliminate WIDTH fiddlin' ! +.nr half_xtra 4 +. +.ll +(\n[half_xtra] + \n[half_xtra]) +. +\# Our darn Bullet style ---------------------------- +.de Jbu +.IP "-" 3 +.. +\# - bullet continuation paragraph +.de Jp +.IP "" 3 +.. +\# New features/differences style ------------------- +.de New +.IP "-*-" 5 +.. +. +\# Commonly used strings (for consistency) ---------- +\# - a real em-dash, darn-it +.ds EM \ \fB\-\-\ \fR +\# - these two are for chuckles, makes great grammar +.ds Me top +.ds ME \fBtop\fR +\# - other misc strings for consistent usage/emphasis +.ds F \fIOff\fR +.ds O \fIOn\fR +. +.ds AM alternate\-display mode +.ds AS asterisk ('*') +.ds CF configuration file +.ds CI interactive command +.ds CO command\-line option +.ds CW \'current' window +.ds FM full\-screen mode +.ds MP \fBphysical\fR memory +.ds MS \fBshared\fR memory +.ds MV \fBvirtual\fR memory +.ds NT \fBNote\fR: +.ds PU CPU +.ds Pu cpu +.ds SA summary area +.ds TA task area +.ds TD task display +.ds TW task window +\# - xref's that depend on commands or topic names +.ds XC See the +.ds Xc see the +.ds XT See topic +.ds Xt see topic +. +.\" ////////////////////////////////////////////////////////////////////// +.\" ---------------------------------------------------------------------- +.TH TOP 1 "September 2002" "Linux" "Linux User's Manual" +.\" ---------------------------------------------------------------------- + + +.\" ---------------------------------------------------------------------- +.SH NAME +.\" ---------------------------------------------------------------------- +top \- display Linux tasks + + +.\" ---------------------------------------------------------------------- +.SH SYNOPSIS +.\" ---------------------------------------------------------------------- +\*(ME \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI +iterations\fR \-\fBp\fI pid\fR [,\fI pid\fR ...] + +The traditional switches '-' and whitespace are optional. + + +.\" ---------------------------------------------------------------------- +.SH DESCRIPTION +.\" ---------------------------------------------------------------------- +The \*(ME program provides a dynamic real-time view of a running system. +It can display\fB system\fR summary information as well as a list of\fB +tasks\fR currently being managed by the Linux kernel. +The types of system summary information shown and the types, order and +size of information displayed for tasks are all user configurable and +that configuration can be made persistent across restarts. + +The program provides a limited interactive interface for process +manipulation as well as a much more extensive interface for personal +configuration \*(EM encompassing every aspect of its operation. +And while \*(ME is referred to throughout this document, you are free +to name the program anything you wish. +That new name, possibly an alias, will then be reflected on \*(Me's display +and used when reading and writing a \*(CF. + + +.\" ---------------------------------------------------------------------- +.SH OVERVIEW +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS Documentation +.\" ---------------------------------------------------------------------- +The remaining Table of Contents + 1. COMMAND\-LINE Options + 2. FIELDS / Columns + a. DESCRIPTIONS of Fields + b. SELECTING and ORDERING Columns + 3. INTERACTIVE Commands + a. GLOBAL Commands + b. SUMMARY Area Commands + c. TASK Area Commands + d. COLOR Mapping + 4. ALTERNATE\-DISPLAY Mode + a. WINDOWS Overview + b. COMMANDS for Windows + 5. FILES + a. SYSTEM Configuration File + b. PERSONAL Configuration File + 6. STUPID TRICKS Sampler + a. Kernel Magic + b. Bouncing Windows + c. The Big Bird Window + 7. BUGS, 8. HISTORY Former top, 9. AUTHOR, 10. SEE ALSO + +.\" ...................................................................... +.SS Operation +.\" ---------------------------------------------------------------------- +When operating \*(Me, the two most important keys are help ('h' or '?') and +quit ('q') key. +Alternatively, you could simply use the traditional interrupt key ('^C') +when you're done. + +When you start \*(Me for the first time, you'll be presented with the +traditional screen elements: 1) Summary Area; 2) Message/Prompt Line; +3) Columns Header; 4) Task Area. +There will, however, be some differences when compared to the former top. + +.TP 3 +.B Highlighting +.I Summary_Area\fR: +There is no highlighting for load/uptime and only values are highlighted for +other elements. + +.I Task_Area\fR: +Tasks running (or ready to run) will be highlighted, and bold is only one way +of emphasizing such processes. + +.TP 3 +.B Content/Labels +.I Summary_Area\fR: +The program name is shown, perhaps a symlink or alias. +The Cpu(s) state label hints at other possibilities. +The memory stats use a lower case 'k'. + +.I Columns_Header\fR: +Will show a new field and some changed labels. +More new fields will be found as you customize your \*(Me. + +.PP +\*(NT the width of \*(Me's display will be limited to 512 positions. +Displaying all fields requires a minimum of 160 characters. +The remaining width could be used for the 'Command' column. + +.\" ...................................................................... +.SS Startup Defaults +.\" ---------------------------------------------------------------------- +The following startup defaults assume no \*(CF, thus no user customizations. +Even so, items shown with an \*(AS could be overridden through the +command-line. + + \fIGlobal_defaults\fR + 'A' - Alt display Off (full-screen) + * 'd' - Delay time 3.0 seconds + 'I' - Irix mode On\ \ (no, 'solaris' smp) + * 'p' - PID monitoring Off + * 's' - Secure mode Off (unsecured) + 'B' - Bold disable Off + \fISummary_Area_defaults\fR + 'l' - Load Avg/Uptime On\ \ (thus program name) + 't' - Task/Cpu states On\ \ (1+1 lines, see '1') + 'm' - Mem/Swap usage On\ \ (2 lines worth) + '1' - Single Cpu On\ \ (thus 1 line if smp) + \fITask_Area_defaults\fR + 'b' - Bold hilite On\ \ (not 'reverse') + * 'c' - Command line Off (name, not cmdline) + * 'H' - Threads Off\ (show all threads) + * 'i' - Idle tasks On\ \ (show all tasks) + 'R' - Reverse sort On\ \ (pids high-to-low) + * 'S' - Cumulative time Off (no, dead children) + 'x' - Column hilite Off\ (no, sort field) + 'y' - Row hilite On\ \ (yes, running tasks) + 'z' - color/mono Off\ (no, colors) + + +.\" ---------------------------------------------------------------------- +.SH 1. COMMAND-LINE Options +.\" ---------------------------------------------------------------------- +The command-line syntax for \*(Me consists of: + + \-\fBhv\fR\ |\ -\fBbcHisS\fR\ \-\fBd\fI\ delay\fR\ \-\fBn\fI\ iterations\ +\fR\ \-\fBp\fI\ pid\fR\ [,\fIpid\fR...] + +The typically mandatory switches ('-') and even whitespace are completely +optional. + +.TP 5 +\-\fBb\fR :\fB Batch mode\fR operation +Starts \*(Me in 'Batch mode', which could be useful for sending output +from \*(Me to other programs or to a file. +In this mode, \*(Me will not accept input and runs until the iterations +limit you've set with the '-n' \*(CO or until killed. + +.TP 5 +\-\fBc\fR :\fB Command line/Program name\fR toggle +Starts \*(Me with the last remembered 'c' state reversed. +Thus, if \*(Me was displaying command lines, now that field will show program +names, and visa versa. +\*(XC 'c' \*(CI for additional information. + +.TP 5 +\-\fBd\fR :\fB Delay time\fR interval as:\ \ \fB-d ss.tt\fR (\fIseconds\fR.\fItenths\fR) +Specifies the delay between screen updates, and overrides the corresponding +value in one's personal \*(CF or the startup default. +Later this can be changed with the 'd' or 's' \*(CIs. + +Fractional seconds are honored, but a negative number is not allowed. +In all cases, however, such changes are prohibited if \*(Me is running +in 'Secure mode', except for root (unless the 's' \*(CO was used). +For additional information on 'Secure mode' \*(Xt 5a. SYSTEM Configuration File. + + +.TP 5 +\-\fBh\fR :\fB Help\fR +Show library version and the usage prompt, then quit. + +.TP 5 +\-\fBH\fR :\fB Threads\fR toggle +Starts \*(Me with the last remembered 'H' state reversed. +When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process. + +.TP 5 +\-\fBi\fR :\fB Idle Processes\fR toggle +Starts \*(Me with the last remembered 'i' state reversed. +When this toggle is \*F, tasks that are idled or zombied will not be displayed. + +.TP 5 +\-\fBn\fR :\fB Number of iterations\fR limit as:\fB\ \ -n number\fR +Specifies the maximum number of iterations, or frames, \*(Me should +produce before ending. + +.TP 5 +\-\fBu\fR :\fB Monitor by user\fR as:\fB\ \ -u somebody +Monitor only processes with an effective UID or user name +matching that given. + +.TP 5 +\-\fBU\fR :\fB Monitor by user\fR as:\fB\ \ -U somebody +Monitor only processes with a UID or user name matching that given. +This matches real, effective, saved, and filesystem UIDs. + +.TP 5 +\-\fBp\fR :\fB Monitor PIDs\fR as:\fB\ \ -pN1 -pN2 ...\fR\ \ or\fB\ \ -pN1, N2 [,...] +Monitor only processes with specified process IDs. +This option can be given up to 20 times, or you can provide a comma delimited +list with up to 20 pids. +Co-mingling both approaches is permitted. + +This is a \*(CO only. +And should you wish to return to normal operation, it is not necessary +to quit and and restart \*(Me \*(EM just issue the '=' \*(CI. + +.TP 5 +\-\fBs\fR :\fB Secure mode\fR operation +Starts \*(Me with secure mode forced, even for root. +This mode is far better controlled through the system \*(CF +(\*(Xt 5. FILES). + +.TP 5 +\-\fBS\fR :\fB Cumulative time mode\fR toggle +Starts \*(Me with the last remembered 'S' state reversed. +When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +time that it and its dead children have used. +\*(XC 'S' \*(CI for additional information regarding this mode. + +.TP 5 +\-\fBv\fR :\fB Version\fR +Show library version and the usage prompt, then quit. + + +.\" ---------------------------------------------------------------------- +.SH 2. FIELDS / Columns +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS 2a. DESCRIPTIONS of Fields +.\" ---------------------------------------------------------------------- +Listed below are \*(Me's available fields. +They are always associated with the letter shown, regardless of the position +you may have established for them with the 'o' (Order fields) \*(CI. + +Any field is selectable as the sort field, and you control whether they +are sorted high-to-low or low-to-high. +For additional information on sort provisions \*(Xt 3c. TASK Area Commands. + +.TP 3 +a:\fB PID\fR \*(EM Process Id\fR +The task's unique process ID, which periodically wraps, +though never restarting at zero. + +.TP 3 +b:\fB PPID\fR \*(EM Parent Process Pid\fR +The process ID of a task's parent. + +.TP 3 +c:\fB RUSER\fR \*(EM Real User Name\fR +The real user name of the task's owner. + +.TP 3 +d:\fB UID\fR \*(EM User Id\fR +The effective user ID of the task's owner. + +.TP 3 +e:\fB USER\fR \*(EM User Name\fR +The effective user name of the task's owner. + +.TP 3 +f:\fB GROUP\fR \*(EM Group Name\fR +The effective group name of the task's owner. + +.TP 3 +g:\fB TTY\fR \*(EM Controlling Tty +The name of the controlling terminal. +This is usually the device (serial port, pty, etc.) from which the +process was started, and which it uses for input or output. +However, a task need not be associated with a terminal, in which case +you'll see '?' displayed. + +.TP 3 +h:\fB PR\fR \*(EM Priority +The priority of the task. + +.TP 3 +i:\fB NI\fR \*(EM Nice value +The nice value of the task. +A negative nice value means higher priority, whereas a positive nice value +means lower priority. +Zero in this field simply means priority will not be adjusted in determining a +task's dispatchability. + +.TP 3 +j:\fB P\fR \*(EM Last used \*(PU (SMP) +A number representing the last used processor. +In a true SMP environment this will likely change frequently since the kernel +intentionally uses weak affinity. +Also, the very act of running \*(Me may break this weak affinity and cause more +processes to change \*(PUs more often (because of the extra demand for +\*(Pu time). + +.TP 3 +k:\fB %CPU\fR \*(EM \*(PU usage +The task's share of the elapsed \*(PU time since the last screen update, +expressed as a percentage of total \*(PU time. +In a true SMP environment, if 'Irix mode' is \*F, \*(Me will operate +in 'Solaris mode' where a task's \*(Pu usage will be divided by the total +number of \*(PUs. +You toggle 'Irix/Solaris' modes with the 'I' \*(CI. + +.TP 3 +l:\fB TIME\fR \*(EM \*(PU Time +Total \*(PU time the task has used since it started. +When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +time that it and its dead children has used. +You toggle 'Cumulative mode' with 'S', which is a \*(CO and an \*(CI. +\*(XC 'S' \*(CI for additional information regarding this mode. + +.TP 3 +m:\fB TIME+\fR \*(EM \*(PU Time, hundredths +The same as 'TIME', but reflecting more granularity through hundredths of +a second. + +.TP 3 +n:\fB %MEM\fR \*(EM Memory usage (RES) +A task's currently used share of available \*(MP. + +.TP 3 +o:\fB VIRT\fR \*(EM Virtual Image (kb) +The total amount of \*(MV used by the task. +It includes all code, data and shared libraries plus pages that have been +swapped out. + +VIRT = SWAP + RES. + +.TP 3 +p:\fB SWAP\fR \*(EM Swapped size (kb) +The swapped out portion of a task's total \*(MV image. + +.TP 3 +q:\fB RES\fR \*(EM Resident size (kb) +The non-swapped \*(MP a task has used. + +RES = CODE + DATA. + +.TP 3 +r:\fB CODE\fR \*(EM Code size (kb) +The amount of \*(MP devoted to executable code, also known as +the 'text resident set' size or TRS. + +.TP 3 +s:\fB DATA\fR \*(EM Data+Stack size (kb) +The amount of \*(MP devoted to other than executable code, also known as +the 'data resident set' size or DRS. + +.TP 3 +t:\fB SHR\fR \*(EM Shared Mem size (kb) +The amount of \*(MS used by a task. +It simply reflects memory that could be potentially shared with +other processes. + +.TP 3 +u:\fB nFLT\fR \*(EM Page Fault count +The number of\fB major\fR page faults that have occurred for a task. +A page fault occurs when a process attempts to read from or write to a virtual +page that is not currently present in its address space. +A major page fault is when disk access is involved in making that +page available. + +.TP 3 +v:\fB nDRT\fR \*(EM Dirty Pages count +The number of pages that have been modified since they were last +written to disk. +Dirty pages must be written to disk before the corresponding physical memory +location can be used for some other virtual page. + +.TP 3 +w:\fB S\fR \*(EM Process Status +The status of the task which can be one of: + '\fBD\fR' = uninterruptible sleep + '\fBR\fR' = running + '\fBS\fR' = sleeping + '\fBT\fR' = traced or stopped + '\fBZ\fR' = zombie + +Tasks shown as running should be more properly thought of as 'ready to run' +\*(EM their task_struct is simply represented on the Linux run-queue. +Even without a true SMP machine, you may see numerous tasks in this state +depending on \*(Me's delay interval and nice value. + +.TP 3 +x:\fB Command\fR \*(EM Command\fB line\fR or Program\fB name\fR +Display the command line used to start a task or the name of the associated +program. +You toggle between command\fI line\fR and\fI name\fR with 'c', which is both +a \*(CO and an \*(CI. + +When you've chosen to display command lines, processes without a command +line (like kernel threads) will be shown with only the program name in +parentheses, as in this example: + \fR( mdrecoveryd ) + +Either form of display is subject to potential truncation if it's too long to +fit in this field's current width. +That width depends upon other fields selected, their order and the current +screen width. + +\*(NT The 'Command' field/column is unique, in that it is not fixed-width. +When displayed, this column will be allocated all remaining screen width (up +to the maximum 512 characters) to provide for the potential growth of program +names into command lines. + +.TP 3 +y:\fB WCHAN\fR \*(EM Sleeping in Function +Depending on the availability of the kernel link map ('System.map'), this field +will show the name or the address of the kernel function in which the task is +currently sleeping. +Running tasks will display a dash ('-') in this column. + +\*(NT By displaying this field, \*(Me's own working set will be increased by +over 700Kb. +Your only means of reducing that overhead will be to stop and restart \*(Me. + +.TP 3 +z:\fB Flags\fR \*(EM Task Flags +This column represents the task's current scheduling flags which are +expressed in hexadecimal notation and with zeros suppressed. +These flags are officially documented in . +Less formal documentation can also be found on the 'Fields select' +and 'Order fields' screens. + +.\" ...................................................................... +.SS 2b. SELECTING and ORDERING Columns +.\" ---------------------------------------------------------------------- +After pressing the \*(CIs 'f' (Fields select) or \'o' (Order fields) you will +be shown a screen containing the current \fBfields string\fR followed by names +and descriptions for all fields. + +Here is a sample\fB fields string\fR from one of \*(Me's four windows/field +groups and an explanation of the conventions used: +.Jbu +Sample fields string: + \fIANOPQRSTUVXbcdefgjlmyzWHIK\fR +.Jbu +The order of displayed fields corresponds to the order of the letters +in that string. +.Jbu +If the letter is\fI upper case\fR the corresponding field itself will +then be shown as part of the \*(TD (screen width permitting). +This will also be indicated by a leading \*(AS, as in this excerpt: + \fR... + \fB* K: %CPU = CPU usage + \fR l: TIME = CPU Time + \fR m: TIME+ = CPU Time, hundredths + \fB* N: %MEM = Memory usage (RES) + \fB* O: VIRT = Virtual Image (kb) + \fR... + +.TP +.B Fields select\fR screen \*(EM the 'f' \*(CI +You\fI toggle\fR the\fB display\fR of a field by simply pressing the +corresponding letter. + +.TP +.B Order fields\fR screen \*(EM the 'o' \*(CI +You\fI move\fR a field to the\fB left\fR by pressing the corresponding\fB +upper case\fR letter and to the\fB right\fR with the\fB lower case\fR +letter. + + +.\" ---------------------------------------------------------------------- +.SH 3. INTERACTIVE Commands +.\" ---------------------------------------------------------------------- +Listed below is a brief index of commands within categories. +Some commands appear more than once \*(EM their meaning or scope may vary +depending on the context in which they are issued. + + 3a.\fI GLOBAL_Commands\fR + ?, =, A, B, d, G, h, I, k, q, r, s, W, Z + 3b.\fI SUMMARY_Area_Commands\fR + l, m, t, 1 + 3c.\fI TASK_Area_Commands\fR + Appearance: b, x, y, z + Content: c, f, H, o, S, u + Size: #, i, n + Sorting: <, >, F, O, R + 3d.\fI COLOR_Mapping\fR + , a, B, b, H, M, q, S, T, w, z, 0 - 7 + 4b.\fI COMMANDS_for_Windows\fR + -, _, =, +, A, a, G, g, w + +.\" ...................................................................... +.SS 3a. GLOBAL Commands +The global \*(CIs are\fB always\fR available\fR in both \*(FM and \*(AM. +However, some of these \*(CIs are\fB not available\fR when running +in 'Secure mode'. + +If you wish to know in advance whether or not your \*(Me has been secured, +simply ask for help and view the system summary on the second line. + +.TP 7 +\ \ \<\fBEnter\fR> or <\fBSpace\fR> :\fIRefresh_Display\fR +These commands do nothing, they are simply ignored. +However, they will awaken \*(Me and following receipt of any input +the entire display will be repainted. + +Use either of these keys if you have a large delay interval and wish to +see current status, + +.TP 7 +\ \ \'\fB?\fR\' or \'\fBh\fR\' :\fIHelp\fR +There are two help levels available. +The first will provide a reminder of all the basic \*(CIs. +If \*(Me is\fI secured\fR, that screen will be abbreviated. + +Typing 'h' or '?' on that help screen will take you to help for those \*(CIs +applicable to \*(AM. + +.TP 7 +\ \ \'\fB=\fR\' :\fIExit_Task_Limits\fR +Removes restrictions on which tasks are shown. +This command will reverse any 'i' (idle tasks) and 'n' (max tasks) commands +that might be active. +It also provides for an 'exit' from PID monitoring. +See the '-p' \*(CO for a discussion of PID monitoring. + +When operating in \*(AM this command has a slightly broader meaning. + +.TP 7 +\ \ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR +This command will switch between \*(FM and \*(AM. +\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into +\*(CWs and field groups. + +.TP 7 +\ \ \'\fBB\fR\' :\fIBold_Disable/Enable_toggle\fR +This command will influence use of the 'bold' terminfo capability and +alters\fB both\fR the \*(SA and \*(TA for the \*(CW. +While it is intended primarily for use with dumb terminals, it can be +applied anytime. + +\*(NT When this toggle is \*O and \*(Me is operating in monochrome mode, +the\fB entire display\fR will appear as normal text. +Thus, unless the 'x' and/or 'y' toggles are using reverse for emphasis, +there will be no visual confirmation that they are even on. + +.TP 7 +*\ \'\fBd\fR\' or \'\fBs\fR\' :\fIChange_Delay_Time_interval\fR +You will be prompted to enter the delay time, in seconds, between +display updates. + +Fractional seconds are honored, but a negative number is not allowed. +Entering 0 causes (nearly) continuous updates, with an unsatisfactory +display as the system and tty driver try to keep up with \*(Me's demands. +The delay value is inversely proportional to system loading, +so set it with care. + +If at any time you wish to know the current delay time, simply ask for help +and view the system summary on the second line. + +.TP 7 +\ \ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR +You will be prompted to enter a number between 1 and 4 designating the +window/field group which should be made the \*(CW. +You will soon grow comfortable with these 4 windows, especially after +experimenting with \*(AM. + +.TP 7 +\ \ \'\fBI\fR\' :\fIIrix/Solaris_Mode_toggle\fR +When operating in 'Solaris mode' ('I' toggled \*F), a task's \*(Pu usage +will be divided by the total number of \*(PUs. +After issuing this command, you'll be informed of the new state of this toggle. + +.TP 7 +\ \ \'\fBu\fR\' :\fIselect a user\fR +You will be prompted for a UID or username. Only processes +belonging to the selected user will be displayed. This option +matches on the effective UID. + +.TP 7 +\ \ \'\fBU\fR\' :\fIselect a user\fR +You will be prompted for a UID or username. Only processes +belonging to the selected user will be displayed. This option +matches on the real, effective, saved, and filesystem UID. + +.TP 7 +*\ \'\fBk\fR\' :\fIKill_a_task\fR +You will be prompted for a PID and then the signal to send. +The default signal, as reflected in the prompt, is SIGTERM. +However, you can send any signal, via number or name. + +If you wish to abort the kill process, do one of the following +depending on your progress: + 1) at the pid prompt, just press + 2) at the signal prompt, type 0 + +.TP 7 +\ \ \'\fBq\fR\' :\fIQuit\fR + +.TP 7 +*\ \'\fBr\fR\' :\fIRenice_a_Task\fR +You will be prompted for a PID and then the value to nice it to. +Entering a positive value will cause a process to lose priority. +Conversely, a negative value will cause a process to be viewed more +favorably by the kernel. + +.TP 7 +\ \ \'\fBW\fR\' :\fIWrite_the_Configuration_File\fR +This will save all of your options and toggles plus the current +display mode and delay time. +By issuing this command just before quitting \*(Me, you will be able restart +later in exactly that same state. + +.TP 7 +\ \ \'\fBZ\fR\' :\fIChange_Color_Mapping +This key will take you to a separate screen where you can change the +colors for the \*(CW, or for all windows. +For details regarding this \*(CI \*(Xt 3d. COLOR Mapping. + +.IP "*" 3 +The commands shown with an \*(AS are not available in 'Secure mode', +nor will they be shown on the level-1 help screen. + +.\" ...................................................................... +.SS 3b. SUMMARY Area Commands +The \*(SA \*(CIs are\fB always available\fR in both \*(FM and \*(AM. +They affect the beginning lines of your display and will determine the position +of messages and prompts. + +These commands always impact just the \*(CW/field group. +\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into +\*(CWs and field groups. + +.TP 7 +\ \ \'\fBl\fR\' :\fIToggle_Load_Average/Uptime\fR \*(EM On/Off +This is also the line containing the program name (possibly an alias) when +operating in \*(FM or the \*(CW name when operating in \*(AM. + +.TP 7 +\ \ \'\fBm\fR\' :\fIToggle_Memory/Swap_Usage\fR \*(EM On/Off +This command affects two \*(SA lines. + +.TP 7 +\ \ \'\fBt\fR\' :\fIToggle_Task/Cpu_States\fR \*(EM On/Off +This command affects from 2 to many \*(SA lines, depending on the state +of the '1' toggle and whether or not \*(Me is running under true SMP. + +.TP 7 +\ \ \'\fB1\fR\' :\fIToggle_Single/Separate_Cpu_States\fR \*(EM On/Off +This command affects how the 't' command's Cpu States portion is shown. +Although this toggle exists primarily to serve massively-parallel SMP machines, +it is not restricted to solely SMP environments. + +When you see 'Cpu(s):' in the \*(SA, the '1' toggle is \*O and all +\*(Pu information is gathered in a single line. +Otherwise, each \*(Pu is displayed separately as: 'Cpu0, Cpu1, ...' + +.PP +\*(NT If the entire \*(SA has been toggled \*F for any window, you would be left +with just the\fB message line\fR. +In that way, you will have maximized available task rows but (temporarily) +sacrificed the program name in \*(FM or the \*(CW name when in \*(AM. + +.\" ...................................................................... +.SS 3c. TASK Area Commands +The \*(TA \*(CIs are\fB always\fR available in \*(FM. + +The \*(TA \*(CIs are\fB never available\fR in \*(AM\fI if\fR the \*(CW's +\*(TD has been toggled \*F (\*(Xt 4. ALTERNATE\-DISPLAY Mode). + +.PP +.\" ......................... +.B APPEARANCE\fR of \*(TW +.br +.in +2 +The following commands will also be influenced by the state of the +global 'B' (bold disable) toggle. +.in + +.TP 7 +\ \ \'\fBb\fR\' :\fIBold/Reverse_toggle\fR +This command will impact how the 'x' and 'y' toggles are displayed. +Further, it will only be available when at least one of those toggles is \*O. + +.TP 7 +\ \ \'\fBx\fR\' :\fIColumn_Highlight_toggle\fR +Changes highlighting for the current sort field. +You probably don't need a constant visual reminder of the sort field and +\*(Me hopes that you always run with 'column highlight' \*F, due to the cost +in path-length. + +If you forget which field is being sorted this command can serve as a quick +visual reminder. + +.TP 7 +\ \ \'\fBy\fR\' :\fIRow_Highlight_toggle\fR +Changes highlighting for "running" tasks. +For additional insight into this task state, \*(Xt 2a. DESCRIPTIONS of Fields, +Process Status. + +Use of this provision provides important insight into your system's health. +The only costs will be a few additional tty escape sequences. + +.TP 7 +\ \ \'\fBz\fR\' :\fIColor/Monochrome_toggle\fR +Switches the \*(CW between your last used color scheme and the older form +of black-on-white or white-on-black. +This command will alter\fB both\fR the \*(SA and \*(TA but does not affect the +state of the 'x', 'y' or 'b' toggles. + +.PP +.\" ......................... +.B CONTENT\fR of \*(TW +.PD 0 +.TP 7 +\ \ \'\fBc\fR\' :\fICommand_Line/Program_Name_toggle\fR +This command will be honored whether or not the 'Command' column +is currently visible. +Later, should that field come into view, the change you applied will be seen. + +.TP 7 +\ \ \'\fBf\fR\' and \'\fBo\fR\' :\fIFields_select\fR or \fIOrder_fields\fR +These keys display separate screens where you can change which +fields are displayed and their order. +For additional information on these \*(CIs +\*(Xt 2b. SELECTING and ORDERING Columns. + +.TP 7 +\ \ \'\fBH\fR\' :\fIThreads_toggle\fR +When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process. + +.TP 7 +\ \ \'\fBS\fR\' :\fICumulative_Time_Mode_toggle\fR +When 'Cumulative mode' is \*O, each process is listed with the \*(Pu +time that it and its dead children have used. + +When \*F, programs that fork into many separate tasks will appear +less demanding. +For programs like 'init' or a shell this is appropriate but for others, +like compilers, perhaps not. +Experiment with two \*(TWs sharing the same sort field but with different 'S' +states and see which representation you prefer. + +After issuing this command, you'll be informed of the new state of this toggle. +If you wish to know in advance whether or not 'Cumulative mode' is in +effect, simply ask for help and view the window summary on the second line. + +.TP 7 +\ \ \'\fBu\fR\' :\fIShow_Specific_User_Only\fR +You will be prompted to enter the name of the user to display. +Thereafter, in that \*(TW only matching User ID's will be shown, or possibly +no tasks will be shown. + +Later, if you wish to monitor all tasks again, re-issue this command but +just press at the prompt, without providing a name. + +.PP +.\" ......................... +.B SIZE\fR of \*(TW +.PD 0 +.TP 7 +\ \ \'\fBi\fR\' :\fIIdle_Processes_toggle\fR +Displays all tasks or just active tasks. +When this toggle is \*F, idled or zombied processes will not be displayed. + +If this command is applied to the last \*(TD when in \*(AM, then it will not +affect the window's size, as all prior \*(TDs will have already been painted. + +.TP 7 +\ \ \'\fBn\fR\' or \'#\' :\fISet_Maximum_Tasks\fR +You will be prompted to enter the number of tasks to display. +The lessor of your number and available screen rows will be used. + +When used in \*(AM, this is the command that gives you precise control over +the size of each currently visible \*(TD, except for the very last. +It will not affect the last window's size, as all prior \*(TDs will have +already been painted. + +\*(NT If you wish to increase the size of the last visible \*(TD when in \*(AM, +simply decrease the size of the \*(TD(s) above it. + +.PP +.\" ......................... +.B SORTING\fR of \*(TW +.br +.in +2 +For compatibility, this \*(Me supports most of the former \*(Me sort keys. +Since this is primarily a service to former \*(Me users, these commands do +not appear on any help screen. + command sorted field supported + A start time (non-display) No + M %MEM Yes + N PID Yes + P %CPU Yes + T TIME+ Yes + +Before using any of the following sort provisions, \*(Me suggests that you +temporarily turn on column highlighting using the 'x' \*(CI. +That will help ensure that the actual sort environment matches your intent. + +The following \*(CIs will\fB only\fR be honored when the +current sort field is\fB visible\fR. +The sort field might\fI not\fR be visible because: + 1) there is insufficient\fI Screen Width\fR + 2) the 'f' \*(CI turned it \*F +.in + +.TP 7 +\ \ \'\fB<\fR\' :\fIMove_Sort_Field_Left\fR +Moves the sort column to the left unless the current sort field is +the first field being displayed. + +.TP 7 +\ \ \'\fB>\fR\' :\fIMove_Sort_Field_Right\fR +Moves the sort column to the right unless the current sort field is +the last field being displayed. + +.PP +.in +2 +The following \*(CIs will\fB always\fR be honored whether or not +the current sort field is visible. +.in + +.TP 7 +\ \ \'\fBF\fR\' or \'\fBO\fR\' :\fISelect_Sort_Field\fR +These keys display a separate screen where you can change which field +is used as the sort column. + +If a field is selected which was not previously being displayed, it will +be forced \*O when you return to the \*(Me display. +However, depending upon your screen width and the order of your fields, +this sort field may not be displayable. + +This \*(CI can be a convenient way to simply verify the current sort field, +when running \*(Me with column highlighting turned \*F. + +.TP 7 +\ \ \'\fBR\fR\' :\fIReverse/Normal_Sort_Field_toggle\fR +Using this \*(CI you can alternate between high-to-low and low-to-high sorts. + +.PP +.in +2 +\*(NT Field sorting uses internal values, not those in column display. +Thus, the TTY and WCHAN fields will violate strict ASCII collating sequence. +.in + +.\" ...................................................................... +.SS 3d. COLOR Mapping +When you issue the 'Z' \*(CI, you will be presented with a separate screen. +That screen can be used to change the colors in just the \*(CW or +in all four windows before returning to the \*(Me display. + +.P +.B Available \*(CIs + \fB4\fR upper case letters to select a\fB target\fR + \fB8\fR numbers to select a\fB color\fR + normal toggles available\fR + 'B' :bold disable/enable + 'b' :running tasks "bold"/reverse + 'z' :color/mono + other commands available\fR + 'a'/'w' :apply, then go to next/prior + :apply and exit + 'q' :abandon current changes and exit + +If your use 'a' or 'w' to cycle the targeted window, you will +have applied the color scheme that was displayed when you left that window. +You can, of course, easily return to any window and reapply different +colors or turn colors \*F completely with the 'z' toggle. + +The Color Mapping screen can also be used to change the \*(CW/field group +in either \*(FM or \*(AM. +Whatever was targeted when 'q' or was pressed will be made current +as you return to the \*(Me display. + + +.\" ---------------------------------------------------------------------- +.SH 4. ALTERNATE\-DISPLAY Mode +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS 4a. WINDOWS Overview +.TP +.B Field Groups/Windows\fR: +.br +In \*(FM there is a single window represented by the entire screen. +That single window can still be changed to display 1 of 4 different\fB field +groups\fR (\*(Xc 'G' \*(CI, repeated below). +Each of the 4 field groups has a unique separately configurable\fB \*(SA\fR +and its own configurable\fB \*(TA\fR. + +In \*(AM, those 4 underlying field groups can now be made visible +simultaneously, or can be turned \*F individually at your command. + +The \*(SA will always exist, even if it's only the message line. +At any given time only\fI one\fR \*(SA can be displayed. +However, depending on your commands, there could be from\fI zero\fR +to\fI four\fR separate \*(TDs currently showing on the screen. + +.TP +.B Current Window\fR: +.br +The \*(CW is the window associated with the \*(SA and the window to which +task related commands are always directed. +Since in \*(AM you can toggle the \*(TD \*F, some commands might be +restricted for the \*(CW. + +A further complication arises when you have toggled the first \*(SA +line \*F. +With the loss of the window name (the 'l' toggled line), you'll not easily +know what window is the \*(CW. + +.\" ...................................................................... +.SS 4b. COMMANDS for Windows +.TP 7 +\ \ \'\fB-\fR\' and \'\fB_\fR\' :\fIShow/Hide_Window(s)_toggles\fR +The '-' key turns the \*(CW's \*(TD \*O and \*F. +When \*O, that \*(TA will show a minimum of the columns header you've +established with the 'f' and 'o' commands. +It will also reflect any other \*(TA options/toggles you've applied yielding +zero or more tasks. + +The '_' key does the same for all \*(TDs. +In other words, it switches between the currently visible \*(TD(s) and any +\*(TD(s) you had toggled \*F. +If all 4 \*(TDs are currently visible, this \*(CI will leave the \*(SA +as the only display element. + +.TP 7 +*\ \'\fB=\fR\' and \'\fB+\fR\' :\fIEqualize_(re-balance)_Window(s)\fR +The '=' key forces the \*(CW's \*(TD to be visible. +It also reverses any 'i' (idle tasks) and 'n' (max tasks) commands that might +be active. + +The '+' key does the same for all windows. +The four \*(TDs will reappear, evenly balanced. +They will also have retained any customizations you had previously applied, +except for the 'i' (idle tasks) and 'n' (max tasks) commands. + +.TP 7 +*\ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR +This command will switch between \*(FM and \*(AM. + +The first time you issue this command, all four \*(TDs will be shown. +Thereafter when you switch modes, you will see only the \*(TD(s) you've +chosen to make visible. + +.TP 7 +*\ \'\fBa\fR\' and \'\fBw\fR\' :\fINext_Window_Forward/Backward\fR +This will change the \*(CW, which in turn changes the window to which +commands are directed. +These keys act in a circular fashion so you can reach any desired \*(CW +using either key. + +Assuming the window name is visible (you have not toggled 'l' \*F), +whenever the \*(CW name loses its emphasis/color, that's a reminder +the \*(TD is \*F and many commands will be restricted. + +.TP 7 +*\ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR +You will be prompted to enter a number between 1 and 4 designating the +window/field group which should be made the \*(CW. + +In \*(FM, this command is necessary to alter the \*(CW. +In \*(AM, it is simply a less convenient alternative to the 'a' and 'w' +commands. + +.TP 7 +\ \ \'\fBg\fR\' :\fIChange_Window/Field_Group_Name\fR +You will be prompted for a new name to be applied to the \*(CW. +It does not require that the window name be visible +(the 'l' toggle to be \*O). + +.IP "*" 3 +The \*(CIs shown with an \*(AS have use beyond \*(AM. + \'=', 'A', 'G' are always available + \'a', 'w' act the same when color mapping + + +.\" ---------------------------------------------------------------------- +.SH 5. FILES +.\" ---------------------------------------------------------------------- +.\" ...................................................................... +.SS 5a. SYSTEM Configuration File +The presence of this file will influence which version of the 'help' screen +is shown to an ordinary user. +More importantly, it will limit what ordinary users are allowed +to do when \*(Me is running. +They will not be able to issue the following commands. + k Kill a task + r Renice a task + d or s Change delay/sleep interval + +The system \*(CF is\fB not\fR created by \*(Me. +Rather, you create this file manually and place it in the \fI/etc\fR +directory. +Its name must be 'toprc' and must have no leading '.' (period). +It must have only two lines. + +Here is an example of the contents of\fI /etc/toprc\fR: + s # line 1: 'secure' mode switch + 5.0 # line 2: 'delay'\ \ interval in seconds + +.\" ...................................................................... +.SS 5b. PERSONAL Configuration File +This file is written as '$HOME/.your-name-4-top' + 'rc'. +Use the 'W' \*(CI to create it or update it. + +Here is the general layout: + global # line 1: the program name/alias notation + " # line 2: id,altscr,irixps,delay,curwin + per ea # line a: winname,fieldscur + window # line b: winflags,sortindx,maxtasks + " # line c: summclr,msgsclr,headclr,taskclr + +If the $HOME variable is not present, \*(Me will try to write the +personal \*(CF to the current directory, subject to permissions. + + +.\" ---------------------------------------------------------------------- +.SH 6. STUPID TRICKS Sampler +.\" ---------------------------------------------------------------------- +Many of these 'tricks' work best when you give \*(Me a scheduling boost. +So plan on starting him with a nice value of -10, assuming you've got +the authority. + +.\" ...................................................................... +.SS 6a. Kernel Magic +.\" sorry, just can't help it -- don't ya love the sound of this? +For these stupid tricks, \*(Me needs \*(FM. +.\" ( apparently AM static was a potential concern ) + +.New +The user interface, through prompts and help, intentionally implies +that the delay interval is limited to tenths of a second. +However, you're free to set any desired delay. +If you want to see Linux at his scheduling best, try a delay of .09 +seconds or less. + +For this experiment, under x-windows open an xterm and maximize it. +Then do the following: + . provide a scheduling boost and tiny delay via: + nice -n -10 top -d.09 + . keep sorted column highlighting \*F to minimize + path length + . turn \*O reverse row highlighting for emphasis + . try various sort columns (TIME/MEM work well), + and normal or reverse sorts to bring the most + active processes into view + +What you'll see is a very busy Linux doing what he's always done for you, +but there was no program available to illustrate this. + +.New +Under an xterm using 'white-on-black' colors, try setting \*(Me's task color +to black and be sure that task highlighting is set to bold, not reverse. +Then set the delay interval to around .3 seconds. + +After bringing the most active processes into view, what you'll see are +the ghostly images of just the currently running tasks. + +.New +Delete the existing rcfile, or create a new symlink. +Start this new version then type 'T' (a secret key, \*(Xt 3c. TASK Area +Commands, Sorting) followed by 'W' and 'q'. +Finally, restart the program with -d0 (zero delay). + +Your display will be refreshed at three times the rate of the former \*(Me, +a 300% speed advantage. +As \*(Me climbs the TIME ladder, be as patient as you can while speculating +on whether or not \*(Me will ever reach the \*(Me. + +.\" ...................................................................... +.SS 6b. Bouncing Windows +For these stupid tricks, \*(Me needs \*(AM. + +.New +With 3 or 4 \*(TDs visible, pick any window other than the last +and turn idle processes \*F. +Depending on where you applied 'i', sometimes several \*(TDs are bouncing and +sometimes it's like an accordion, as \*(Me tries his best to allocate space. + +.New +Set each window's summary lines differently: one with no memory; another with +no states; maybe one with nothing at all, just the message line. +Then hold down 'a' or 'w' and watch a variation on bouncing windows \*(EM +hopping windows. + +.New +Display all 4 windows and for each, in turn, set idle processes to \*F. +You've just entered the "extreme bounce" zone. + +.\" ...................................................................... +.SS 6c. The Big Bird Window +This stupid trick also requires \*(AM. + +.New +Display all 4 windows and make sure that 1:Def is the \*(CW. +Then, keep increasing window size until the all the other \*(TDs +are "pushed out of the nest". + +When they've all been displaced, toggle between all visible/invisible windows. +Then ponder this: +.br + is \*(Me fibbing or telling honestly your imposed truth? + + +.\" ---------------------------------------------------------------------- +.SH 7. BUGS +.\" ---------------------------------------------------------------------- +Send bug reports to: + Albert D\. Cahalan, + + +.\" ---------------------------------------------------------------------- +.SH 8. HISTORY Former top +.\" ---------------------------------------------------------------------- +The original top was written by Roger Binns, +based on Branko Lankester's ps program. + +Robert Nation +adapted it for the proc file system. + +Helmut Geyer +added support for configurable fields. + +Plus many other individuals contributed over the years. + + +.\" ---------------------------------------------------------------------- +.SH 9. AUTHOR +.\" ---------------------------------------------------------------------- +This entirely new and enhanced replacement was written by: + Jim / James C. Warner, +.ig + ( as a means to learn Linux, can you believe it? ) + ( & he accidentally learned a little groff, too! ) +.. + +With invaluable help from: + Albert D\. Cahalan, + Craig Small, + +.ig +.rj 2 +.B -*-\fR few though they are, some yet believe\fB -*-\fR +.B -*-\~\~\~\~\~\~\~\fRin-the-\fBart\fR-of-programming\~\~\~\~\~\~\~\fB-*-\fR +.. + +.\" ---------------------------------------------------------------------- +.SH 10. SEE ALSO +.\" ---------------------------------------------------------------------- +.BR free (1), +.BR ps (1), +.BR uptime (1), +.BR atop (1), +.BR slabtop (1), +.BR vmstat (8), +.BR w (1). + + +.\" ---------------------------------------------------------------------- +.ig +.rj 1 +\-*- +.PD +.in -3 +Copyright (c) 2002 \*(EM JC Warner & Associates, Ltd. + +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.1 or +any later version published by the Free Software Foundation; +with no Front-Cover Texts, no Back-Cover Texts, and with the following +Invariant Sections and any sub-sections therein: +.na +.hy 0 +.in +3 +STUPID\ TRICKS\ Sampler; +.br +AUTHOR +.in +A copy of the license is included in the section entitled +\(dqGNU Free Documentation License\(dq. +.. +. +.\" end: active doc |||||||||||||||||||||||||||||||||||||||||||||||||| +.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +.ig +.\" ---------------------------------------------------------------------- +.SH GNU Free Documentation License +Version 1.1, March 2000 + +Copyright (C) 2000 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +.SS 0. PREAMBLE +The purpose of this License is to make a manual, textbook, or other +written document "free" in the sense of freedom: to assure everyone +the effective freedom to copy and redistribute it, with or without +modifying it, either commercially or noncommercially. Secondarily, +this License preserves for the author and publisher a way to get +credit for their work, while not being considered responsible for +modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +.SS 1. APPLICABILITY AND DEFINITIONS +This License applies to any manual or other work that contains a +notice placed by the copyright holder saying it can be distributed +under the terms of this License. The "Document", below, refers to any +such manual or work. Any member of the public is a licensee, and is +addressed as "you". + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (For example, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, whose contents can be viewed and edited directly and +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup has been designed to thwart or discourage +subsequent modification by readers is not Transparent. A copy that is +not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML designed for human modification. Opaque formats include +PostScript, PDF, proprietary formats that can be read and edited only +by proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML produced by some word processors for output +purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +.SS 2. VERBATIM COPYING +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +.SS 3. COPYING IN QUANTITY +If you publish printed copies of the Document numbering more than 100, +and the Document's license notice requires Cover Texts, you must enclose +the copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a publicly-accessible computer-network location containing a complete +Transparent copy of the Document, free of added material, which the +general network-using public has access to download anonymously at no +charge using public-standard network protocols. If you use the latter +option, you must take reasonably prudent steps, when you begin +distribution of Opaque copies in quantity, to ensure that this +Transparent copy will remain thus accessible at the stated location +until at least one year after the last time you distribute an Opaque +copy (directly or through your agents or retailers) of that edition to +the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +.SS 4. MODIFICATIONS +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +.HP 3 +.B A\fR.\ Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions (which should, +if there were any, be listed in the History section of the Document). +You may use the same title as a previous version if the original publisher of +that version gives permission. +.HP 3 +.B B\fR.\ List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified Version, +together with at least five of the principal authors of the Document +(all of its principal authors, if it has less than five). +.HP 3 +.B C\fR.\ State on the Title page the name of the publisher of the Modified +Version, as the publisher. +.HP 3 +.B D\fR.\ Preserve all the copyright notices of the Document. +.HP 3 +.B E\fR.\ Add an appropriate copyright notice for your modifications adjacent +to the other copyright notices. +.HP 3 +.B F\fR.\ Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the terms of +this License, in the form shown in the Addendum below. +.HP 3 +.B G\fR.\ Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. +.HP 3 +.B H\fR.\ Include an unaltered copy of this License. +.HP 3 +.B I\fR.\ Preserve the section entitled "History", and its title, and add to it +an item stating at least the title, year, new authors, and publisher of the +Modified Version as given on the Title Page. +If there is no section entitled "History" in the Document, create one stating +the title, year, authors, and publisher of the Document as given on its Title +Page, then add an item describing the Modified Version as stated in the +previous sentence. +.HP 3 +.B J\fR.\ Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise the network +locations given in the Document for previous versions it was based on. +These may be placed in the "History" section. +You may omit a network location for a work that was published at least four +years before the Document itself, or if the original publisher of the version +it refers to gives permission. +.HP 3 +.B K\fR.\ In any section entitled "Acknowledgements" or "Dedications", preserve +the section's title, and preserve in the section all the substance and tone of +each of the contributor acknowledgements and/or dedications given therein. +.HP 3 +.B L\fR.\ Preserve all the Invariant Sections of the Document, unaltered in their +text and in their titles. +Section numbers or the equivalent are not considered part of the section titles. +.HP 3 +.B M\fR.\ Delete any section entitled "Endorsements". +Such a section may not be included in the Modified Version. +.HP 3 +.B N\fR.\ Do not retitle any existing section as "Endorsements" or to conflict +in title with any Invariant Section. + +.PP +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +.SS 5. COMBINING DOCUMENTS +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections entitled "History" +in the various original documents, forming one section entitled +"History"; likewise combine any sections entitled "Acknowledgements", +and any sections entitled "Dedications". You must delete all sections +entitled "Endorsements." + +.SS 6. COLLECTIONS OF DOCUMENTS +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +.SS 7. AGGREGATION WITH INDEPENDENT WORKS +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, does not as a whole count as a Modified Version +of the Document, provided no compilation copyright is claimed for the +compilation. Such a compilation is called an "aggregate", and this +License does not apply to the other self-contained works thus compiled +with the Document, on account of their being thus compiled, if they +are not themselves derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one quarter +of the entire aggregate, the Document's Cover Texts may be placed on +covers that surround only the Document within the aggregate. +Otherwise they must appear on covers around the whole aggregate. + +.SS 8. TRANSLATION +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License provided that you also include the +original English version of this License. In case of a disagreement +between the translation and the original English version of this +License, the original English version will prevail. + +.SS 9. TERMINATION +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +.SS 10. FUTURE REVISIONS OF THIS LICENSE +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + +.SS ADDENDUM: How to use this License for your documents +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +.IP "" 3 +Copyright (c) YEAR YOUR NAME. + +Permission is granted to copy, distribute and/or modify this document under the +terms of the GNU Free Documentation License, Version 1.1 or any later version +published by the Free Software Foundation;\ \ with the Invariant Sections being +LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the +Back-Cover Texts being LIST. +A copy of the license is included in the section entitled "GNU +Free Documentation License". + +If you have no Invariant Sections, write "with no Invariant Sections" +instead of saying which ones are invariant. If you have no +Front-Cover Texts, write "no Front-Cover Texts" instead of +"Front-Cover Texts being LIST"; likewise for Back-Cover Texts. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +.\" ---------------------------------------------------------------------- +.SH \fRend of\fB GNU Free Documentation License +.IP "" +.PP +.. +.\" end: gfdl license |||||||||||||||||||||||||||||||||||||||||||||||| +.\" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/top.c b/top.c new file mode 100644 index 0000000..6d4a7cc --- /dev/null +++ b/top.c @@ -0,0 +1,3411 @@ +/* top.c - Source file: show Linux processes */ +/* + * Copyright (c) 2002, by: James C. Warner + * All rights reserved. 8921 Hilloway Road + * Eden Prairie, Minnesota 55347 USA + * + * + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * For their contributions to this program, the author wishes to thank: + * Albert D. Cahalan, + * Craig Small, + * + * Changes by Albert Cahalan, 2002-2004. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Foul POS defines all sorts of stuff... +#include +#undef tab + +#include +#include +#include +#include + +#include "proc/devname.h" +#include "proc/wchan.h" +#include "proc/procps.h" +#include "proc/readproc.h" +#include "proc/escape.h" +#include "proc/sig.h" +#include "proc/sysinfo.h" +#include "proc/version.h" +#include "proc/whattime.h" + +#include "top.h" + +/*###### Miscellaneous global stuff ####################################*/ + + /* The original and new terminal attributes */ +static struct termios Savedtty, + Rawtty; +static int Ttychanged = 0; + + /* Program name used in error messages and local 'rc' file name */ +static char *Myname; + + /* Name of user config file (dynamically constructed) and our + 'Current' rcfile contents, initialized with defaults but may be + overridden with the local rcfile (old or new-style) values */ +static char Rc_name [OURPATHSZ]; +static RCF_t Rc = DEF_RCFILE; + + /* The run-time acquired page size */ +static unsigned Page_size; +static unsigned page_to_kb_shift; + + /* SMP Irix/Solaris mode */ +static int Cpu_tot; +static double pcpu_max_value; // usually 99.9, for %CPU display + /* assume no IO-wait stats, overridden if linux 2.5.41 */ +static const char *States_fmts = STATES_line2x4; + + /* Specific process id monitoring support */ +static pid_t Monpids [MONPIDMAX] = { 0 }; +static int Monpidsidx = 0; + + /* A postponed error message */ +static char Msg_delayed [SMLBUFSIZ]; +static int Msg_awaiting = 0; + +// This is the select() timeout. Clear it in sig handlers to avoid a race. +// (signal happens just as we are about to select() and thus does not +// break us out of the select(), causing us to delay until timeout) +static volatile struct timeval tv; +#define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0); + + /* Configurable Display support ##################################*/ + + /* Current screen dimensions. + note: the number of processes displayed is tracked on a per window + basis (see the WIN_t). Max_lines is the total number of + screen rows after deducting summary information overhead. */ + /* Current terminal screen size. */ +static int Screen_cols, Screen_rows, Max_lines; + +// set to 1 if writing to the last column would be troublesome +// (we don't distinguish the lowermost row from the other rows) +static int avoid_last_column; + + /* This is really the number of lines needed to display the summary + information (0 - nn), but is used as the relative row where we + stick the cursor between frames. */ +static int Msg_row; + + /* Global/Non-windows mode stuff that is NOT persistent */ +static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise + PSDBopen = 0, // set to '1' if psdb opened (now postponed) + Batch = 0, // batch mode, collect no input, dumb output + Loops = -1, // number of iterations, -1 loops forever + Secure_mode = 0; // set if some functionality restricted + + /* Some cap's stuff to reduce runtime calls -- + to accomodate 'Batch' mode, they begin life as empty strings */ +static char Cap_clr_eol [CAPBUFSIZ], + Cap_clr_eos [CAPBUFSIZ], + Cap_clr_scr [CAPBUFSIZ], + Cap_rmam [CAPBUFSIZ], + Cap_smam [CAPBUFSIZ], + Cap_curs_norm [CAPBUFSIZ], + Cap_curs_huge [CAPBUFSIZ], + Cap_home [CAPBUFSIZ], + Cap_norm [CAPBUFSIZ], + Cap_reverse [CAPBUFSIZ], + Caps_off [CAPBUFSIZ]; +static int Cap_can_goto = 0; + + /* Some optimization stuff, to reduce output demands... + The Pseudo_ guys are managed by wins_resize and frame_make. They + are exploited in a macro and represent 90% of our optimization. + The Stdout_buf is transparent to our code and regardless of whose + buffer is used, stdout is flushed at frame end or if interactive. */ +static char *Pseudo_scrn; +static int Pseudo_row, Pseudo_cols, Pseudo_size; +#ifndef STDOUT_IOLBF + // less than stdout's normal buffer but with luck mostly '\n' anyway +static char Stdout_buf[2048]; +#endif + + + /* ////////////////////////////////////////////////////////////// */ + /* Special Section: multiple windows/field groups ---------------*/ + + /* The pointers to our four WIN_t's, and which of those is considered + the 'current' window (ie. which window is associated with any summ + info displayed and to which window commands are directed) */ +static WIN_t Winstk [GROUPSMAX], + *Curwin; + + /* Frame oriented stuff that can't remain local to any 1 function + and/or that would be too cumbersome managed as parms, + and/or that are simply more efficiently handled as globals + (first 2 persist beyond a single frame, changed infrequently) */ +static int Frames_libflags; // PROC_FILLxxx flags (0 = need new) +//atic int Frames_maxcmdln; // the largest from the 4 windows +static unsigned Frame_maxtask; // last known number of active tasks + // ie. current 'size' of proc table +static unsigned Frame_running, // state categories for this frame + Frame_sleepin, + Frame_stopped, + Frame_zombied; +static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu' +static int Frame_srtflg, // the subject window's sort direction + Frame_ctimes, // the subject window's ctimes flag + Frame_cmdlin; // the subject window's cmdlin flag + /* ////////////////////////////////////////////////////////////// */ + + +/*###### Sort callbacks ################################################*/ + + /* + * These happen to be coded in the same order as the enum 'pflag' + * values. Note that 2 of these routines serve double duty -- + * 2 columns each. + */ + +SCB_NUMx(P_PID, XXXID) +SCB_NUMx(P_PPD, ppid) +SCB_STRx(P_URR, ruser) +SCB_NUMx(P_UID, euid) +SCB_STRx(P_URE, euser) +SCB_STRx(P_GRP, egroup) +SCB_NUMx(P_TTY, tty) +SCB_NUMx(P_PRI, priority) +SCB_NUMx(P_NCE, nice) +SCB_NUMx(P_CPN, processor) +SCB_NUM1(P_CPU, pcpu) + // also serves P_TM2 ! +static int sort_P_TME (const proc_t **P, const proc_t **Q) +{ + if (Frame_ctimes) { + if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) + < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return SORT_lt; + if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) + > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return SORT_gt; + } else { + if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime)) + return SORT_lt; + if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime)) + return SORT_gt; + } + return SORT_eq; +} + +SCB_NUM1(P_VRT, size) +SCB_NUM2(P_SWP, size, resident) +SCB_NUM1(P_RES, resident) // also serves P_MEM ! +SCB_NUM1(P_COD, trs) +SCB_NUM1(P_DAT, drs) +SCB_NUM1(P_SHR, share) +SCB_NUM1(P_FLT, maj_flt) +SCB_NUM1(P_DRT, dt) +SCB_NUMx(P_STA, state) + +static int sort_P_CMD (const proc_t **P, const proc_t **Q) +{ + /* if a process doesn't have a cmdline, we'll consider it a kernel thread + -- since displayed tasks are given special treatment, we must too */ + if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) { + if (!(*Q)->cmdline) return Frame_srtflg * -1; + if (!(*P)->cmdline) return Frame_srtflg; + return Frame_srtflg * + strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln); + } + // this part also handles the compare if both are kernel threads + return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd); +} + +SCB_NUM1(P_WCH, wchan) +SCB_NUM1(P_FLG, flags) + + /* ///////////////////////////////// special sort for prochlp() ! */ +static int sort_HST_t (const HST_t *P, const HST_t *Q) +{ + return P->pid - Q->pid; +} + + +/*###### Tiny useful routine(s) ########################################*/ + + /* + * This routine isolates ALL user INPUT and ensures that we + * wont be mixing I/O from stdio and low-level read() requests */ +static int chin (int ech, char *buf, unsigned cnt) +{ + int rc; + + fflush(stdout); + if (!ech) + rc = read(STDIN_FILENO, buf, cnt); + else { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + rc = read(STDIN_FILENO, buf, cnt); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); + } + // may be the beginning of a lengthy escape sequence + tcflush(STDIN_FILENO, TCIFLUSH); + return rc; // note: we do NOT produce a vaid 'string' +} + + +// This routine simply formats whatever the caller wants and +// returns a pointer to the resulting 'const char' string... +static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2))); +static const char *fmtmk (const char *fmts, ...) +{ + static char buf[BIGBUFSIZ]; // with help stuff, our buffer + va_list va; // requirements exceed 1k + + va_start(va, fmts); + vsnprintf(buf, sizeof(buf), fmts, va); + va_end(va); + return (const char *)buf; +} + + +// This guy is just our way of avoiding the overhead of the standard +// strcat function (should the caller choose to participate) +static inline char *scat (char *restrict dst, const char *restrict src) +{ + while (*dst) dst++; + while ((*(dst++) = *(src++))); + return --dst; +} + + +// Trim the rc file lines and any 'open_psdb_message' result which arrives +// with an inappropriate newline (thanks to 'sysmap_mmap') +static char *strim_0 (char *str) +{ + static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape + char *p; + + if ((p = strpbrk(str, ws))) *p = 0; + return str; +} + + +// This guy just facilitates Batch and protects against dumb ttys +// -- we'd 'inline' him but he's only called twice per frame, +// yet used in many other locations. +static const char *tg2 (int x, int y) +{ + return Cap_can_goto ? tgoto(cursor_address, x, y) : ""; +} + + +/*###### Exit/Interrput routines #######################################*/ + +// The usual program end -- called only by functions in this section. +static void bye_bye (FILE *fp, int eno, const char *str) NORETURN; +static void bye_bye (FILE *fp, int eno, const char *str) +{ + if (!Batch) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + putp(tg2(0, Screen_rows)); + putp(Cap_curs_norm); + putp(Cap_smam); + putp("\n"); + fflush(stdout); + +//#define ATEOJ_REPORT +#ifdef ATEOJ_REPORT + + fprintf(fp, + "\n\tTerminal: %s" + "\n\t device = %s, ncurses = v%s" + "\n\t max_colors = %d, max_pairs = %d" + "\n\t Cap_can_goto = %s" + "\n\t Screen_cols = %d, Screen_rows = %d" + "\n\t Max_lines = %d, most recent Pseudo_size = %d" + "\n" +#ifdef PRETENDNOCAP + , "dumb" +#else + , termname() +#endif + , ttyname(STDOUT_FILENO), NCURSES_VERSION + , max_colors, max_pairs + , Cap_can_goto ? "yes" : "No!" + , Screen_cols, Screen_rows + , Max_lines, Pseudo_size + ); + + fprintf(fp, +#ifndef STDOUT_IOLBF + "\n\t Stdout_buf = %d, BUFSIZ = %u" +#endif + "\n\tWindows and Curwin->" + "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d" + "\n\t rc.winname = %s, grpname = %s" + "\n\t rc.winflags = %08x, maxpflgs = %d" + "\n\t rc.fieldscur = %s" + "\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d" + "\n\t rc.sortindx = %d" + "\n" +#ifndef STDOUT_IOLBF + , sizeof(Stdout_buf), (unsigned)BUFSIZ +#endif + , sizeof(WIN_t), GROUPSMAX + , Curwin->rc.winname, Curwin->grpname + , Curwin->rc.winflags, Curwin->maxpflgs + , Curwin->rc.fieldscur + , Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln + , Curwin->rc.sortindx + ); + + fprintf(fp, + "\n\tProgram" + "\n\t Linux version = %u.%u.%u, %s" + "\n\t Hertz = %u (%u bytes, %u-bit time)" + "\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u" + "\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)" + "\n" + , LINUX_VERSION_MAJOR(linux_version_code) + , LINUX_VERSION_MINOR(linux_version_code) + , LINUX_VERSION_PATCH(linux_version_code) + , procps_version + , (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8 + , Page_size, Cpu_tot, sizeof(proc_t) + , sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t) + ); + + +#endif + + if (str) fputs(str, fp); + exit(eno); +} + + + /* + * Normal end of execution. + * catches: + * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */ +// FIXME: can't do this shit in a signal handler +static void end_pgm (int sig) NORETURN; +static void end_pgm (int sig) +{ + if(sig) + sig |= 0x80; // for a proper process exit code + bye_bye(stdout, sig, NULL); +} + + + /* + * Standard error handler to normalize the look of all err o/p */ +static void std_err (const char *str) NORETURN; +static void std_err (const char *str) +{ + static char buf[SMLBUFSIZ]; + + fflush(stdout); + /* we'll use our own buffer so callers can still use fmtmk() and, yes the + leading tab is not the standard convention, but the standard is wrong + -- OUR msg won't get lost in screen clutter, like so many others! */ + snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str); + if (!Ttychanged) { + fprintf(stderr, "%s\n", buf); + exit(1); + } + /* not to worry, he'll change our exit code to 1 due to 'buf' */ + bye_bye(stderr, 1, buf); +} + + + /* + * Standard out handler */ +static void std_out (const char *str) NORETURN; +static void std_out (const char *str) +{ + static char buf[SMLBUFSIZ]; + + fflush(stdout); + /* we'll use our own buffer so callers can still use fmtmk() and, yes the + leading tab is not the standard convention, but the standard is wrong + -- OUR msg won't get lost in screen clutter, like so many others! */ + snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str); + if (!Ttychanged) { + fprintf(stdout, "%s\n", buf); + exit(0); + } + bye_bye(stdout, 0, buf); +} + + + /* + * Suspend ourself. + * catches: + * SIGTSTP, SIGTTIN and SIGTTOU */ +// FIXME: can't do this shit in a signal handler! +static void suspend (int dont_care_sig) +{ + (void)dont_care_sig; + /* reset terminal */ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + putp(tg2(0, Screen_rows)); + putp(Cap_curs_norm); + putp(Cap_smam); + putp("\n"); + fflush(stdout); + raise(SIGSTOP); + /* later, after SIGCONT... */ + ZAP_TIMEOUT + if (!Batch) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); + putp(Cap_clr_scr); + putp(Cap_rmam); +} + + +/*###### Misc Color/Display support ####################################*/ + + /* macro to test if a basic (non-color) capability is valid + thanks: Floyd Davidson */ +#define tIF(s) s ? s : "" +#define CAPCOPY(dst,src) src && strcpy(dst,src) + + /* + * Make the appropriate caps/color strings and set some + * lengths which are used to distinguish twix the displayed + * columns and an actual printed row! + * note: we avoid the use of background color so as to maximize + * compatibility with the user's xterm settings */ +static void capsmk (WIN_t *q) +{ + static int capsdone = 0; + + // we must NOT disturb our 'empty' terminfo strings! + if (Batch) return; + + // these are the unchangeable puppies, so we only do 'em once + if (!capsdone) { + CAPCOPY(Cap_clr_eol, clr_eol); + CAPCOPY(Cap_clr_eos, clr_eos); + CAPCOPY(Cap_clr_scr, clear_screen); + + if (!eat_newline_glitch) { // we like the eat_newline_glitch + CAPCOPY(Cap_rmam, exit_am_mode); + CAPCOPY(Cap_smam, enter_am_mode); + if (!*Cap_rmam || !*Cap_smam) { // need both + *Cap_rmam = '\0'; + *Cap_smam = '\0'; + if (auto_right_margin) { + avoid_last_column = 1; + } + } + } + + CAPCOPY(Cap_curs_huge, cursor_visible); + CAPCOPY(Cap_curs_norm, cursor_normal); + CAPCOPY(Cap_home, cursor_home); + CAPCOPY(Cap_norm, exit_attribute_mode); + CAPCOPY(Cap_reverse, enter_reverse_mode); + + snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair)); + if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1; + capsdone = 1; + } + /* the key to NO run-time costs for configurable colors -- we spend a + little time with the user now setting up our terminfo strings, and + the job's done until he/she/it has a change-of-heart */ + strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode)); + if (CHKw(q, Show_COLORS) && max_colors > 0) { + strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr)); + snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s" + , tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse); + snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s" + , tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold); + snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s" + , tparm(set_a_foreground, q->rc.headclr), Cap_reverse); + snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s" + , Caps_off, tparm(set_a_foreground, q->rc.taskclr)); + } else { + q->capclr_sum[0] = '\0'; + strcpy(q->capclr_msg, Cap_reverse); + strcpy(q->capclr_pmt, q->cap_bold); + strcpy(q->capclr_hdr, Cap_reverse); + strcpy(q->capclr_rownorm, Cap_norm); + } + // composite(s), so we do 'em outside and after the if + snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s" + , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse); + q->len_rownorm = strlen(q->capclr_rownorm); + q->len_rowhigh = strlen(q->capclr_rowhigh); + +#undef tIF +} + + +// Show an error, but not right now. +// Due to the postponed opening of ksym, using open_psdb_message, +// if P_WCH had been selected and the program is restarted, the +// message would otherwise be displayed prematurely. +static void msg_save (const char *fmts, ...) __attribute__((format(printf,1,2))); +static void msg_save (const char *fmts, ...) +{ + char tmp[SMLBUFSIZ]; + va_list va; + + va_start(va, fmts); + vsnprintf(tmp, sizeof(tmp), fmts, va); + va_end(va); + /* we'll add some extra attention grabbers to whatever this is */ + snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim_0(tmp)); + Msg_awaiting = 1; +} + + + /* + * Show an error message (caller may include a '\a' for sound) */ +static void show_msg (const char *str) +{ + PUTT("%s%s %s %s%s", + tg2(0, Msg_row), + Curwin->capclr_msg, + str, + Caps_off, + Cap_clr_eol + ); + fflush(stdout); + sleep(MSG_SLEEP); + Msg_awaiting = 0; +} + + + /* + * Show an input prompt + larger cursor */ +static void show_pmt (const char *str) +{ + PUTT("%s%s%s: %s%s", + tg2(0, Msg_row), + Curwin->capclr_pmt, + str, + Cap_curs_huge, + Caps_off + ); + fflush(stdout); +} + + + /* + * Show lines with specially formatted elements, but only output + * what will fit within the current screen width. + * Our special formatting consists of: + * "some text <_delimiter_> some more text <_delimiter_>...\n" + * Where <_delimiter_> is a single byte in the range of: + * \01 through \10 (in decimalizee, 1 - 8) + * and is used to select an 'attribute' from a capabilities table + * which is then applied to the *preceding* substring. + * Once recognized, the delimiter is replaced with a null character + * and viola, we've got a substring ready to output! Strings or + * substrings without delimiters will receive the Cap_norm attribute. + * + * Caution: + * This routine treats all non-delimiter bytes as displayable + * data subject to our screen width marching orders. If callers + * embed non-display data like tabs or terminfo strings in our + * glob, a line will truncate incorrectly at best. Worse case + * would be truncation of an embedded tty escape sequence. + * + * Tabs must always be avoided or our efforts are wasted and + * lines will wrap. To lessen but not eliminate the risk of + * terminfo string truncation, such non-display stuff should + * be placed at the beginning of a "short" line. + * (and as for tabs, gimme 1 more color then no worries, mate) */ +static void show_special (int interact, const char *glob) +{ /* note: the following is for documentation only, + the real captab is now found in a group's WIN_t ! + +------------------------------------------------------+ + | char *captab[] = { : Cap's/Delim's | + | Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 | + | Sum_color, = \03 | + | Msg_color, Pmt_color, = \04, \05 | + | Hdr_color, = \06 | + | Row_color_high, = \07 | + | Row_color_norm }; = \10 [octal!] | + +------------------------------------------------------+ */ + char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ]; + char *rp, *cap, *lin_end, *sub_beg, *sub_end; + int room; + + /* handle multiple lines passed in a bunch */ + while ((lin_end = strchr(glob, '\n'))) { + + /* create a local copy we can extend and otherwise abuse */ + size_t amt = lin_end - glob; + if(amt > sizeof lin - 1) + amt = sizeof lin - 1; // shit happens + memcpy(lin, glob, amt); + /* zero terminate this part and prepare to parse substrings */ + lin[amt] = '\0'; + room = Screen_cols; + sub_beg = sub_end = lin; + *(rp = row) = '\0'; + + while (*sub_beg) { + switch (*sub_end) { + case 0: /* no end delim, captab makes normal */ + *(sub_end + 1) = '\0'; /* extend str end, then fall through */ + case 1 ... 8: + cap = Curwin->captab[(int)*sub_end]; + *sub_end = '\0'; + snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off); + amt = strlen(tmp); + if(rp - row + amt + 1 > sizeof row) + goto overflow; // shit happens + rp = scat(rp, tmp); + room -= (sub_end - sub_beg); + sub_beg = ++sub_end; + break; + default: /* nothin' special, just text */ + ++sub_end; + } + if (unlikely(0 >= room)) break; /* skip substrings that won't fit */ + } +overflow: + if (interact) PUTT("%s%s\n", row, Cap_clr_eol); + else PUFF("%s%s\n", row, Cap_clr_eol); + glob = ++lin_end; /* point to next line (maybe) */ + } /* end: while 'lines' */ + + /* If there's anything left in the glob (by virtue of no trailing '\n'), + it probably means caller wants to retain cursor position on this final + line. That, in turn, means we're interactive and so we'll just do our + 'fit-to-screen' thingy... */ + if (*glob) PUTT("%.*s", Screen_cols, glob); +} + + +/*###### Small Utility routines ########################################*/ + +// Get a string from the user +static char *ask4str (const char *prompt) +{ + static char buf[GETBUFSIZ]; + + show_pmt(prompt); + memset(buf, '\0', sizeof(buf)); + chin(1, buf, sizeof(buf) - 1); + putp(Cap_curs_norm); + return strim_0(buf); +} + + +// Get a float from the user +static float get_float (const char *prompt) +{ + char *line; + float f; + + if (!(*(line = ask4str(prompt)))) return -1; + // note: we're not allowing negative floats + if (strcspn(line, ",.1234567890")) { + show_msg("\aNot valid"); + return -1; + } + sscanf(line, "%f", &f); + return f; +} + + +// Get an integer from the user +static int get_int (const char *prompt) +{ + char *line; + int n; + + if (!(*(line = ask4str(prompt)))) return -1; + // note: we've got to allow negative ints (renice) + if (strcspn(line, "-1234567890")) { + show_msg("\aNot valid"); + return -1; + } + sscanf(line, "%d", &n); + return n; +} + + + /* + * Do some scaling stuff. + * We'll interpret 'num' as one of the following types and + * try to format it to fit 'width'. + * SK_no (0) it's a byte count + * SK_Kb (1) it's kilobytes + * SK_Mb (2) it's megabytes + * SK_Gb (3) it's gigabytes + * SK_Tb (4) it's terabytes */ +static const char *scale_num (unsigned long num, const int width, const unsigned type) +{ + /* kilobytes, megabytes, gigabytes, terabytes, duh! */ + static double scale[] = { 1024.0, 1024.0*1024, 1024.0*1024*1024, 1024.0*1024*1024*1024, 0 }; + /* kilo, mega, giga, tera, none */ +#ifdef CASEUP_SCALE + static char nextup[] = { 'K', 'M', 'G', 'T', 0 }; +#else + static char nextup[] = { 'k', 'm', 'g', 't', 0 }; +#endif + static char buf[TNYBUFSIZ]; + double *dp; + char *up; + + /* try an unscaled version first... */ + if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf; + + /* now try successively higher types until it fits */ + for (up = nextup + type, dp = scale; *dp; ++dp, ++up) { + /* the most accurate version */ + if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up)) + return buf; + /* the integer version */ + if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up)) + return buf; + } + /* well shoot, this outta' fit... */ + return "?"; +} + + + /* + * Do some scaling stuff. + * format 'tics' to fit 'width'. */ +static const char *scale_tics (TIC_t tics, const int width) +{ +#ifdef CASEUP_SCALE +#define HH "%uH" +#define DD "%uD" +#define WW "%uW" +#else +#define HH "%uh" +#define DD "%ud" +#define WW "%uw" +#endif + static char buf[TNYBUFSIZ]; + unsigned long nt; // narrow time, for speed on 32-bit + unsigned cc; // centiseconds + unsigned nn; // multi-purpose whatever + + nt = (tics * 100ull) / Hertz; + cc = nt % 100; // centiseconds past second + nt /= 100; // total seconds + nn = nt % 60; // seconds past the minute + nt /= 60; // total minutes + if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc)) + return buf; + if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn)) + return buf; + nn = nt % 60; // minutes past the hour + nt /= 60; // total hours + if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn)) + return buf; + nn = nt; // now also hours + if (width >= snprintf(buf, sizeof buf, HH, nn)) + return buf; + nn /= 24; // now days + if (width >= snprintf(buf, sizeof buf, DD, nn)) + return buf; + nn /= 7; // now weeks + if (width >= snprintf(buf, sizeof buf, WW, nn)) + return buf; + // well shoot, this outta' fit... + return "?"; + +#undef HH +#undef DD +#undef WW +} + +#include + +static int selection_type; +static uid_t selection_uid; + +// FIXME: this is "temporary" code we hope +static int good_uid(const proc_t *restrict const pp){ + switch(selection_type){ + case 'p': + return 1; + case 0: + return 1; + case 'U': + if (pp->ruid == selection_uid) return 1; + if (pp->suid == selection_uid) return 1; + if (pp->fuid == selection_uid) return 1; + // FALLTHROUGH + case 'u': + if (pp->euid == selection_uid) return 1; + // FALLTHROUGH + default: + ; // don't know what it is; find bugs fast + } + return 0; +} + +// swiped from ps, and ought to be in libproc +static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){ + struct passwd *passwd_data; + char *endp; + unsigned long num; + static const char uidrange[] = "User ID out of range."; + static const char uidexist[] = "User name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + passwd_data = getpwnam(str); + if(!passwd_data) return uidexist; + num = passwd_data->pw_uid; + } + if(num > 0xfffffffeUL) return uidrange; + *ret = num; + return 0; +} + + +/*###### Library Alternatives ##########################################*/ + + /* + * Handle our own memory stuff without the risk of leaving the + * user's terminal in an ugly state should things go sour. */ + +static void *alloc_c (unsigned numb) MALLOC; +static void *alloc_c (unsigned numb) +{ + void * p; + + if (!numb) ++numb; + if (!(p = calloc(1, numb))) + std_err("failed memory allocate"); + return p; +} + +static void *alloc_r (void *q, unsigned numb) MALLOC; +static void *alloc_r (void *q, unsigned numb) +{ + void *p; + + if (!numb) ++numb; + if (!(p = realloc(q, numb))) + std_err("failed memory allocate"); + return p; +} + + + /* + * This guy's modeled on libproc's 'five_cpu_numbers' function except + * we preserve all cpu data in our CPU_t array which is organized + * as follows: + * cpus[0] thru cpus[n] == tics for each separate cpu + * cpus[Cpu_tot] == tics from the 1st /proc/stat line */ +static CPU_t *cpus_refresh (CPU_t *cpus) +{ + static FILE *fp = NULL; + int i; + int num; + // enough for a /proc/stat CPU line (not the intr line) + char buf[SMLBUFSIZ]; + + /* by opening this file once, we'll avoid the hit on minor page faults + (sorry Linux, but you'll have to close it for us) */ + if (!fp) { + if (!(fp = fopen("/proc/stat", "r"))) + std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno))); + /* note: we allocate one more CPU_t than Cpu_tot so that the last slot + can hold tics representing the /proc/stat cpu summary (the first + line read) -- that slot supports our View_CPUSUM toggle */ + cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t)); + } + rewind(fp); + fflush(fp); + + // first value the last slot with the cpu summary line + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); + cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number + cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number + cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number + num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", + &cpus[Cpu_tot].u, + &cpus[Cpu_tot].n, + &cpus[Cpu_tot].s, + &cpus[Cpu_tot].i, + &cpus[Cpu_tot].w, + &cpus[Cpu_tot].x, + &cpus[Cpu_tot].y, + &cpus[Cpu_tot].z + ); + if (num < 4) + std_err("failed /proc/stat read"); + + // and just in case we're 2.2.xx compiled without SMP support... + if (Cpu_tot == 1) { + cpus[1].id = 0; + memcpy(cpus, &cpus[1], sizeof(CPU_t)); + } + + // now value each separate cpu's tics + for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) { + if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read"); + cpus[i].x = 0; // FIXME: can't tell by kernel version number + cpus[i].y = 0; // FIXME: can't tell by kernel version number + cpus[i].z = 0; // FIXME: can't tell by kernel version number + num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", + &cpus[i].id, + &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z + ); + if (num < 4) + std_err("failed /proc/stat read"); + } + return cpus; +} + + + /* + * Refresh procs *Helper* function to eliminate yet one more need + * to loop through our darn proc_t table. He's responsible for: + * 1) calculating the elapsed time since the previous frame + * 2) counting the number of tasks in each state (run, sleep, etc) + * 3) maintaining the HST_t's and priming the proc_t pcpu field + * 4) establishing the total number tasks for this frame */ +static void prochlp (proc_t *this) +{ + static HST_t *hist_sav = NULL; + static HST_t *hist_new = NULL; + static unsigned hist_siz = 0; // number of structs + static unsigned maxt_sav; // prior frame's max tasks + TIC_t tics; + + if (unlikely(!this)) { + static struct timeval oldtimev; + struct timeval timev; + struct timezone timez; + HST_t *hist_tmp; + float et; + + gettimeofday(&timev, &timez); + et = (timev.tv_sec - oldtimev.tv_sec) + + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0; + oldtimev.tv_sec = timev.tv_sec; + oldtimev.tv_usec = timev.tv_usec; + + // if in Solaris mode, adjust our scaling for all cpus + Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot)); + maxt_sav = Frame_maxtask; + Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0; + + // reuse memory each time around + hist_tmp = hist_sav; + hist_sav = hist_new; + hist_new = hist_tmp; + // prep for our binary search by sorting the last frame's HST_t's + qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t); + return; + } + + switch (this->state) { + case 'R': + Frame_running++; + break; + case 'S': + case 'D': + Frame_sleepin++; + break; + case 'T': + Frame_stopped++; + break; + case 'Z': + Frame_zombied++; + break; + } + + if (unlikely(Frame_maxtask+1 >= hist_siz)) { + hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25% + hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz); + hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz); + } + /* calculate time in this process; the sum of user time (utime) and + system time (stime) -- but PLEASE dont waste time and effort on + calcs and saves that go unused, like the old top! */ + hist_new[Frame_maxtask].pid = this->tid; + hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime); + +#if 0 +{ int i; + int lo = 0; + int hi = maxt_sav - 1; + + // find matching entry from previous frame and make ticks elapsed + while (lo <= hi) { + i = (lo + hi) / 2; + if (this->tid < hist_sav[i].pid) + hi = i - 1; + else if (likely(this->tid > hist_sav[i].pid)) + lo = i + 1; + else { + tics -= hist_sav[i].tics; + break; + } + } +} +#else +{ + HST_t tmp; + const HST_t *ptr; + tmp.pid = this->tid; + ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t); + if(ptr) tics -= ptr->tics; +} +#endif + + // we're just saving elapsed tics, to be converted into %cpu if + // this task wins it's displayable screen row lottery... */ + this->pcpu = tics; +// if (Frames_maxcmdln) { } + // shout this to the world with the final call (or us the next time in) + Frame_maxtask++; +} + + + /* + * This guy's modeled on libproc's 'readproctab' function except + * we reuse and extend any prior proc_t's. He's been customized + * for our specific needs and to avoid the use of */ +static proc_t **procs_refresh (proc_t **table, int flags) +{ +#define PTRsz sizeof(proc_t *) +#define ENTsz sizeof(proc_t) + static unsigned savmax = 0; // first time, Bypass: (i) + proc_t *ptsk = (proc_t *)-1; // first time, Force: (ii) + unsigned curmax = 0; // every time (jeeze) + PROCTAB* PT; + static int show_threads_was_enabled = 0; // optimization + + prochlp(NULL); // prep for a new frame + if (Monpidsidx) + PT = openproc(flags, Monpids); + else + PT = openproc(flags); + + // i) Allocated Chunks: *Existing* table; refresh + reuse + if (!(CHKw(Curwin, Show_THREADS))) { + while (curmax < savmax) { + if (table[curmax]->cmdline) { + unsigned idx; + // Skip if Show_THREADS was never enabled + if (show_threads_was_enabled) { + for (idx = curmax + 1; idx < savmax; idx++) { + if (table[idx]->cmdline == table[curmax]->cmdline) + table[idx]->cmdline = NULL; + } + } + free(*table[curmax]->cmdline); + table[curmax]->cmdline = NULL; + } + if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break; + prochlp(ptsk); // tally & complete this proc_t + ++curmax; + } + } + else { // show each thread in a process separately + while (curmax < savmax) { + proc_t *ttsk; + if (unlikely(!(ptsk = readproc(PT, NULL)))) break; + show_threads_was_enabled = 1; + while (curmax < savmax) { + unsigned idx; + if (table[curmax]->cmdline) { + // threads share the same cmdline storage. 'table' is + // qsort()ed, so must look through the rest of the table. + for (idx = curmax + 1; idx < savmax; idx++) { + if (table[idx]->cmdline == table[curmax]->cmdline) + table[idx]->cmdline = NULL; + } + free(*table[curmax]->cmdline); // only free once + table[curmax]->cmdline = NULL; + } + if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break; + prochlp(ttsk); + ++curmax; + } + free(ptsk); // readproc() proc_t not used + } + } + + // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill + if (!(CHKw(Curwin, Show_THREADS))) { + while (ptsk) { + // realloc as we go, keeping 'table' ahead of 'currmax++' + table = alloc_r(table, (curmax + 1) * PTRsz); + // here, readproc will allocate the underlying proc_t stg + if (likely(ptsk = readproc(PT, NULL))) { + prochlp(ptsk); // tally & complete this proc_t + table[curmax++] = ptsk; + } + } + } + else { // show each thread in a process separately + while (ptsk) { + proc_t *ttsk; + if (likely(ptsk = readproc(PT, NULL))) { + show_threads_was_enabled = 1; + while (1) { + table = alloc_r(table, (curmax + 1) * PTRsz); + if (!(ttsk = readtask(PT, ptsk, NULL))) break; + prochlp(ttsk); + table[curmax++] = ttsk; + } + free(ptsk); // readproc() proc_t not used + } + } + } + closeproc(PT); + + // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists + if (curmax >= savmax) { + table = alloc_r(table, (curmax + 1) * PTRsz); + // here, we must allocate the underlying proc_t stg ourselves + table[curmax] = alloc_c(ENTsz); + savmax = curmax + 1; + } + // this frame's end, but not necessarily end of allocated space + table[curmax]->tid = -1; + return table; + +#undef PTRsz +#undef ENTsz +} + +/*###### Field Table/RCfile compatability support ######################*/ + +// from either 'stat' or 'status' (preferred), via bits not otherwise used +#define L_EITHER PROC_SPARE_1 +// These are the Fieldstab.lflg values used here and in reframewins. +// (own identifiers as documentation and protection against changes) +#define L_stat PROC_FILLSTAT +#define L_statm PROC_FILLMEM +#define L_status PROC_FILLSTATUS +#define L_CMDLINE L_EITHER | PROC_FILLARG +#define L_EUSER PROC_FILLUSR +#define L_RUSER L_status | PROC_FILLUSR +#define L_GROUP L_status | PROC_FILLGRP +#define L_NONE 0 +// for reframewins and summary_show 1st pass +#define L_DEFAULT PROC_FILLSTAT + +// a temporary macro, soon to be undef'd... +#define SF(f) (QFP_t)sort_P_ ## f + + /* These are our gosh darn 'Fields' ! + They MUST be kept in sync with pflags !! + note: for integer data, the length modifiers found in .fmts may + NOT reflect the true field type found in proc_t -- this plus + a cast when/if displayed provides minimal width protection. */ +static FLD_t Fieldstab[] = { +/* .lflg anomolies: + P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid) + P_CPU, L_stat - never filled by libproc, but requires times (pcpu) + P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline) + L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit ! + keys head fmts width scale sort desc lflg + ------ ----------- ------- ------ ----- ----- ---------------------- -------- */ + { "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE }, + { "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER }, + { "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER }, + { "DdCc", " UID", " %4u", -1, -1, SF(UID), "User Id", L_NONE }, + { "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER }, + { "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP }, + { "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat }, + { "HhHh", " PR", " %3d", -1, -1, SF(PRI), "Priority", L_stat }, + { "IiIi", " NI", " %3d", -1, -1, SF(NCE), "Nice value", L_stat }, + { "JjYy", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat }, + { "KkEe", " %CPU", " %#4.1f", -1, -1, SF(CPU), "CPU usage", L_stat }, + { "LlWw", " TIME", " %6.6s", 6, -1, SF(TME), "CPU Time", L_stat }, + { "MmRr", " TIME+ ", " %9.9s", 9, -1, SF(TME), "CPU Time, hundredths", L_stat }, + { "NnFf", " %MEM", " %#4.1f", -1, -1, SF(RES), "Memory usage (RES)", L_statm }, + { "OoMm", " VIRT", " %5.5s", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", L_statm }, + { "PpOo", " SWAP", " %4.4s", 4, SK_Kb, SF(SWP), "Swapped size (kb)", L_statm }, + { "QqTt", " RES", " %4.4s", 4, SK_Kb, SF(RES), "Resident size (kb)", L_statm }, + { "RrKk", " CODE", " %4.4s", 4, SK_Kb, SF(COD), "Code size (kb)", L_statm }, + { "SsLl", " DATA", " %4.4s", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm }, + { "TtPp", " SHR", " %4.4s", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm }, + { "UuJj", " nFLT", " %4.4s", 4, SK_no, SF(FLT), "Page Fault count", L_stat }, + { "VvSs", " nDRT", " %4.4s", 4, SK_no, SF(DRT), "Dirty Pages count", L_statm }, + { "WwVv", " S", " %c", -1, -1, SF(STA), "Process Status", L_EITHER }, + // next entry's special: '.head' will be formatted using table entry's own + // '.fmts' plus runtime supplied conversion args! + { "XxXx", " COMMAND", " %-*.*s", -1, -1, SF(CMD), "Command name/line", L_EITHER }, + { "YyUu", " WCHAN ", " %-9.9s", -1, -1, SF(WCH), "Sleeping in Function", L_stat }, + // next entry's special: the 0's will be replaced with '.'! + { "ZzZz", " Flags ", " %08lx", -1, -1, SF(FLG), "Task Flags ", L_stat }, +#if 0 + { "..Qq", " A", " %4.4s", 4, SK_no, SF(PID), "Accessed Page count", L_stat }, + { "..Nn", " TRS", " %4.4s", 4, SK_Kb, SF(PID), "Code in memory (kb)", L_stat }, + { "..Rr", " WP", " %4.4s", 4, SK_no, SF(PID), "Unwritable Pages", L_stat }, + { "Jj[{", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat }, + { "..\\|"," Bad", " %2u", -1, -1, SF(CPN), "-- must ignore | --", 0 }, + { "..]}", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 }, + { "..^~", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 }, +#endif +}; +#undef SF + + + /* All right, those-that-follow -- Listen Up! + * For the above table keys and the following present/future rc file + * compatibility support, you have Mr. Albert D. Cahalan to thank. + * He must have been in a 'Christmas spirit'. Were it left to me, + * this top would never have gotten that close to the former top's + * crufty rcfile. Not only is it illogical, it's odoriferous ! + */ + + // used as 'to' and/or 'from' args in the ft_xxx utilities... +#define FT_NEW_fmt 0 +#define FT_OLD_fmt 2 + + +#if 0 + // convert, or 0 for failure +static int ft_cvt_char (const int fr, const int to, int c) { + int j = -1; + + while (++j < MAXTBL(Fieldstab)) { + if (c == Fieldstab[j].keys[fr]) return Fieldstab[j].keys[to]; + if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1]; + } + return 0; +} +#endif + + + // convert +static inline int ft_get_char (const int fr, int i) { + int c; + if (i < 0) return 0; + if (i >= MAXTBL(Fieldstab)) return 0; + c = Fieldstab[i].keys[fr]; + if (c == '.') c = 0; // '.' marks a bad entry + return c; +} + + +#if 0 + // convert, or -1 for failure +static int ft_get_idx (const int fr, int c) { + int j = -1; + + while (++j < MAXTBL(Fieldstab)) { + if (c == Fieldstab[j].keys[fr]) return j; + if (c == Fieldstab[j].keys[fr+1]) return j; + } + return -1; +} +#endif + + + // convert, or NULL for failure +static const FLD_t *ft_get_ptr (const int fr, int c) { + int j = -1; + + while (++j < MAXTBL(Fieldstab)) { + if (c == Fieldstab[j].keys[fr]) return Fieldstab+j; + if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j; + } + return NULL; +} + + +#if 0 + // convert, or NULL for failure +static const FLD_t *ft_idx_to_ptr (const int i) { + if (i < 0) return NULL; + if (i >= MAXTBL(Fieldstab)) return NULL; + return Fieldstab + i; +} + + + // convert, or -1 for failure +static int ft_ptr_to_idx (const FLD_t *p) { + int i; + if (p < Fieldstab) return -1; + i = p - Fieldstab; + if (i >= MAXTBL(Fieldstab)) return -1; + return i; +} +#endif + + +#if 0 +static void rc_bugless (const RCF_t *const rc) { + const RCW_t *w; + int i = 0; + + fprintf(stderr,"\n%d %d %f %d\n", + rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index + ); + while(i < 4) { + w = &rc->win[i++]; + fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n", + w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks, + w->summclr, w->msgsclr, w->headclr, w->taskclr + ); + } +} +#endif + + +// '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window. +// line 1: an eyecatcher, with a shameless advertisement +// line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin. +// For each of the 4 windows: +// line a: contains winname, fieldscur +// line b: contains winflags, sortindx, maxtasks +// line c: contains summclr, msgsclr, headclr, taskclr +// line d: if present, would crash procps-3.1.1 +static int rc_read_new (const char *const buf, RCF_t *rc) { + int i; + int cnt; + const char *cp; + + cp = strstr(buf, "\n\n" RCF_EYECATCHER); + if (!cp) return -1; + cp = strchr(cp + 2, '\n'); + if (!cp++) return -2; + + cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n", + &rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index + ); + if (cnt != 4) return -3; + cp = strchr(cp, '\n'); + if (!cp++) return -4; + + for (i = 0; i < GROUPSMAX; i++) { + RCW_t *ptr = &rc->win[i]; + cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur); + if (cnt != 2) return 5+100*i; // OK to have less than 4 windows + if (WINNAMSIZ <= strlen(ptr->winname)) return -6; + if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7; + cp = strchr(cp, '\n'); + if (!cp++) return -(8+100*i); + + cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n", + &ptr->winflags, &ptr->sortindx, &ptr->maxtasks + ); + if (cnt != 3) return -(9+100*i); + cp = strchr(cp, '\n'); + if (!cp++) return -(10+100*i); + + cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n", + &ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr + ); + if (cnt != 4) return -(11+100*i); + cp = strchr(cp, '\n'); + if (!cp++) return -(12+100*i); + while (*cp == '\t') { // skip unknown per-window settings + cp = strchr(cp, '\n'); + if (!cp++) return -(13+100*i); + } + } + return 13; +} + + + +static int rc_read_old (const char *const buf, RCF_t *rc) { + const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......"; + const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr"; + unsigned u; + const char *cp; + unsigned c_show = 0; + int badchar = 0; // allow a limited number of duplicates and junk + + char scoreboard[256]; + memset(scoreboard, '\0', sizeof scoreboard); + + cp = buf+2; // skip the "\n\n" we stuck at the beginning + u = 0; + for (;;) { + const char *tmp; + int c = *cp++; + if (u+1 >= sizeof rc->win[0].fieldscur) return -1; + if (c == '\0') return -2; + if (c == '\n') break; + if (c & ~0x7f) return -3; + if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden + if (scoreboard[c|0xe0u]) badchar++; // duplicates not allowed + scoreboard[c|0xe0u]++; + tmp = strchr(old,c); + if (!tmp) continue; + c = *((tmp-old)+std); + if (c == '.') continue; + if (scoreboard[c&0x1fu]) badchar++; // duplicates not allowed + scoreboard[c&0x1fu]++; + rc->win[0].fieldscur[u++] = c; + } + rc->win[0].fieldscur[u++] = '\0'; + if (u < 21) return -6; // catch junk, not good files (had 23 chars in one) + if (u > 33) return -7; // catch junk, not good files (had 29 chars in one) +// fprintf(stderr, "badchar: %d\n", badchar); sleep(2); + if (badchar > 8) return -8; // too much junk + if (!c_show) return -9; // nothing was shown + + // rest of file is optional, but better look right if it exists + if (!*cp) return 12; + if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid? + rc->delay_time = *cp - '0'; + + memset(scoreboard, '\0', sizeof(scoreboard)); + for (;;) { + int c = *++cp & 0xffu; // protect scoreboard[] from negative char + if (!c) return -14; // not OK to hit EOL w/o '\n' + if (c == '\n') break; + switch (c) { + case ' ': + case '.': + case '0' ... '9': + return -15; // not supposed to have digits here + +// case 's': // mostly for global rcfile +// rc->mode_secure = 1; +// break; + case 'S': + rc->win[0].winflags |= Show_CTIMES; + break; + case 'c': + rc->win[0].winflags |= Show_CMDLIN; + break; + case 'i': + rc->win[0].winflags &= ~Show_IDLEPS; + break; + case 'H': + rc->win[0].winflags |= Show_THREADS; + break; + case 'm': + rc->win[0].winflags &= ~View_MEMORY; + break; + case 'l': + rc->win[0].winflags &= ~View_LOADAV; + break; + case 't': + rc->win[0].winflags &= ~View_STATES; + break; + case 'I': + rc->mode_irixps = 0; + break; + + case 'M': + c = 0; // for scoreboard + rc->win[0].sortindx = P_MEM; + break; + case 'P': + c = 0; // for scoreboard + rc->win[0].sortindx = P_CPU; + break; + case 'A': // supposed to be start_time + c = 0; // for scoreboard + rc->win[0].sortindx = P_PID; + break; + case 'T': + c = 0; // for scoreboard + rc->win[0].sortindx = P_TM2; + break; + case 'N': + c = 0; // for scoreboard + rc->win[0].sortindx = P_PID; + break; + + default: + // just ignore it, except for the scoreboard of course + break; + } + if (scoreboard[c]) return -16; // duplicates not allowed + scoreboard[c] = 1; + } + return 17; +} + + +static void rc_write_new (FILE *fp) { + int i; + + fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n", + Myname + ); + fprintf(fp, RCF_DEPRECATED + "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n", + Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk) + ); + for (i = 0; i < GROUPSMAX; i++) { + char buf[40]; + char *cp = Winstk[i].rc.fieldscur; + int j = 0; + + while (j < 36) { + int c = *cp++ & 0xff; + switch (c) { + case '.': + case 1 ... ' ': + case 0x7f ... 0xff: + continue; // throw away junk (some of it) + default: + buf[j++] = c; // gets the '\0' too + } + if (!c) break; + } + fprintf(fp, "%s\tfieldscur=%s\n", + Winstk[i].rc.winname, buf + ); + fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n", + Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks + ); + fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n", + Winstk[i].rc.summclr, Winstk[i].rc.msgsclr, + Winstk[i].rc.headclr, Winstk[i].rc.taskclr + ); + } +} + + +static const char *rc_write_whatever (void) { + FILE *fp = fopen(Rc_name, "w"); + + if (!fp) return strerror(errno); + rc_write_new(fp); + fclose(fp); + return NULL; +} + + +/*###### Startup routines ##############################################*/ + +// No mater what *they* say, we handle the really really BIG and +// IMPORTANT stuff upon which all those lessor functions depend! +static void before (char *me) +{ + int i; + + /* setup our program name -- big! */ + Myname = strrchr(me, '/'); + if (Myname) ++Myname; else Myname = me; + + /* establish cpu particulars -- even bigger! */ + Cpu_tot = smp_num_cpus; + if (linux_version_code > LINUX_VERSION(2, 5, 41)) + States_fmts = STATES_line2x5; + if (linux_version_code >= LINUX_VERSION(2, 6, 0)) // grrr... only some 2.6.0-testX :-( + States_fmts = STATES_line2x6; + if (linux_version_code >= LINUX_VERSION(2, 6, 11)) + States_fmts = STATES_line2x7; + + /* get virtual page size -- nearing huge! */ + Page_size = getpagesize(); + i = Page_size; + while(i>1024){ + i >>= 1; + page_to_kb_shift++; + } + + pcpu_max_value = 99.9; + + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %1u"; + if(smp_num_cpus>9){ + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %2u"; + } + if(smp_num_cpus>99){ + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %3u"; + } + if(smp_num_cpus>999){ + Fieldstab[P_CPN].head = " P"; + Fieldstab[P_CPN].fmts = " %4u"; + } + + { + static char pid_fmt[6]; + unsigned pid_digits = get_pid_digits(); + if(pid_digits<4) pid_digits=4; + snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits); + Fieldstab[P_PID].fmts = pid_fmt; + Fieldstab[P_PID].head = " PID" + 10 - pid_digits; + Fieldstab[P_PPD].fmts = pid_fmt; + Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits; + } +} + + +// Config file read *helper* function. +// Anything missing won't show as a choice in the field editor, +// so make sure there is exactly one of each letter. +// +// Due to Rik blindly accepting damem's broken patches, procps-2.0.1x +// has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too. +static void confighlp (char *fields) { + unsigned upper[PFLAGSSIZ]; + unsigned lower[PFLAGSSIZ]; + char c; + char *cp; + + memset(upper, '\0', sizeof upper); + memset(lower, '\0', sizeof lower); + + cp = fields; + for (;;) { + c = *cp++; + if (!c) break; + if(isupper(c)) upper[c&0x1f]++; + else lower[c&0x1f]++; + } + + c = 'a'; + while (c <= 'z') { + if (upper[c&0x1f] && lower[c&0x1f]) { + lower[c&0x1f] = 0; // got both, so wipe out unseen column + for (;;) { + cp = strchr(fields, c); + if (cp) memmove(cp, cp+1, strlen(cp)); + else break; + } + } + while (lower[c&0x1f] > 1) { // got too many a..z + lower[c&0x1f]--; + cp = strchr(fields, c); + memmove(cp, cp+1, strlen(cp)); + } + while (upper[c&0x1f] > 1) { // got too many A..Z + upper[c&0x1f]--; + cp = strchr(fields, toupper(c)); + memmove(cp, cp+1, strlen(cp)); + } + if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing + lower[c&0x1f]++; + memmove(fields+1, fields, strlen(fields)+1); + fields[0] = c; + } + c++; + } +} + + +// First attempt to read the /etc/rcfile which contains two lines +// consisting of the secure mode switch and an update interval. +// It's presence limits what ordinary users are allowed to do. +// (it's actually an old-style config file) +// +// Then build the local rcfile name and try to read a crufty old-top +// rcfile (whew, odoriferous), which may contain an embedded new-style +// rcfile. Whether embedded or standalone, new-style rcfile values +// will always override that crufty stuff! +// note: If running in secure mode via the /etc/rcfile, +// Delay_time will be ignored except for root. +static void configs_read (void) +{ + const RCF_t def_rcf = DEF_RCFILE; + char fbuf[MEDBUFSIZ]; + int i, fd; + RCF_t rcf; + float delay = Rc.delay_time; + + // read part of an old-style config in /etc/toprc + fd = open(SYS_RCFILESPEC, O_RDONLY); + if (fd > 0) { + ssize_t num; + num = read(fd, fbuf, sizeof(fbuf) - 1); + if (num > 0) { + const char *sec = strchr(fbuf, 's'); + const char *eol = strchr(fbuf, '\n'); + if (eol) { + const char *two = eol + 1; // line two + if (sec < eol) Secure_mode = !!sec; + eol = strchr(two, '\n'); + if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two); + } + } + close(fd); + } + + if (getenv("TOPRC")) { // should switch on Myname before documenting this? + // not the most optimal here... + snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC")); + } else { + snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew... + if (getenv("HOME")) + snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname); + } + + rcf = def_rcf; + fd = open(Rc_name, O_RDONLY); + if (fd > 0) { + ssize_t num; + num = read(fd, fbuf+2, sizeof(fbuf) -3); + if (num > 0) { + fbuf[0] = '\n'; + fbuf[1] = '\n'; + fbuf[num+2] = '\0'; +//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf)); +//sleep(2); + if (rc_read_new(fbuf, &rcf) < 0) { + rcf = def_rcf; // on failure, maybe mangled + if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf; + } + delay = rcf.delay_time; + } + close(fd); + } + + // update Rc defaults, establish a Curwin and fix up the window stack + Rc.mode_altscr = rcf.mode_altscr; + Rc.mode_irixps = rcf.mode_irixps; + if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0; + Curwin = &Winstk[rcf.win_index]; + for (i = 0; i < GROUPSMAX; i++) { + memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]); + confighlp(Winstk[i].rc.fieldscur); + } + + if(Rc.mode_irixps && smp_num_cpus>1){ + // good for 100 CPUs per process + pcpu_max_value = 9999.0; + Fieldstab[P_CPU].fmts = " %4.0f"; + } + + // lastly, establish the true runtime secure mode and delay time + if (!getuid()) Secure_mode = 0; + if (!Secure_mode) Rc.delay_time = delay; +} + + +// Parse command line arguments. +// Note: it's assumed that the rc file(s) have already been read +// and our job is to see if any of those options are to be +// overridden -- we'll force some on and negate others in our +// best effort to honor the loser's (oops, user's) wishes... +static void parse_args (char **args) +{ + /* differences between us and the former top: + -C (separate CPU states for SMP) is left to an rcfile + -p (pid monitoring) allows a comma delimited list + -q (zero delay) eliminated as redundant, incomplete and inappropriate + use: "nice -n-10 top -d0" to achieve what was only claimed + -c,i,S act as toggles (not 'on' switches) for enhanced user flexibility + . no deprecated/illegal use of 'breakargv:' with goto + . bunched args are actually handled properly and none are ignored + . we tolerate NO whitespace and NO switches -- maybe too tolerant? */ + static const char usage[] = + " -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]"; + float tmp_delay = MAXFLOAT; + char *p; + + while (*args) { + const char *cp = *(args++); + + while (*cp) { + switch (*cp) { + case '\0': + case '-': + break; + case 'b': + Batch = 1; + break; + case 'c': + TOGw(Curwin, Show_CMDLIN); + break; + case 'd': + if (cp[1]) ++cp; + else if (*args) cp = *args++; + else std_err("-d requires argument"); + /* a negative delay will be dealt with shortly... */ + if (sscanf(cp, "%f", &tmp_delay) != 1) + std_err(fmtmk("bad delay '%s'", cp)); + break; + case 'H': + TOGw(Curwin, Show_THREADS); + break; + case 'h': + case 'v': case 'V': + std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage)); + case 'i': + TOGw(Curwin, Show_IDLEPS); + Curwin->rc.maxtasks = 0; + break; + case 'n': + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-n requires argument"); + if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1) + std_err(fmtmk("bad iterations arg '%s'", cp)); + break; + case 'p': + do { + if (selection_type && selection_type != 'p') std_err("conflicting process selection"); + selection_type = 'p'; + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-p argument missing"); + if (Monpidsidx >= MONPIDMAX) + std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX)); + if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0) + std_err(fmtmk("bad pid '%s'", cp)); + if (!Monpids[Monpidsidx]) + Monpids[Monpidsidx] = getpid(); + Monpidsidx++; + if (!(p = strchr(cp, ','))) + break; + cp = p; + } while (*cp); + break; + case 's': + Secure_mode = 1; + break; + case 'S': + TOGw(Curwin, Show_CTIMES); + break; + case 'u': + do { + const char *errmsg; + if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection"); + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-u missing name"); + errmsg = parse_uid(cp, &selection_uid); + if (errmsg) std_err(errmsg); + selection_type = 'u'; + cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk + } while(0); + break; + case 'U': + do { + const char *errmsg; + if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection"); + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-u missing name"); + errmsg = parse_uid(cp, &selection_uid); + if (errmsg) std_err(errmsg); + selection_type = 'U'; + cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk + } while(0); + break; + default : + std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s" + , *cp, Myname, usage)); + + } /* end: switch (*cp) */ + + /* advance cp and jump over any numerical args used above */ + if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1; + } /* end: while (*cp) */ + } /* end: while (*args) */ + + /* fixup delay time, maybe... */ + if (MAXFLOAT != tmp_delay) { + if (Secure_mode || tmp_delay < 0) + msg_save("Delay time Not changed"); + else + Rc.delay_time = tmp_delay; + } +} + + + /* + * Set up the terminal attributes */ +static void whack_terminal (void) +{ + struct termios newtty; + + if (Batch) { + setupterm("dumb", STDOUT_FILENO, NULL); + return; + } + setupterm(NULL, STDOUT_FILENO, NULL); + if (tcgetattr(STDIN_FILENO, &Savedtty) == -1) + std_err("failed tty get"); + newtty = Savedtty; + newtty.c_lflag &= ~(ICANON | ECHO); + newtty.c_oflag &= ~(TAB3); + newtty.c_cc[VMIN] = 1; + newtty.c_cc[VTIME] = 0; + + Ttychanged = 1; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) { + putp(Cap_clr_scr); + std_err(fmtmk("failed tty set: %s", strerror(errno))); + } + tcgetattr(STDIN_FILENO, &Rawtty); +#ifndef STDOUT_IOLBF + // thanks anyway stdio, but we'll manage buffering at the frame level... + setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf)); +#endif + putp(Cap_clr_scr); + fflush(stdout); +} + + +/*###### Field Selection/Ordering routines #############################*/ + + +// Display each field represented in the Fields Table along with its +// description and mark (with a leading asterisk) fields associated +// with upper case letter(s) in the passed 'fields string'. +// +// After all fields have been displayed, some extra explanatory +// text may also be output +static void display_fields (const char *fields, const char *xtra) +{ +#define yRSVD 3 + const char *p; + int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD; + + /* we're relying on callers to first clear the screen and thus avoid screen + flicker if they're too lazy to handle their own asterisk (*) logic */ + putp(Curwin->cap_bold); + for (i = 0; fields[i]; ++i) { + const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]); + int b = isupper(fields[i]); + + if (!f) continue; // hey, should be std_err! + for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces + ; + PUTT("%s%s%c %c: %-10s = %s", + tg2((i / rmax) * cmax, (i % rmax) + yRSVD), + b ? Curwin->cap_bold : Cap_norm, + b ? '*' : ' ', + fields[i], + p, + f->desc + ); + } + if (xtra) { + putp(Curwin->capclr_rownorm); + while ((p = strchr(xtra, '\n'))) { + ++i; + PUTT("%s%.*s", + tg2((i / rmax) * cmax, (i % rmax) + yRSVD), + (int)(p - xtra), + xtra + ); + xtra = ++p; + } + } + putp(Caps_off); + +#undef yRSVD +} + + +// Change order of displayed fields. +static void fields_reorder (void) +{ + static const char prompt[] = + "Upper case letter moves field left, lower case right"; + char c, *p; + int i; + + putp(Cap_clr_scr); + putp(Cap_curs_huge); + for (;;) { + display_fields(Curwin->rc.fieldscur, FIELDS_xtra); + show_special(1, fmtmk(FIELDS_current + , Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt)); + chin(0, &c, 1); + if (!ft_get_ptr(FT_NEW_fmt, c)) break; + i = toupper(c) - 'A'; + if (((p = strchr(Curwin->rc.fieldscur, i + 'A'))) + || ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) { + if (isupper(c)) p--; + if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) { + c = p[0]; + p[0] = p[1]; + p[1] = c; + } + } + } + putp(Cap_curs_norm); +} + +// Select sort field. +static void fields_sort (void) +{ + static const char prompt[] = + "Select sort field via field letter, type any other key to return"; + char phoney[PFLAGSSIZ]; + char c, *p; + int i, x; + + strcpy(phoney, NUL_FIELDS); + x = i = Curwin->rc.sortindx; + putp(Cap_clr_scr); + putp(Cap_curs_huge); + for (;;) { + p = phoney + i; + *p = toupper(*p); + display_fields(phoney, SORT_xtra); + show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt)); + chin(0, &c, 1); + if (!ft_get_ptr(FT_NEW_fmt, c)) break; + i = toupper(c) - 'A'; + *p = tolower(*p); + x = i; + } + if ((p = strchr(Curwin->rc.fieldscur, x + 'a'))) + *p = x + 'A'; + Curwin->rc.sortindx = x; + putp(Cap_curs_norm); +} + + +// Toggle displayed fields. +static void fields_toggle (void) +{ + static const char prompt[] = + "Toggle fields via field letter, type any other key to return"; + char c, *p; + int i; + + putp(Cap_clr_scr); + putp(Cap_curs_huge); + for (;;) { + display_fields(Curwin->rc.fieldscur, FIELDS_xtra); + show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt)); + chin(0, &c, 1); + if (!ft_get_ptr(FT_NEW_fmt, c)) break; + i = toupper(c) - 'A'; + if ((p = strchr(Curwin->rc.fieldscur, i + 'A'))) + *p = i + 'a'; + else if ((p = strchr(Curwin->rc.fieldscur, i + 'a'))) + *p = i + 'A'; + } + putp(Cap_curs_norm); +} + +/*###### Windows/Field Groups support #################################*/ + +// For each of the four windows: +// 1) Set the number of fields/columns to display +// 2) Create the field columns heading +// 3) Set maximum cmdline length, if command lines are in use +// In the process, the required PROC_FILLxxx flags will be rebuilt! +static void reframewins (void) +{ + WIN_t *w; + char *s; + const char *h; + int i, needpsdb = 0; + +// Frames_libflags = 0; // should be called only when it's zero +// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible + w = Curwin; + do { + if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) { + // build window's procflags array and establish a tentative maxpflgs + for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) { + if (isupper(w->rc.fieldscur[i])) + w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A'; + } + + /* build a preliminary columns header not to exceed screen width + while accounting for a possible leading window number */ + *(s = w->columnhdr) = '\0'; + if (Rc.mode_altscr) s = scat(s, " "); + for (i = 0; i < w->maxpflgs; i++) { + h = Fieldstab[w->procflags[i]].head; + // oops, won't fit -- we're outta here... + if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break; + s = scat(s, h); + } + + // establish the final maxpflgs and prepare to grow the command column + // heading via maxcmdln - it may be a fib if P_CMD wasn't encountered, + // but that's ok because it won't be displayed anyway + w->maxpflgs = i; + w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head)); + + // finally, we can build the true run-time columns header, format the + // command column heading, if P_CMD is really being displayed, and + // rebuild the all-important PROC_FILLxxx flags that will be used + // until/if we're we're called again + *(s = w->columnhdr) = '\0'; +// if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum)); + for (i = 0; i < w->maxpflgs; i++) { + int advance = (i==0) && !Rc.mode_altscr; + h = Fieldstab[w->procflags[i]].head; + if (P_WCH == w->procflags[i]) needpsdb = 1; + if (P_CMD == w->procflags[i]) { + s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/ )); + if (CHKw(w, Show_CMDLIN)) { + Frames_libflags |= L_CMDLINE; +// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln; + } + } else + s = scat(s, h+advance); + Frames_libflags |= Fieldstab[w->procflags[i]].lflg; + } + if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0'; + } + if (Rc.mode_altscr) w = w->next; + } while (w != Curwin); + + // do we need the kernel symbol table (and is it already open?) + if (needpsdb) { + if (No_ksyms == -1) { + No_ksyms = 0; + if (open_psdb_message(NULL, msg_save)) + No_ksyms = 1; + else + PSDBopen = 1; + } + } + + if (selection_type=='U') Frames_libflags |= L_status; + + if (Frames_libflags & L_EITHER) { + Frames_libflags &= ~L_EITHER; + if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status; + } + if (!Frames_libflags) Frames_libflags = L_DEFAULT; + if (selection_type=='p') Frames_libflags |= PROC_PID; +} + + +// Value a window's name and make the associated group name. +static void win_names (WIN_t *q, const char *name) +{ + sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name); + sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name); +} + + +// Display a window/field group (ie. make it "current"). +static void win_select (char ch) +{ + static const char prompt[] = "Choose field group (1 - 4)"; + + /* if there's no ch, it means we're supporting the external interface, + so we must try to get our own darn ch by begging the user... */ + if (!ch) { + show_pmt(prompt); + chin(0, (char *)&ch, 1); + } + switch (ch) { + case 'a': /* we don't carry 'a' / 'w' in our */ + Curwin = Curwin->next; /* pmt - they're here for a good */ + break; /* friend of ours -- wins_colors. */ + case 'w': /* (however those letters work via */ + Curwin = Curwin->prev; /* the pmt too but gee, end-loser */ + break; /* should just press the darn key) */ + case '1': case '2': + case '3': case '4': + Curwin = &Winstk[ch - '1']; + break; + } +} + + +// Just warn the user when a command can't be honored. +static int win_warn (void) +{ + show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname)); + // we gotta' return false 'cause we're somewhat well known within + // macro society, by way of that sassy little tertiary operator... + return 0; +} + + +// Change colors *Helper* function to save/restore settings; +// ensure colors will show; and rebuild the terminfo strings. +static void winsclrhlp (WIN_t *q, int save) +{ + static int flgssav, summsav, msgssav, headsav, tasksav; + + if (save) { + flgssav = q->rc.winflags; summsav = q->rc.summclr; + msgssav = q->rc.msgsclr; headsav = q->rc.headclr; tasksav = q->rc.taskclr; + SETw(q, Show_COLORS); + } else { + q->rc.winflags = flgssav; q->rc.summclr = summsav; + q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav; + } + capsmk(q); +} + + +// Change colors used in display +static void wins_colors (void) +{ +#define kbdABORT 'q' +#define kbdAPPLY '\n' + int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr; + char ch, tgt = 'T'; + + if (0 >= max_colors) { + show_msg("\aNo colors to map!"); + return; + } + winsclrhlp(Curwin, 1); + putp(Cap_clr_scr); + putp(Cap_curs_huge); + + do { + putp(Cap_home); + /* this string is well above ISO C89's minimum requirements! */ + show_special( + 1, + fmtmk( + COLOR_help, + procps_version, + Curwin->grpname, + CHKw(Curwin, View_NOBOLD) ? "On" : "Off", + CHKw(Curwin, Show_COLORS) ? "On" : "Off", + CHKw(Curwin, Show_HIBOLD) ? "On" : "Off", + tgt, + clr, + Curwin->grpname + ) + ); + chin(0, &ch, 1); + switch (ch) { + case 'S': + pclr = &Curwin->rc.summclr; + clr = *pclr; + tgt = ch; + break; + case 'M': + pclr = &Curwin->rc.msgsclr; + clr = *pclr; + tgt = ch; + break; + case 'H': + pclr = &Curwin->rc.headclr; + clr = *pclr; + tgt = ch; + break; + case 'T': + pclr = &Curwin->rc.taskclr; + clr = *pclr; + tgt = ch; + break; + case '0' ... '7': + clr = ch - '0'; + *pclr = clr; + break; + case 'B': + TOGw(Curwin, View_NOBOLD); + break; + case 'b': + TOGw(Curwin, Show_HIBOLD); + break; + case 'z': + TOGw(Curwin, Show_COLORS); + break; + case 'a': + case 'w': + win_select(ch); + winsclrhlp(Curwin, 1); + clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr; + tgt = 'T'; + break; + } + capsmk(Curwin); + } while (kbdAPPLY != ch && kbdABORT != ch); + + if (kbdABORT == ch) + winsclrhlp(Curwin, 0); + putp(Cap_curs_norm); + +#undef kbdABORT +#undef kbdAPPLY +} + + +// Manipulate flag(s) for all our windows. +static void wins_reflag (int what, int flg) +{ + WIN_t *w; + + w = Curwin; + do { + switch (what) { + case Flags_TOG: + TOGw(w, flg); + break; + case Flags_SET: /* Ummmm, i can't find anybody */ + SETw(w, flg); /* who uses Flags_set ... */ + break; + case Flags_OFF: + OFFw(w, flg); + break; + } + // a flag with special significance -- user wants to rebalance + // display so we gotta' 'off' one number then force on two flags... + if (EQUWINS_cwo == flg) { + w->rc.maxtasks = 0; + SETw(w, Show_IDLEPS | VISIBLE_tsk); + } + w = w->next; + } while (w != Curwin); +} + + +// using a flag to avoid other code seeing inconsistant state +static volatile int need_resize; +static void wins_resize_sighandler (int dont_care_sig) +{ + (void)dont_care_sig; + need_resize = 1; + ZAP_TIMEOUT +} + + +// Set the screen dimensions and arrange for the real workhorse. +// (also) catches: +// SIGWINCH and SIGCONT +static void wins_resize (void) +{ + struct winsize wz; + char *env_columns; // Unix98 environment variable COLUMNS + char *env_lines; // Unix98 environment variable LINES + + Screen_cols = columns; // + Screen_rows = lines; // + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) { + Screen_cols = wz.ws_col; + Screen_rows = wz.ws_row; + } + + if (Batch) Screen_rows = MAXINT; + + env_columns = getenv("COLUMNS"); + if(env_columns && *env_columns){ + long t; + char *endptr; + t = strtol(env_columns, &endptr, 0); + if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t; + } + env_lines = getenv("LINES"); + if(env_lines && *env_lines){ + long t; + char *endptr; + t = strtol(env_lines, &endptr, 0); + if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t; + } + + // be crudely tolerant of crude tty emulators + if (avoid_last_column) Screen_cols--; + + // we might disappoint some folks (but they'll deserve it) + if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX; + + // keep our support for output optimization in sync with current reality + // note: when we're in Batch mode, we don't really need a Pseudo_scrn and + // when not Batch, our buffer will contain 1 extra 'line' since + // Msg_row is never represented -- but it's nice to have some space + // between us and the great-beyond... + Pseudo_cols = Screen_cols + CLRBUFSIZ + 1; + if (Batch) Pseudo_size = ROWBUFSIZ + 1; + else Pseudo_size = Pseudo_cols * Screen_rows; + Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size); + + // force rebuild of column headers AND libproc/readproc requirements + Frames_libflags = 0; +} + + +// Set up the raw/incomplete field group windows -- +// they'll be finished off after startup completes. +// [ and very likely that will override most/all of our efforts ] +// [ --- life-is-NOT-fair --- ] +static void windows_stage1 (void) +{ + WIN_t *w; + int i; + + for (i = 0; i < GROUPSMAX; i++) { + w = &Winstk[i]; + w->winnum = i + 1; + w->rc = Rc.win[i]; + w->captab[0] = Cap_norm; + w->captab[1] = Cap_norm; + w->captab[2] = w->cap_bold; + w->captab[3] = w->capclr_sum; + w->captab[4] = w->capclr_msg; + w->captab[5] = w->capclr_pmt; + w->captab[6] = w->capclr_hdr; + w->captab[7] = w->capclr_rowhigh; + w->captab[8] = w->capclr_rownorm; + w->next = w + 1; + w->prev = w - 1; + ++w; + } + /* fixup the circular chains... */ + Winstk[3].next = &Winstk[0]; + Winstk[0].prev = &Winstk[3]; + Curwin = Winstk; +} + + +// This guy just completes the field group windows after the +// rcfiles have been read and command line arguments parsed +static void windows_stage2 (void) +{ + int i; + + for (i = 0; i < GROUPSMAX; i++) { + win_names(&Winstk[i], Winstk[i].rc.winname); + capsmk(&Winstk[i]); + } + // rely on this next guy to force a call (eventually) to reframewins + wins_resize(); +} + + +/*###### Main Screen routines ##########################################*/ + +// Process keyboard input during the main loop +static void do_key (unsigned c) +{ + // standardized 'secure mode' errors + static const char err_secure[] = "\aUnavailable in secure mode"; + static const char err_num_cpus[] = "\aSorry, terminal is not big enough"; +#ifdef WARN_NOT_SMP + // standardized 'smp' errors + static const char err_smp[] = "\aSorry, only 1 cpu detected"; +#endif + + switch (c) { + case '1': + if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) { + show_msg(err_num_cpus); + break; + } +#ifdef WARN_NOT_SMP + if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM); + else show_msg(err_smp); +#else + TOGw(Curwin, View_CPUSUM); +#endif + break; + + case 'a': + if (Rc.mode_altscr) Curwin = Curwin->next; + break; + + case 'A': + Rc.mode_altscr = !Rc.mode_altscr; + wins_resize(); + break; + + case 'b': + if (VIZCHKc) { + if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS)) + show_msg("\aNothing to highlight!"); + else { + TOGw(Curwin, Show_HIBOLD); + capsmk(Curwin); + } + } + break; + + case 'B': + TOGw(Curwin, View_NOBOLD); + capsmk(Curwin); + break; + + case 'c': + VIZTOGc(Show_CMDLIN); + break; + + case 'd': + case 's': + if (Secure_mode) + show_msg(err_secure); + else { + float tmp = + get_float(fmtmk("Change delay from %.1f to", Rc.delay_time)); + if (tmp > -1) Rc.delay_time = tmp; + } + break; + + case 'f': + if (VIZCHKc) fields_toggle(); + break; + + case 'F': + case 'O': + if (VIZCHKc) fields_sort(); + break; + + case 'g': + if (Rc.mode_altscr) { + char tmp[GETBUFSIZ]; + strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname))); + if (tmp[0]) win_names(Curwin, tmp); + } + break; + + case 'G': + win_select(0); + break; + + case 'H': + if (VIZCHKc) { + TOGw(Curwin, Show_THREADS); + show_msg(fmtmk("Show threads %s" + , CHKw(Curwin, Show_THREADS) ? "On" : "Off")); + } + break; + + case 'h': + case '?': + { char ch; + putp(Cap_clr_scr); + putp(Cap_curs_huge); + /* this string is well above ISO C89's minimum requirements! */ + show_special( + 1, + fmtmk( + KEYS_help, + procps_version, + Curwin->grpname, + CHKw(Curwin, Show_CTIMES) ? "On" : "Off", + Rc.delay_time, + Secure_mode ? "On" : "Off", + Secure_mode ? "" : KEYS_help_unsecured + ) + ); + chin(0, &ch, 1); + if ('?' == ch || 'h' == ch) { + do { + putp(Cap_clr_scr); + show_special(1, fmtmk(WINDOWS_help + , Curwin->grpname + , Winstk[0].rc.winname + , Winstk[1].rc.winname + , Winstk[2].rc.winname + , Winstk[3].rc.winname)); + chin(0, &ch, 1); + win_select(ch); + } while ('\n' != ch); + } + putp(Cap_curs_norm); + } + break; + + case 'i': + VIZTOGc(Show_IDLEPS); + break; + + case 'I': +#ifdef WARN_NOT_SMP + if (Cpu_tot > 1) { + Rc.mode_irixps = !Rc.mode_irixps; + show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off")); + } else + show_msg(err_smp); +#else + Rc.mode_irixps = !Rc.mode_irixps; + show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off")); +#endif + if(Rc.mode_irixps && smp_num_cpus>1){ + // good for 100 CPUs per process + pcpu_max_value = 9999.0; + Fieldstab[P_CPU].fmts = " %4.0f"; + } else { + pcpu_max_value = 99.9; + Fieldstab[P_CPU].fmts = " %#4.1f"; + } + break; + + case 'k': + if (Secure_mode) { + show_msg(err_secure); + } else { + int sig, pid = get_int("PID to kill"); + if (pid > 0) { + sig = signal_name_to_number( + ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL))); + if (sig == -1) sig = DEF_SIGNAL; + if (sig && kill(pid, sig)) + show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno))); + } + } + break; + + case 'l': + TOGw(Curwin, View_LOADAV); + break; + + case 'm': + TOGw(Curwin, View_MEMORY); + break; + + case 'n': + case '#': + if (VIZCHKc) { + int num = + get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks)); + if (num > -1) Curwin->rc.maxtasks = num; + } + break; + + case 'o': + if (VIZCHKc) fields_reorder(); + break; + + case 'q': + end_pgm(0); + + case 'r': + if (Secure_mode) + show_msg(err_secure); + else { + int val, pid = get_int("PID to renice"); + if (pid > 0) { + val = get_int(fmtmk("Renice PID %d to value", pid)); + if (setpriority(PRIO_PROCESS, (unsigned)pid, val)) + show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno))); + } + } + break; + + case 'R': + VIZTOGc(Qsrt_NORMAL); + break; + + case 'S': + if (VIZCHKc) { + TOGw(Curwin, Show_CTIMES); + show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off")); + } + break; + + case 't': + TOGw(Curwin, View_STATES); + break; + +// case 'u': +// if (VIZCHKc) +// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)")); +// break; + + case 'u': +// if (!VIZCHKc) break; + do { + const char *errmsg; + const char *answer; + answer = ask4str("Which user (blank for all)"); + // FIXME: do this better: + if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') { + selection_type = 0; + selection_uid = -1; + break; + } + errmsg = parse_uid(answer, &selection_uid); + if (errmsg) { + show_msg(errmsg); + // Change settings here? I guess not. + break; + } + selection_type = 'u'; + } while(0); + break; + + case 'U': +// if (!VIZCHKc) break; + do { + const char *errmsg; + const char *answer; + answer = ask4str("Which user (blank for all)"); + // FIXME: do this better: + if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') { + selection_type = 0; + selection_uid = -1; + break; + } + errmsg = parse_uid(answer, &selection_uid); + if (errmsg) { + show_msg(errmsg); + // Change settings here? I guess not. + break; + } + selection_type = 'U'; + } while(0); + break; + + case 'w': + if (Rc.mode_altscr) Curwin = Curwin->prev; + break; + + case 'W': + { const char *err = rc_write_whatever(); + if (err) + show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err)); + else + show_msg(fmtmk("Wrote configuration to '%s'", Rc_name)); + } + break; + + case 'x': + if (VIZCHKc) { + TOGw(Curwin, Show_HICOLS); + capsmk(Curwin); + } + break; + + case 'y': + if (VIZCHKc) { + TOGw(Curwin, Show_HIROWS); + capsmk(Curwin); + } + break; + + case 'z': + if (VIZCHKc) { + TOGw(Curwin, Show_COLORS); + capsmk(Curwin); + } + break; + + case 'Z': + wins_colors(); + break; + + case '-': + if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk); + break; + + case '_': + if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk); + break; + + case '=': + Curwin->rc.maxtasks = 0; + SETw(Curwin, Show_IDLEPS | VISIBLE_tsk); + Monpidsidx = 0; + selection_type = '\0'; + break; + + case '+': + if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo); + break; + + case '<': + if (VIZCHKc) { + FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1; + while (*p != Curwin->rc.sortindx) --p; + if (--p >= Curwin->procflags) + Curwin->rc.sortindx = *p; + } + break; + + case '>': + if (VIZCHKc) { + FLG_t *p = Curwin->procflags; + while (*p != Curwin->rc.sortindx) ++p; + if (++p < Curwin->procflags + Curwin->maxpflgs) + Curwin->rc.sortindx = *p; + } + break; + + case 'M': // these keys represent old-top compatability + case 'N': // -- grouped here so that if users could ever + case 'P': // be weaned, we would just whack this part... + case 'T': + { static struct { + const char *xmsg; + const unsigned xkey; + const FLG_t sort; + } xtab[] = { + { "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, }, + { "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, }; + int i; + for (i = 0; i < MAXTBL(xtab); ++i) + if (c == xtab[i].xkey) { + Curwin->rc.sortindx = xtab[i].sort; +// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg)); + break; + } + } + break; + + case '\n': // just ignore these, they'll have the effect + case ' ': // of refreshing display after waking us up ! + break; + + default: + show_msg("\aUnknown command - try 'h' for help"); + } + // The following assignment will force a rebuild of all column headers and + // the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are + // some keys that COULD require new column headers and/or libproc flags: + // 'A' - likely + // 'c' - likely when !Mode_altscr, maybe when Mode_altscr + // 'F' - maybe, if new field forced on + // 'f' - likely + // 'G' - likely + // 'O' - maybe, if new field forced on + // 'o' - maybe, if new field brought into view + // 'Z' - likely, if 'Curwin' changed when !Mode_altscr + // '-' - likely (restricted to Mode_altscr) + // '_' - likely (restricted to Mode_altscr) + // '=' - maybe, but only when Mode_altscr + // '+' - likely (restricted to Mode_altscr) + // ( At this point we have a human being involved and so have all the time ) + // ( in the world. We can afford a few extra cpu cycles every now & then! ) + Frames_libflags = 0; +} + + +// State display *Helper* function to calc and display the state +// percentages for a single cpu. In this way, we can support +// the following environments without the usual code bloat. +// 1) single cpu machines +// 2) modest smp boxes with room for each cpu's percentages +// 3) massive smp guys leaving little or no room for process +// display and thus requiring the cpu summary toggle +static void summaryhlp (CPU_t *cpu, const char *pfx) +{ + // we'll trim to zero if we get negative time ticks, + // which has happened with some SMP kernels (pre-2.4?) +#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz) + SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz; + float scale; + + u_frme = cpu->u - cpu->u_sav; + s_frme = cpu->s - cpu->s_sav; + n_frme = cpu->n - cpu->n_sav; + i_frme = TRIMz(cpu->i - cpu->i_sav); + w_frme = cpu->w - cpu->w_sav; + x_frme = cpu->x - cpu->x_sav; + y_frme = cpu->y - cpu->y_sav; + z_frme = cpu->z - cpu->z_sav; + tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme; + if (tot_frme < 1) tot_frme = 1; + scale = 100.0 / (float)tot_frme; + + // display some kinda' cpu state percentages + // (who or what is explained by the passed prefix) + show_special( + 0, + fmtmk( + States_fmts, + pfx, + (float)u_frme * scale, + (float)s_frme * scale, + (float)n_frme * scale, + (float)i_frme * scale, + (float)w_frme * scale, + (float)x_frme * scale, + (float)y_frme * scale, + (float)z_frme * scale + ) + ); + Msg_row += 1; + + // remember for next time around + cpu->u_sav = cpu->u; + cpu->s_sav = cpu->s; + cpu->n_sav = cpu->n; + cpu->i_sav = cpu->i; + cpu->w_sav = cpu->w; + cpu->x_sav = cpu->x; + cpu->y_sav = cpu->y; + cpu->z_sav = cpu->z; + +#undef TRIMz +} + + +// Begin a new frame by: +// 1) Refreshing the all important proc table +// 2) Displaying uptime and load average (maybe) +// 3) Displaying task/cpu states (maybe) +// 4) Displaying memory & swap usage (maybe) +// and then, returning a pointer to the pointers to the proc_t's! +static proc_t **summary_show (void) +{ + static proc_t **p_table = NULL; + static CPU_t *smpcpu = NULL; + + // whoa first time, gotta' prime the pump... + if (!p_table) { + p_table = procs_refresh(NULL, Frames_libflags); + putp(Cap_clr_scr); + putp(Cap_rmam); +#ifndef PROF + // sleep for half a second + tv.tv_sec = 0; + tv.tv_usec = 500000; + select(0, NULL, NULL, NULL, &tv); // ought to loop until done +#endif + } else { + putp(Batch ? "\n\n" : Cap_home); + } + p_table = procs_refresh(p_table, Frames_libflags); + + // Display Uptime and Loadavg + if (CHKw(Curwin, View_LOADAV)) { + if (!Rc.mode_altscr) { + show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime())); + } else { + show_special( + 0, + fmtmk( + CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line, + Curwin->grpname, + sprint_uptime() + ) + ); + } + Msg_row += 1; + } + + // Display Task and Cpu(s) States + if (CHKw(Curwin, View_STATES)) { + show_special( + 0, + fmtmk( + STATES_line1, + Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied + ) + ); + Msg_row += 1; + + smpcpu = cpus_refresh(smpcpu); + + if (CHKw(Curwin, View_CPUSUM)) { + // display just the 1st /proc/stat line + summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):"); + } else { + int i; + char tmp[SMLBUFSIZ]; + // display each cpu's states separately + for (i = 0; i < Cpu_tot; i++) { + snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id); + summaryhlp(&smpcpu[i], tmp); + } + } + } + + // Display Memory and Swap stats + meminfo(); + if (CHKw(Curwin, View_MEMORY)) { + show_special(0, fmtmk(MEMORY_line1 + , kb_main_total, kb_main_used, kb_main_free, kb_main_buffers)); + show_special(0, fmtmk(MEMORY_line2 + , kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached)); + Msg_row += 2; + } + + SETw(Curwin, NEWFRAM_cwo); + return p_table; +} + + +#define PAGES_TO_KB(n) (unsigned long)( (n) << page_to_kb_shift ) + +// the following macro is our means to 'inline' emitting a column -- next to +// procs_refresh, that's the most frequent and costly part of top's job ! +#define MKCOL(va...) do { \ + if(likely(!( CHKw(q, Show_HICOLS) && q->rc.sortindx==i ))) { \ + snprintf(cbuf, sizeof(cbuf), f, ## va); \ + } else { \ + snprintf(_z, sizeof(_z), f, ## va); \ + snprintf(cbuf, sizeof(cbuf), "%s%s%s", \ + q->capclr_rowhigh, \ + _z, \ + !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \ + ); \ + pad += q->len_rowhigh; \ + if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \ + } \ +} while (0) + +// Display information for a single task row. +static void task_show (const WIN_t *q, const proc_t *p) +{ + char rbuf[ROWBUFSIZ]; + char *rp = rbuf; + int j, x, pad; + + *rp = '\0'; + + pad = Rc.mode_altscr; +// if (pad) rp = scat(rp, " "); + + for (x = 0; x < q->maxpflgs; x++) { + char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ]; + FLG_t i = q->procflags[x]; // support for our field/column + const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt + unsigned s = Fieldstab[i].scale; // string must be altered ! + unsigned w = Fieldstab[i].width; + + int advance = (x==0) && !Rc.mode_altscr; + + switch (i) { + case P_CMD: + { char tmp[ROWBUFSIZ]; + unsigned flags; + int maxcmd = q->maxcmdln; + if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS; + else flags = ESC_DEFUNCT; + escape_command(tmp, p, sizeof tmp, &maxcmd, flags); + MKCOL(q->maxcmdln, q->maxcmdln, tmp); + } + break; + case P_COD: + MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s)); + break; + case P_CPN: + MKCOL((unsigned)p->processor); + break; + case P_CPU: + { float u = (float)p->pcpu * Frame_tscale; + if (u > pcpu_max_value) u = pcpu_max_value; + MKCOL(u); + } + break; + case P_DAT: + MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s)); + break; + case P_DRT: + MKCOL(scale_num((unsigned)p->dt, w, s)); + break; + case P_FLG: + { char tmp[TNYBUFSIZ]; + snprintf(tmp, sizeof(tmp), f, (long)p->flags); + for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.'; + f = tmp; + MKCOL(""); + } + break; + case P_FLT: + MKCOL(scale_num(p->maj_flt, w, s)); + break; + case P_GRP: + MKCOL(p->egroup); + break; + case P_MEM: + MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total); + break; + case P_NCE: + MKCOL((int)p->nice); + break; + case P_PID: + MKCOL((unsigned)p->XXXID); + break; + case P_PPD: + MKCOL((unsigned)p->ppid); + break; + case P_PRI: + if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) { + f = " RT"; + MKCOL(""); + } else + MKCOL((int)p->priority); + break; + case P_RES: + MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s)); + break; + case P_SHR: + MKCOL(scale_num(PAGES_TO_KB(p->share), w, s)); + break; + case P_STA: + MKCOL(p->state); + break; + case P_SWP: + MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s)); + break; + case P_TME: + case P_TM2: + { TIC_t t = p->utime + p->stime; + if (CHKw(q, Show_CTIMES)) + t += (p->cutime + p->cstime); + MKCOL(scale_tics(t, w)); + } + break; + case P_TTY: + { char tmp[TNYBUFSIZ]; + dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV); + MKCOL(tmp); + } + break; + case P_UID: + MKCOL((unsigned)p->euid); + break; + case P_URE: + MKCOL(p->euser); + break; + case P_URR: + MKCOL(p->ruser); + break; + case P_VRT: + MKCOL(scale_num(PAGES_TO_KB(p->size), w, s)); + break; + case P_WCH: + if (No_ksyms) { + f = " %08lx "; + MKCOL((long)p->wchan); + } else { + MKCOL(lookup_wchan(p->wchan, p->XXXID)); + } + break; + + } /* end: switch 'procflag' */ + + rp = scat(rp, cbuf+advance); + } /* end: for 'maxpflgs' */ + + PUFF( + "\n%s%.*s%s%s", + (CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm, + Screen_cols + pad, + rbuf, + Caps_off, + "" /*Cap_clr_eol*/ + ); + +#undef MKCOL +} + + +// Squeeze as many tasks as we can into a single window, +// after sorting the passed proc table. +static void window_show (proc_t **ppt, WIN_t *q, int *lscr) +{ +#ifdef SORT_SUPRESS + // the 1 flag that DOES and 2 flags that MAY impact our proc table qsort +#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES ) + static FLG_t sav_indx = 0; + static int sav_flgs = -1; +#endif + int i, lwin; + + // Display Column Headings -- and distract 'em while we sort (maybe) + PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol); + +#ifdef SORT_SUPRESS + if (CHKw(Curwin, NEWFRAM_cwo) + || sav_indx != q->rc.sortindx + || sav_flgs != (q->rc.winflags & srtMASK)) { + sav_indx = q->rc.sortindx; + sav_flgs = (q->rc.winflags & srtMASK); +#endif + if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed! + else Frame_srtflg = -1; + Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe + Frame_cmdlin = CHKw(q, Show_CMDLIN); + qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort); +#ifdef SORT_SUPRESS + } +#endif + // account for column headings + (*lscr)++; + lwin = 1; + i = 0; + + while ( ppt[i]->tid != -1 && *lscr < Max_lines && (!q->winlines || (lwin <= q->winlines)) ) { + if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state)) + && good_uid(ppt[i]) ) { + // Display a process Row + task_show(q, ppt[i]); + (*lscr)++; + ++lwin; + } + ++i; + } + // for this frame that window's toast, cleanup for next time + q->winlines = 0; + OFFw(Curwin, FLGSOFF_cwo); + +#ifdef SORT_SUPRESS +#undef srtMASK +#endif +} + + +/*###### Entry point plus two ##########################################*/ + +// This guy's just a *Helper* function who apportions the +// remaining amount of screen real estate under multiple windows +static void framehlp (int wix, int max) +{ + int i, rsvd, size, wins; + + // calc remaining number of visible windows + total 'user' lines + for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) { + if (CHKw(&Winstk[i], VISIBLE_tsk)) { + rsvd += Winstk[i].rc.maxtasks; + ++wins; + if (max <= rsvd) break; + } + } + if (!wins) wins = 1; + // set aside 'rsvd' & deduct 1 line/window for the columns heading + size = (max - wins) - rsvd; + if (0 <= size) size = max; + size = (max - wins) / wins; + + // for remaining windows, set WIN_t winlines to either the user's + // maxtask (1st choice) or our 'foxized' size calculation + // (foxized adj. - 'fair and balanced') + for (i = wix ; i < GROUPSMAX; i++) { + if (CHKw(&Winstk[i], VISIBLE_tsk)) { + Winstk[i].winlines = + Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size; + } + } +} + + +// Initiate the Frame Display Update cycle at someone's whim! +// This routine doesn't do much, mostly he just calls others. +// +// (Whoa, wait a minute, we DO caretake those row guys, plus) +// (we CALCULATE that IMPORTANT Max_lines thingy so that the) +// (*subordinate* functions invoked know WHEN the user's had) +// (ENOUGH already. And at Frame End, it SHOULD be apparent) +// (WE am d'MAN -- clearing UNUSED screen LINES and ensuring) +// (the CURSOR is STUCK in just the RIGHT place, know what I) +// (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say) +// (THAT about THIS function again, Ok? Good that's better.) +static void frame_make (void) +{ + proc_t **ppt; + int i, scrlins; + + // note: all libproc flags are managed by + // reframewins(), who also builds each window's column headers + if (!Frames_libflags) { + reframewins(); + memset(Pseudo_scrn, '\0', Pseudo_size); + } + Pseudo_row = Msg_row = scrlins = 0; + ppt = summary_show(); + Max_lines = (Screen_rows - Msg_row) - 1; + + if (CHKw(Curwin, EQUWINS_cwo)) + wins_reflag(Flags_OFF, EQUWINS_cwo); + + // sure hope each window's columns header begins with a newline... + putp(tg2(0, Msg_row)); + + if (!Rc.mode_altscr) { + // only 1 window to show so, piece o' cake + Curwin->winlines = Curwin->rc.maxtasks; + window_show(ppt, Curwin, &scrlins); + } else { + // maybe NO window is visible but assume, pieces o' cakes + for (i = 0 ; i < GROUPSMAX; i++) { + if (CHKw(&Winstk[i], VISIBLE_tsk)) { + framehlp(i, Max_lines - scrlins); + window_show(ppt, &Winstk[i], &scrlins); + } + if (Max_lines <= scrlins) break; + } + } + // clear to end-of-screen (critical if last window is 'idleps off'), + // then put the cursor in-its-place, and rid us of any prior frame's msg + // (main loop must iterate such that we're always called before sleep) + PUTT( + "%s%s%s%s", + scrlins < Max_lines ? "\n" : "", + scrlins < Max_lines ? Cap_clr_eos : "", + tg2(0, Msg_row), + Cap_clr_eol + ); + fflush(stdout); +} + + +int main (int dont_care_argc, char *argv[]) +{ + (void)dont_care_argc; + before(*argv); + // +-------------+ + windows_stage1(); // top (sic) slice + configs_read(); // > spread etc, < + parse_args(&argv[1]); // > lean stuff, < + whack_terminal(); // > onions etc. < + windows_stage2(); // as bottom slice + // +-------------+ + signal(SIGALRM, end_pgm); + signal(SIGHUP, end_pgm); + signal(SIGINT, end_pgm); + signal(SIGPIPE, end_pgm); + signal(SIGQUIT, end_pgm); + signal(SIGTERM, end_pgm); + signal(SIGTSTP, suspend); + signal(SIGTTIN, suspend); + signal(SIGTTOU, suspend); + signal(SIGCONT, wins_resize_sighandler); + signal(SIGWINCH, wins_resize_sighandler); + + for (;;) { + if (need_resize){ + need_resize = 0; + wins_resize(); + } + frame_make(); + + if (Msg_awaiting) show_msg(Msg_delayed); + if (Loops > 0) --Loops; + if (!Loops) end_pgm(0); + + tv.tv_sec = Rc.delay_time; + tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000; + + if (Batch) { + select(0, NULL, NULL, NULL, &tv); // ought to loop until done + } else { + long file_flags; + int rc; + char c; + fd_set fs; + FD_ZERO(&fs); + FD_SET(STDIN_FILENO, &fs); + file_flags = fcntl(STDIN_FILENO, F_GETFL); + if(file_flags==-1) file_flags=0; + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags); + + // check 1st, in case tv zeroed (by sig handler) before it got set + rc = chin(0, &c, 1); + if (rc <= 0) { + // EOF is pretty much a "can't happen" except for a kernel bug. + // We should quickly die via SIGHUP, and thus not spin here. + // if (rc == 0) end_pgm(0); /* EOF from terminal */ + fcntl(STDIN_FILENO, F_SETFL, file_flags); + select(1, &fs, NULL, NULL, &tv); + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags); + } + if (chin(0, &c, 1) > 0) { + fcntl(STDIN_FILENO, F_SETFL, file_flags); + do_key((unsigned)c); + } else { + fcntl(STDIN_FILENO, F_SETFL, file_flags); + } + } + } + + return 0; +} diff --git a/top.h b/top.h new file mode 100644 index 0000000..b4ac079 --- /dev/null +++ b/top.h @@ -0,0 +1,567 @@ +// top.h - Header file: show Linux processes +// +// Copyright (c) 2002, by: James C. Warner +// All rights reserved. 8921 Hilloway Road +// Eden Prairie, Minnesota 55347 USA +// +// +// This file may be used subject to the terms and conditions of the +// GNU Library General Public License Version 2, or any later version +// at your option, as published by the Free Software Foundation. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Library General Public License for more details. +// +// For their contributions to this program, the author wishes to thank: +// Albert D. Cahalan, +// Craig Small, +// +// Changes by Albert Cahalan, 2002, 2004. + +#ifndef _Itop +#define _Itop + +// Defines intended to be experimented with ------------------------ +//#define CASEUP_HEXES // show any hex values in upper case +//#define CASEUP_SCALE // show scaled time/num suffix upper case +//#define CASEUP_SUMMK // show memory summary kilobytes with 'K' +//#define SORT_SUPRESS // *attempt* to reduce qsort overhead +//#define WARN_NOT_SMP // restrict '1' & 'I' commands to true smp + +// Development/Debugging defines ----------------------------------- +//#define ATEOJ_REPORT // report a bunch of stuff, at end-of-job +//#define PRETEND2_5_X // pretend we're linux 2.5.x (for IO-wait) +//#define PRETENDNOCAP // use a terminal without essential caps +//#define STDOUT_IOLBF // disable our own stdout _IOFBF override + +#ifdef PRETEND2_5_X +#define linux_version_code LINUX_VERSION(2,5,43) +#endif + +/*###### Some Miscellaneous constants ##################################*/ + +// The default delay twix updates +#define DEF_DELAY 3.0 + +// The length of time a 'message' is displayed +#define MSG_SLEEP 2 + +// The default value for the 'k' (kill) request +#define DEF_SIGNAL SIGTERM + +// Specific process id monitoring support (command line only) +#define MONPIDMAX 20 + +// Power-of-two sizes lead to trouble; the largest power of +// two factor should be the cache line size. It'll mean the +// array indexing math gets slower, but cache aliasing is +// avoided. +#define CACHE_TWEAK_FACTOR 64 + +// Miscellaneous buffer sizes with liberal values -- mostly +// just to pinpoint source code usage/dependancies +#define SCREENMAX ( 512 + CACHE_TWEAK_FACTOR) +// the above might seem pretty stingy, until you consider that with every +// one of top's fields displayed we're talking a 160 byte column header -- +// so that will provide for all fields plus a 350+ byte command line +#define WINNAMSIZ 4 +#define CAPTABMAX 9 +#define PFLAGSSIZ 32 +#define CAPBUFSIZ 32 +#define CLRBUFSIZ 64 +#define GETBUFSIZ 32 +#define TNYBUFSIZ 32 +#define SMLBUFSIZ ( 256 + CACHE_TWEAK_FACTOR) +#define OURPATHSZ (1024 + CACHE_TWEAK_FACTOR) +#define MEDBUFSIZ (1024 + CACHE_TWEAK_FACTOR) +#define BIGBUFSIZ (2048 + CACHE_TWEAK_FACTOR) +#define USRNAMSIZ GETBUFSIZ +#define ROWBUFSIZ SCREENMAX + CLRBUFSIZ + + +/*###### Some Miscellaneous Macro definitions ##########################*/ + +// Yield table size as 'int' +#define MAXTBL(t) (int)(sizeof(t) / sizeof(t[0])) + +// Used as return arguments in *some* of the sort callbacks +#define SORT_lt ( Frame_srtflg > 0 ? 1 : -1 ) +#define SORT_gt ( Frame_srtflg > 0 ? -1 : 1 ) +#define SORT_eq 0 + +// Used to create *most* of the sort callback functions +#define SCB_NUM1(f,n) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + if ( (*P)->n < (*Q)->n ) return SORT_lt; \ + if (likely( (*P)->n > (*Q)->n )) return SORT_gt; \ + return SORT_eq; } +#define SCB_NUM2(f,n1,n2) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + if ( ((*P)->n1 - (*P)->n2) < ((*Q)->n1 - (*Q)->n2) ) return SORT_lt; \ + if (likely( ((*P)->n1 - (*P)->n2) > ((*Q)->n1 - (*Q)->n2) )) return SORT_gt; \ + return SORT_eq; } +#define SCB_NUMx(f,n) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + return Frame_srtflg * ( (*Q)->n - (*P)->n ); } +#define SCB_STRx(f,s) \ + static int sort_ ## f (const proc_t **P, const proc_t **Q) { \ + return Frame_srtflg * strcmp((*Q)->s, (*P)->s); } + +// The following two macros are used to 'inline' those portions of the +// display process requiring formatting, while protecting against any +// potential embedded 'millesecond delay' escape sequences. + +// PUTT - Put to Tty (used in many places) +// - for temporary, possibly interactive, 'replacement' output +// - may contain ANY valid terminfo escape sequences +// - need NOT represent an entire screen row +#define PUTT(fmt,arg...) do { \ + char _str[ROWBUFSIZ]; \ + snprintf(_str, sizeof(_str), fmt, ## arg); \ + putp(_str); \ + } while (0) + +// PUFF - Put for Frame (used in only 3 places) +// - for more permanent frame-oriented 'update' output +// - may NOT contain cursor motion terminfo escapes +// - assumed to represent a complete screen ROW +// - subject to optimization, thus MAY be discarded + +// The evil version (53892 byte stripped top, oddly enough) +#define _PUFF(fmt,arg...) \ +do { \ + char _str[ROWBUFSIZ]; \ + int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ + putp ( Batch ? _str : \ + ({ \ + char *restrict const _pse = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ + memcmp(_pse, _str, _len) ? memcpy(_pse, _str, _len) : "\n"; \ + }) \ + ); \ +} while (0) + +// The good version (53876 byte stripped top) +#define PUFF(fmt,arg...) \ +do { \ + char _str[ROWBUFSIZ]; \ + char *_ptr; \ + int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \ + if (Batch) _ptr = _str; \ + else { \ + _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \ + if (memcmp(_ptr, _str, _len)) { \ + memcpy(_ptr, _str, _len); \ + } else { \ + _ptr = "\n"; \ + } \ + } \ + putp(_ptr); \ +} while (0) + + +/*------ Special Macros (debug and/or informative) ---------------------*/ + +// Orderly end, with any sort of message - see fmtmk +#define debug_END(s) { \ + static void std_err (const char *); \ + fputs(Cap_clr_scr, stdout); \ + std_err(s); \ + } + +// A poor man's breakpoint, if he's too lazy to learn gdb +#define its_YOUR_fault { *((char *)0) = '!'; } + + +/*###### Some Typedef's and Enum's #####################################*/ + +// This typedef just ensures consistent 'process flags' handling +typedef unsigned FLG_t; + +// These typedefs attempt to ensure consistent 'ticks' handling +typedef unsigned long long TIC_t; +typedef long long SIC_t; + +// Sort support, callback funtion signature +typedef int (*QFP_t)(const void *, const void *); + +// This structure consolidates the information that's used +// in a variety of display roles. +typedef struct FLD_t { + const char keys [4]; // order: New-on New-off Old-on Old-off + // misaligned on 64-bit, but part of a table -- oh well + const char *head; // name for col heads + toggle/reorder fields + const char *fmts; // sprintf format string for field display + const int width; // field width, if applicable + const int scale; // scale_num type, if applicable + const QFP_t sort; // sort function + const char *desc; // description for toggle/reorder fields + const int lflg; // PROC_FILLxxx flag(s) needed by this field +} FLD_t; + +// This structure stores one piece of critical 'history' +// information from one frame to the next -- we don't calc +// and save data that goes unused +typedef struct HST_t { + TIC_t tics; + int pid; +} HST_t; + +// This structure stores a frame's cpu tics used in history +// calculations. It exists primarily for SMP support but serves +// all environments. +typedef struct CPU_t { + TIC_t u, n, s, i, w, x, y, z; // as represented in /proc/stat + TIC_t u_sav, s_sav, n_sav, i_sav, w_sav, x_sav, y_sav, z_sav; // in the order of our display + unsigned id; // the CPU ID number +} CPU_t; + +// These 2 types support rcfile compatibility +typedef struct RCW_t { // the 'window' portion of an rcfile + FLG_t sortindx; // sort field, represented as a procflag + int winflags, // 'view', 'show' and 'sort' mode flags + maxtasks, // user requested maximum, 0 equals all + summclr, // color num used in summ info + msgsclr, // " in msgs/pmts + headclr, // " in cols head + taskclr; // " in task rows + char winname [WINNAMSIZ], // window name, user changeable + fieldscur [PFLAGSSIZ]; // fields displayed and ordered +} RCW_t; + +typedef struct RCF_t { // the complete rcfile (new style) + int mode_altscr; // 'A' - Alt display mode (multi task windows) + int mode_irixps; // 'I' - Irix vs. Solaris mode (SMP-only) + float delay_time; // 'd' or 's' - How long to sleep twixt updates + int win_index; // Curwin, as index + RCW_t win [4]; // a 'WIN_t.rc' for each of the 4 windows +} RCF_t; + +// The scaling 'type' used with scale_num() -- this is how +// the passed number is interpreted should scaling be necessary +enum scale_num { + SK_no, SK_Kb, SK_Mb, SK_Gb, SK_Tb +}; + +// Flags for each possible field +enum pflag { + P_PID, P_PPD, P_URR, P_UID, P_URE, P_GRP, P_TTY, + P_PRI, P_NCE, + P_CPN, P_CPU, P_TME, P_TM2, + P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR, + P_FLT, P_DRT, + P_STA, P_CMD, P_WCH, P_FLG +}; + + +/////////////////////////////////////////////////////////////////////////// +// Special Section: multiple windows/field groups ------------- +// (kind of a header within a header: constants, macros & types) + +#define GROUPSMAX 4 // the max number of simultaneous windows +#define GRPNAMSIZ WINNAMSIZ+2 // window's name + number as in: '#:...' + +#define Flags_TOG 1 // these are used to direct wins_reflag +#define Flags_SET 2 +#define Flags_OFF 3 + +// The Persistent 'Mode' flags! +// These are preserved in the rc file, as a single integer and the +// letter shown is the corresponding 'command' toggle + +// 'View_' flags affect the summary (minimum), taken from 'Curwin' +#define View_CPUSUM 0x8000 // '1' - show combined cpu stats (vs. each) +#define View_LOADAV 0x4000 // 'l' - display load avg and uptime summary +#define View_STATES 0x2000 // 't' - display task/cpu(s) states summary +#define View_MEMORY 0x1000 // 'm' - display memory summary +#define View_NOBOLD 0x0001 // 'B' - disable 'bold' attribute globally + +// 'Show_' & 'Qsrt_' flags are for task display in a visible window +#define Show_THREADS 0x10000 // 'H' - show threads in each task +#define Show_COLORS 0x0800 // 'z' - show in color (vs. mono) +#define Show_HIBOLD 0x0400 // 'b' - rows and/or cols bold (vs. reverse) +#define Show_HICOLS 0x0200 // 'x' - show sort column highlighted +#define Show_HIROWS 0x0100 // 'y' - show running tasks highlighted +#define Show_CMDLIN 0x0080 // 'c' - show cmdline vs. name +#define Show_CTIMES 0x0040 // 'S' - show times as cumulative +#define Show_IDLEPS 0x0020 // 'i' - show idle processes (all tasks) +#define Qsrt_NORMAL 0x0010 // 'R' - reversed column sort (high to low) + +// these flag(s) have no command as such - they're for internal use +#define VISIBLE_tsk 0x0008 // tasks are showable when in 'Mode_altscr' +#define NEWFRAM_cwo 0x0004 // new frame (if anyone cares) - in Curwin +#define EQUWINS_cwo 0x0002 // rebalance tasks next frame (off 'i'/ 'n') + // ...set in Curwin, but impacts all windows + +// Current-window-only flags -- always turned off at end-of-window! +#define FLGSOFF_cwo EQUWINS_cwo | NEWFRAM_cwo + +// Default flags if there's no rcfile to provide user customizations +#define DEF_WINFLGS ( \ + View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY | View_NOBOLD | \ + Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Qsrt_NORMAL | \ + VISIBLE_tsk \ +) + + // Used to test/manipulate the window flags +#define CHKw(q,f) (int)((q)->rc.winflags & (f)) +#define TOGw(q,f) (q)->rc.winflags ^= (f) +#define SETw(q,f) (q)->rc.winflags |= (f) +#define OFFw(q,f) (q)->rc.winflags &= ~(f) +#define VIZCHKc (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \ + ? 1 : win_warn() +#define VIZTOGc(f) (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \ + ? TOGw(Curwin, f) : win_warn() + +// This structure stores configurable information for each window. +// By expending a little effort in its creation and user requested +// maintainence, the only real additional per frame cost of having +// windows is an extra sort -- but that's just on ptrs! +typedef struct WIN_t { + struct WIN_t *next, // next window in window stack + *prev; // prior window in window stack + char *captab [CAPTABMAX]; // captab needed by show_special + int winnum, // window's num (array pos + 1) + winlines; // task window's rows (volatile) + FLG_t procflags [PFLAGSSIZ]; // fieldscur subset, as enum + int maxpflgs, // number of procflags (upcase fieldscur) + maxcmdln; // max length of a process' command line + int len_rownorm, // lengths of the corresponding terminfo + len_rowhigh; // strings to avoid repeated strlen calls + RCW_t rc; // stuff that gets saved in the rcfile + char capclr_sum [CLRBUFSIZ], // terminfo strings built from + capclr_msg [CLRBUFSIZ], // above clrs (& rebuilt too), + capclr_pmt [CLRBUFSIZ], // but NO recurring costs !!! + capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs + capclr_rowhigh [CLRBUFSIZ], // are only used when this + capclr_rownorm [CLRBUFSIZ]; // window is the 'Curwin'! + char cap_bold [CAPBUFSIZ]; // support for View_NOBOLD toggle + char grpname [GRPNAMSIZ], // window number:name, printable + columnhdr [SCREENMAX], // column headings for procflags + colusrnam [USRNAMSIZ]; // if selected by the 'u' command +} WIN_t; + + +/*###### Display Support *Data* ########################################*/ + +// Configuration files support +#define SYS_RCFILESPEC "/etc/toprc" +#define RCF_EYECATCHER "RCfile for " +#define RCF_DEPRECATED "Id:a, " + +// The default fields displayed and their order, +#define DEF_FIELDS "AEHIOQTWKNMbcdfgjplrsuvyzX" +// Pre-configured field groupss +#define JOB_FIELDS "ABcefgjlrstuvyzMKNHIWOPQDX" +#define MEM_FIELDS "ANOPQRSTUVbcdefgjlmyzWHIKX" +#define USR_FIELDS "ABDECGfhijlopqrstuvyzMKNWX" +// Used by fields_sort, placed here for peace-of-mind +#define NUL_FIELDS "abcdefghijklmnopqrstuvwxyz" + + +// The default values for the local config file +#define DEF_RCFILE { \ + 0, 1, DEF_DELAY, 0, { \ + { P_CPU, DEF_WINFLGS, 0, \ + COLOR_RED, COLOR_RED, COLOR_YELLOW, COLOR_RED, \ + "Def", DEF_FIELDS }, \ + { P_PID, DEF_WINFLGS, 0, \ + COLOR_CYAN, COLOR_CYAN, COLOR_WHITE, COLOR_CYAN, \ + "Job", JOB_FIELDS }, \ + { P_MEM, DEF_WINFLGS, 0, \ + COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLUE, COLOR_MAGENTA, \ + "Mem", MEM_FIELDS }, \ + { P_URE, DEF_WINFLGS, 0, \ + COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW, \ + "Usr", USR_FIELDS } \ + } } + + +// Summary Lines specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define LOADAV_line "%s -%s\n" +#define LOADAV_line_alt "%s\06 -%s\n" +#define STATES_line1 "Tasks:\03" \ + " %3u \02total,\03 %3u \02running,\03 %3u \02sleeping,\03 %3u \02stopped,\03 %3u \02zombie\03\n" +#define STATES_line2x4 "%s\03" \ + " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle\03\n" +#define STATES_line2x5 "%s\03" \ + " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle,\03 %#5.1f%% \02IO-wait\03\n" +#define STATES_line2x6 "%s\03" \ + " %#4.1f%% \02us,\03 %#4.1f%% \02sy,\03 %#4.1f%% \02ni,\03 %#4.1f%% \02id,\03 %#4.1f%% \02wa,\03 %#4.1f%% \02hi,\03 %#4.1f%% \02si\03\n" +#define STATES_line2x7 "%s\03" \ + "%#5.1f%%\02us,\03%#5.1f%%\02sy,\03%#5.1f%%\02ni,\03%#5.1f%%\02id,\03%#5.1f%%\02wa,\03%#5.1f%%\02hi,\03%#5.1f%%\02si,\03%#5.1f%%\02st\03\n" +#ifdef CASEUP_SUMMK +#define MEMORY_line1 "Mem: \03" \ + " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02buffers\03\n" +#define MEMORY_line2 "Swap:\03" \ + " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02cached\03\n" +#else +#define MEMORY_line1 "Mem: \03" \ + " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02buffers\03\n" +#define MEMORY_line2 "Swap:\03" \ + " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02cached\03\n" +#endif + +// Keyboard Help specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define KEYS_help \ + "Help for Interactive Commands\02 - %s\n" \ + "Window \01%s\06: \01Cumulative mode \03%s\02. \01System\06: \01Delay \03%.1f secs\02; \01Secure mode \03%s\02.\n" \ + "\n" \ + " Z\05,\01B\05 Global: '\01Z\02' change color mappings; '\01B\02' disable/enable bold\n" \ + " l,t,m Toggle Summaries: '\01l\02' load avg; '\01t\02' task/cpu stats; '\01m\02' mem info\n" \ + " 1,I Toggle SMP view: '\0011\02' single/separate states; '\01I\02' Irix/Solaris mode\n" \ + "\n" \ + " f,o . Fields/Columns: '\01f\02' add or remove; '\01o\02' change display order\n" \ + " F or O . Select sort field\n" \ + " <,> . Move sort field: '\01<\02' next col left; '\01>\02' next col right\n" \ + " R,H . Toggle: '\01R\02' normal/reverse sort; '\01H\02' show threads\n" \ + " c,i,S . Toggle: '\01c\02' cmd name/line; '\01i\02' idle tasks; '\01S\02' cumulative time\n" \ + " x\05,\01y\05 . Toggle highlights: '\01x\02' sort field; '\01y\02' running tasks\n" \ + " z\05,\01b\05 . Toggle: '\01z\02' color/mono; '\01b\02' bold/reverse (only if 'x' or 'y')\n" \ + " u . Show specific user only\n" \ + " n or # . Set maximum tasks displayed\n" \ + "\n" \ + "%s" \ + " W Write configuration file\n" \ + " q Quit\n" \ + " ( commands shown with '.' require a \01visible\02 task display \01window\02 ) \n" \ + "Press '\01h\02' or '\01?\02' for help with \01Windows\02,\n" \ + "any other key to continue " \ + "" + +// This guy goes into the help text (maybe) +#define KEYS_help_unsecured \ + " k,r Manipulate tasks: '\01k\02' kill; '\01r\02' renice\n" \ + " d or s Set update interval\n" \ + "" + +// Fields Reorder/Toggle specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions +// note: the leading newline below serves really dumb terminals; +// if there's no 'cursor_home', the screen will be a mess +// but this part will still be functional. +#define FIELDS_current \ + "\n%sCurrent Fields\02: \01 %s \04 for window \01%s\06\n%s " \ + "" + +// Some extra explanatory text which accompanies the Fields display. +// note: the newlines cannot actually be used, they just serve as +// substring delimiters for the 'display_fields' routine. +#define FIELDS_xtra \ + "Flags field:\n" \ + " 0x00000001 PF_ALIGNWARN\n" \ + " 0x00000002 PF_STARTING\n" \ + " 0x00000004 PF_EXITING\n" \ + " 0x00000040 PF_FORKNOEXEC\n" \ + " 0x00000100 PF_SUPERPRIV\n" \ + " 0x00000200 PF_DUMPCORE\n" \ + " 0x00000400 PF_SIGNALED\n" \ + " 0x00000800 PF_MEMALLOC\n" \ + " 0x00002000 PF_FREE_PAGES (2.5)\n" \ + " 0x00008000 debug flag (2.5)\n" \ + " 0x00024000 special threads (2.5)\n" \ + " 0x001D0000 special states (2.5)\n" \ + " 0x00100000 PF_USEDFPU (thru 2.4)\n" \ + "" +/* no room, sacrificed this one: 'Killed for out-of-memory' */ +/* " 0x00001000 PF_MEMDIE (2.5)\n" ....................... */ + +// Sort Select specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions +// note: the leading newline below serves really dumb terminals; +// if there's no 'cursor_home', the screen will be a mess +// but this part will still be functional. +#define SORT_fields \ + "\n%sCurrent Sort Field\02: \01 %c \04 for window \01%s\06\n%s " \ + "" + +// Some extra explanatory text which accompanies the Sort display. +// note: the newlines cannot actually be used, they just serve as +// substring delimiters for the 'display_fields' routine. +#define SORT_xtra \ + "Note1:\n" \ + " If a selected sort field can't be\n" \ + " shown due to screen width or your\n" \ + " field order, the '<' and '>' keys\n" \ + " will be unavailable until a field\n" \ + " within viewable range is chosen.\n" \ + "\n" \ + "Note2:\n" \ + " Field sorting uses internal values,\n" \ + " not those in column display. Thus,\n" \ + " the TTY & WCHAN fields will violate\n" \ + " strict ASCII collating sequence.\n" \ + " (shame on you if WCHAN is chosen)\n" \ + "" + +// Colors Help specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define COLOR_help \ + "Help for color mapping\02 - %s\n" \ + "current window: \01%s\06\n" \ + "\n" \ + " color - 04:25:44 up 8 days, 50 min, 7 users, load average:\n" \ + " Tasks:\03 64 \02total,\03 2 \03running,\03 62 \02sleeping,\03 0 \02stopped,\03\n" \ + " Cpu(s):\03 76.5%% \02user,\03 11.2%% \02system,\03 0.0%% \02nice,\03 12.3%% \02idle\03\n" \ + " \01 Nasty Message! \04 -or- \01Input Prompt\05\n" \ + " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \ + " 17284 \10pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bash \10\n" \ + " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R < color -b \07\n" \ + " 11005 \10? \07 9 0 0.0 0:02.50 2852 1008 S amor -ses \10\n" \ + " available toggles: \01B\02 =disable bold globally (\01%s\02),\n" \ + " \01z\02 =color/mono (\01%s\02), \01b\02 =tasks \"bold\"/reverse (\01%s\02)\n" \ + "\n" \ + "Select \01target\02 as upper case letter:\n" \ + " S\02 = Summary Data,\01 M\02 = Messages/Prompts,\n" \ + " H\02 = Column Heads,\01 T\02 = Task Information\n" \ + "Select \01color\02 as number:\n" \ + " 0\02 = black,\01 1\02 = red, \01 2\02 = green,\01 3\02 = yellow,\n" \ + " 4\02 = blue, \01 5\02 = magenta,\01 6\02 = cyan, \01 7\02 = white\n" \ + "\n" \ + "Selected: \01target\02 \01 %c \04; \01color\02 \01 %d \04\n" \ + " press 'q' to abort changes to window '\01%s\02'\n" \ + " press 'a' or 'w' to commit & change another, to commit and end " \ + "" + +// Windows/Field Group Help specially formatted string(s) -- +// see 'show_special' for syntax details + other cautions. +#define WINDOWS_help \ + "Help for Windows / Field Groups\02 - \"Current Window\" = \01 %s \06\n" \ + "\n" \ + ". Use multiple \01windows\02, each with separate config opts (color,fields,sort,etc)\n" \ + ". The 'current' window controls the \01Summary Area\02 and responds to your \01Commands\02\n" \ + " . that window's \01task display\02 can be turned \01Off\02 & \01On\02, growing/shrinking others\n" \ + " . with \01NO\02 task display, some commands will be \01disabled\02 ('i','R','n','c', etc)\n" \ + " until a \01different window\02 has been activated, making it the 'current' window\n" \ + ". You \01change\02 the 'current' window by: \01 1\02) cycling forward/backward;\01 2\02) choosing\n" \ + " a specific field group; or\01 3\02) exiting the color mapping screen\n" \ + ". Commands \01available anytime -------------\02\n" \ + " A . Alternate display mode toggle, show \01Single\02 / \01Multiple\02 windows\n" \ + " G . Choose another field group and make it 'current', or change now\n" \ + " by selecting a number from: \01 1\02 =%s;\01 2\02 =%s;\01 3\02 =%s; or\01 4\02 =%s\n" \ + ". Commands \01requiring\02 '\01A\02' mode\01 -------------\02\n" \ + " g . Change the \01Name\05 of the 'current' window/field group\n" \ + " \01*\04 a , w . Cycle through all four windows: '\01a\05' Forward; '\01w\05' Backward\n" \ + " \01*\04 - , _ . Show/Hide: '\01-\05' \01Current\02 window; '\01_\05' all \01Visible\02/\01Invisible\02\n" \ + " The screen will be divided evenly between task displays. But you can make\n" \ + " some \01larger\02 or \01smaller\02, using '\01n\02' and '\01i\02' commands. Then later you could:\n" \ + " \01*\04 = , + . Rebalance tasks: '\01=\05' \01Current\02 window; '\01+\05' \01Every\02 window\n" \ + " (this also forces the \01current\02 or \01every\02 window to become visible)\n" \ + "\n" \ + "In '\01A\02' mode, '\01*\04' keys are your \01essential\02 commands. Please try the '\01a\02' and '\01w\02'\n" \ + "commands plus the 'G' sub-commands NOW. Press to make 'Current' " \ + "" + + +/*###### For Piece of mind #############################################*/ + + /* just sanity check(s)... */ +#if USRNAMSIZ < GETBUFSIZ +# error "Jeeze, USRNAMSIZ Must NOT be less than GETBUFSIZ !" +#endif + + + +#endif /* _Itop */ diff --git a/uptime.1 b/uptime.1 new file mode 100644 index 0000000..3aae739 --- /dev/null +++ b/uptime.1 @@ -0,0 +1,34 @@ +.\" -*-Nroff-*- +.\" +.TH UPTIME 1 "26 Jan 1993" "Cohesive Systems" "Linux User's Manual" +.SH NAME +uptime \- Tell how long the system has been running. +.SH SYNOPSIS +.B uptime +.br +.BR uptime " [" "\-V" ] +.SH DESCRIPTION +.B uptime +gives a one line display of the following information. +The current time, +how long the system has been running, +how many users are currently logged on, +and the system load averages for the past 1, 5, and 15 minutes. +.sp +This is the same information contained in the header line displayed by +.BR w (1). +.SH FILES +.IR /var/run/utmp " information about who is currently logged on" +.br +.IR /proc " process information" +.SH AUTHORS +.B uptime +was written by Larry Greenfield and +Michael K. Johnson . + +Please send bug reports to +.SH "SEE ALSO" +.BR ps (1), +.BR top (1), +.BR utmp (5), +.BR w (1) diff --git a/uptime.c b/uptime.c new file mode 100644 index 0000000..6a4d8bd --- /dev/null +++ b/uptime.c @@ -0,0 +1,17 @@ +#include +#include +#include "proc/whattime.h" +#include "proc/version.h" + +int main(int argc, char *argv[]) { + if(argc == 1) { + print_uptime(); + return 0; + } + if((argc == 2) && (!strcmp(argv[1], "-V"))) { + display_version(); + return 0; + } + fprintf(stderr, "usage: uptime [-V]\n -V display version\n"); + return 1; +} diff --git a/v b/v new file mode 100755 index 0000000..f23230b --- /dev/null +++ b/v @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with embedded spaces in arguments. +# The "$@" won't break that into 2 arguments. +# +LD_LIBRARY_PATH=proc exec ./vmstat "$@" diff --git a/vmstat.8 b/vmstat.8 new file mode 100644 index 0000000..c059ec0 --- /dev/null +++ b/vmstat.8 @@ -0,0 +1,198 @@ +.\" This page Copyright (C) 1994 Henry Ware +.\" Distributed under the GPL, Copyleft 1994. +.TH VMSTAT 8 "27 July 1994 " "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual" +.SH NAME +vmstat \- Report virtual memory statistics +.SH SYNOPSIS +.ft B +.B vmstat +.RB [ "\-a" ] +.RB [ "\-n" ] +.RI [ delay " [ " count ]] +.br +.B vmstat +.RB [ "\-f" ] +.RB [ "\-s" ] +.RB [ "\-m" ] +.br +.B vmstat +.RB [ "\-S unit"] +.br +.B vmstat +.RB [ "\-d"] +.br +.B vmstat +.RB [ "\-p disk partition"] +.br +.B vmstat +.RB [ "\-V" ] +.SH DESCRIPTION +\fBvmstat\fP reports information about processes, memory, paging, +block IO, traps, and cpu activity. + +The first report produced gives averages since the last reboot. Additional +reports give information on a sampling period of length \fIdelay\fP. +The process and memory reports are instantaneous in either case. + +.SS Options +The \fB-a\fP switch displays active/inactive memory, given a 2.5.41 kernel or better. +.PP +The \fB-f\fP switch displays the number of forks since boot. +This includes the fork, vfork, and clone system calls, and is +equivalent to the total number of tasks created. Each process +is represented by one or more tasks, depending on thread usage. +This display does not repeat. +.PP +The \fB-m\fP displays slabinfo. +.PP +The \fB-n\fP switch causes the header to be displayed only once rather than periodically. +.PP +The \fB-s\fP switch displays a table of various event counters +and memory statistics. This display does not repeat. +.PP +.I delay +is the delay between updates in seconds. If no delay is specified, +only one report is printed with the average values since boot. +.PP +.I count +is the number of updates. If no count is specified and delay is +defined, \fIcount\fP defaults to infinity. +.PP +The \fB-d\fP reports disk statistics (2.5.70 or above required) +.PP +The \fB-p\fP followed by some partition name for detailed statistics (2.5.70 or above required) +.PP +The \fB-S\fP followed by k or K or m or M switches outputs between 1000, 1024, 1000000, or 1048576 bytes +.PP +The \fB-V\fP switch results in displaying version information. +.PP +.SH FIELD DESCRIPTION FOR VM MODE +.SS +.B "Procs" +.nf +r: The number of processes waiting for run time. +b: The number of processes in uninterruptible sleep. +.fi +.PP +.SS +.B "Memory" +.nf +swpd: the amount of virtual memory used. +free: the amount of idle memory. +buff: the amount of memory used as buffers. +cache: the amount of memory used as cache. +inact: the amount of inactive memory. (-a option) +active: the amount of active memory. (-a option) +.fi +.PP +.SS +.B "Swap" +.nf +si: Amount of memory swapped in from disk (/s). +so: Amount of memory swapped to disk (/s). +.fi +.PP +.SS +.B "IO" +.nf +bi: Blocks received from a block device (blocks/s). +bo: Blocks sent to a block device (blocks/s). +.fi +.PP +.SS +.B "System" +.nf +in: The number of interrupts per second, including the clock. +cs: The number of context switches per second. +.if +.PP +.SS +.B "CPU " +These are percentages of total CPU time. +.nf +us: Time spent running non-kernel code. (user time, including nice time) +sy: Time spent running kernel code. (system time) +id: Time spent idle. Prior to Linux 2.5.41, this includes IO-wait time. +wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle. +st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown. + +.PP +.SH FIELD DESCRIPTION FOR DISK MODE +.SS +.B "Reads" +.nf +total: Total reads completed successfully +merged: grouped reads (resulting in one I/O) +sectors: Sectors read successfully +ms: milliseconds spent reading +.fi +.PP +.SS +.B "Writes" +.nf +total: Total writes completed successfully +merged: grouped writes (resulting in one I/O) +sectors: Sectors written successfully +ms: milliseconds spent writing +.fi +.PP +.SS +.B "IO" +.nf +cur: I/O in progress +s: seconds spent for I/O +.fi + +.PP +.SH FIELD DESCRIPTION FOR DISK PARTITION MODE +.nf +reads: Total number of reads issued to this partition +read sectors: Total read sectors for partition +writes : Total number of writes issued to this partition +requested writes: Total number of write requests made for partition + +.fi + +.PP +.SH FIELD DESCRIPTION FOR SLAB MODE +.nf +cache: Cache name +num: Number of currently active objects +total: Total number of available objects +size: Size of each object +pages: Number of pages with at least one active object +totpages: Total number of allocated pages +pslab: Number of pages per slab +.fi + +.SH NOTES +.B "vmstat " +does not require special permissions. +.PP +These reports are intended to help identify system bottlenecks. Linux +.B "vmstat " +does not count itself as a running process. +.PP +All linux blocks are currently 1024 bytes. Old kernels may report +blocks as 512 bytes, 2048 bytes, or 4096 bytes. +.PP +Since procps 3.1.9, vmstat lets you choose units (k, K, m, M) default is K (1024 bytes) in the default mode +.PP +vmstat uses slabinfo 1.1 FIXME +.SH FILES +.ta +.nf +/proc/meminfo +/proc/stat +/proc/*/stat +.fi + +.SH "SEE ALSO" +iostat(1), sar(1), mpstat(1), ps(1), top(1), free(1) +.PP +.SH BUGS +Does not tabulate the block io per device or count the number of system calls. +.SH AUTHORS +.nf +Written by Henry Ware . +Fabian Fr\('ed\('erick (diskstat, slab, partitions...) diff --git a/vmstat.c b/vmstat.c new file mode 100644 index 0000000..f022928 --- /dev/null +++ b/vmstat.c @@ -0,0 +1,689 @@ +// old: "Copyright 1994 by Henry Ware . Copyleft same year." +// most code copyright 2002 Albert Cahalan +// +// 27/05/2003 (Fabian Frederick) : Add unit conversion + interface +// Export proc/stat access to libproc +// Adapt vmstat helpfile +// 31/05/2003 (Fabian) : Add diskstat support (/libproc) +// June 2003 (Fabian) : -S -s & -s -S patch +// June 2003 (Fabian) : -Adding diskstat against 3.1.9, slabinfo +// -patching 'header' in disk & slab +// July 2003 (Fabian) : -Adding disk partition output +// -Adding disk table +// -Syncing help / usage + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/sysinfo.h" +#include "proc/version.h" + +static unsigned long dataUnit=1024; +static char szDataUnit [16]; +#define UNIT_B 1 +#define UNIT_k 1000 +#define UNIT_K 1024 +#define UNIT_m 1000000 +#define UNIT_M 1048576 + +#define VMSTAT 0 +#define DISKSTAT 0x00000001 +#define VMSUMSTAT 0x00000002 +#define SLABSTAT 0x00000004 +#define PARTITIONSTAT 0x00000008 +#define DISKSUMSTAT 0x00000010 + +static int statMode=VMSTAT; + +#define FALSE 0 +#define TRUE 1 + +static int a_option; /* "-a" means "show active/inactive" */ + +static unsigned sleep_time = 1; +static unsigned long num_updates; + +static unsigned int height; // window height +static unsigned int moreheaders=TRUE; + + +///////////////////////////////////////////////////////////////////////// + +static void usage(void) NORETURN; +static void usage(void) { + fprintf(stderr,"usage: vmstat [-V] [-n] [delay [count]]\n"); + fprintf(stderr," -V prints version.\n"); + fprintf(stderr," -n causes the headers not to be reprinted regularly.\n"); + fprintf(stderr," -a print inactive/active page stats.\n"); + fprintf(stderr," -d prints disk statistics\n"); + fprintf(stderr," -D prints disk table\n"); + fprintf(stderr," -p prints disk partition statistics\n"); + fprintf(stderr," -s prints vm table\n"); + fprintf(stderr," -m prints slabinfo\n"); + fprintf(stderr," -S unit size\n"); + fprintf(stderr," delay is the delay between updates in seconds. \n"); + fprintf(stderr," unit size k:1000 K:1024 m:1000000 M:1048576 (default is K)\n"); + fprintf(stderr," count is the number of updates.\n"); + exit(EXIT_FAILURE); +} + +///////////////////////////////////////////////////////////////////////////// + +#if 0 +// produce: " 6 ", "123 ", "123k ", etc. +static int format_1024(unsigned long long val64, char *restrict dst){ + unsigned oldval; + const char suffix[] = " kmgtpe"; + unsigned level = 0; + unsigned val32; + + if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do + val32 = val64; + return sprintf(dst,"%3u ",val32); + } + + while(val64 > 0xffffffffull){ + level++; + val64 /= 1024; + } + + val32 = val64; + + while(val32 > 999){ + level++; + oldval = val32; + val32 /= 1024; + } + + if(val32 < 10){ + unsigned fract = (oldval % 1024) * 10 / 1024; + return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]); + } + return sprintf(dst, "%3u%c ", val32, suffix[level]); +} + + +// produce: " 6 ", "123 ", "123k ", etc. +static int format_1000(unsigned long long val64, char *restrict dst){ + unsigned oldval; + const char suffix[] = " kmgtpe"; + unsigned level = 0; + unsigned val32; + + if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do + val32 = val64; + return sprintf(dst,"%3u ",val32); + } + + while(val64 > 0xffffffffull){ + level++; + val64 /= 1000; + } + + val32 = val64; + + while(val32 > 999){ + level++; + oldval = val32; + val32 /= 1000; + } + + if(val32 < 10){ + unsigned fract = (oldval % 1000) / 100; + return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]); + } + return sprintf(dst, "%3u%c ", val32, suffix[level]); +} +#endif + +//////////////////////////////////////////////////////////////////////////// + +static void new_header(void){ + printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n"); + printf( + "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s\n", + "r","b", + "swpd", "free", a_option?"inact":"buff", a_option?"active":"cache", + "si","so", + "bi","bo", + "in","cs", + "us","sy","id","wa" + ); +} + +//////////////////////////////////////////////////////////////////////////// + +static unsigned long unitConvert(unsigned int size){ + float cvSize; + cvSize=(float)size/dataUnit*((statMode==SLABSTAT)?1:1024); + return ((unsigned long) cvSize); +} + +//////////////////////////////////////////////////////////////////////////// + +static void new_format(void) { + const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n"; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i; + unsigned int hz = Hertz; + unsigned int running,blocked,dummy_1,dummy_2; + jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2], cpu_xxx[2], cpu_yyy[2], cpu_zzz[2]; + jiff duse, dsys, didl, diow, dstl, Div, divo2; + unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; + unsigned int intr[2], ctxt[2]; + unsigned int sleep_half; + unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul; + int debt = 0; // handle idle ticks running backwards + + sleep_half=(sleep_time/2); + new_header(); + meminfo(); + + getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl,cpu_iow,cpu_xxx,cpu_yyy,cpu_zzz, + pgpgin,pgpgout,pswpin,pswpout, + intr,ctxt, + &running,&blocked, + &dummy_1, &dummy_2); + + duse= *cpu_use + *cpu_nic; + dsys= *cpu_sys + *cpu_xxx + *cpu_yyy; + didl= *cpu_idl; + diow= *cpu_iow; + dstl= *cpu_zzz; + Div= duse+dsys+didl+diow+dstl; + divo2= Div/2UL; + printf(format, + running, blocked, + unitConvert(kb_swap_used), unitConvert(kb_main_free), + unitConvert(a_option?kb_inactive:kb_main_buffers), + unitConvert(a_option?kb_active:kb_main_cached), + (unsigned)( (*pswpin * unitConvert(kb_per_page) * hz + divo2) / Div ), + (unsigned)( (*pswpout * unitConvert(kb_per_page) * hz + divo2) / Div ), + (unsigned)( (*pgpgin * hz + divo2) / Div ), + (unsigned)( (*pgpgout * hz + divo2) / Div ), + (unsigned)( (*intr * hz + divo2) / Div ), + (unsigned)( (*ctxt * hz + divo2) / Div ), + (unsigned)( (100*duse + divo2) / Div ), + (unsigned)( (100*dsys + divo2) / Div ), + (unsigned)( (100*didl + divo2) / Div ), + (unsigned)( (100*diow + divo2) / Div ) /* , + (unsigned)( (100*dstl + divo2) / Div ) */ + ); + + for(i=1;ireads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes); + fflush(stdout); + free(disks); + free(partitions); + for(j=1; jreads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes); + fflush(stdout); + free(disks); + free(partitions); + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +static void diskheader(void){ + printf("disk- ------------reads------------ ------------writes----------- -----IO------\n"); + + printf("%5s %6s %6s %7s %7s %6s %6s %7s %7s %6s %6s\n", + " ", "total", "merged","sectors","ms","total","merged","sectors","ms","cur","sec"); + +} + +//////////////////////////////////////////////////////////////////////////// + +static void diskformat(void){ + FILE *fDiskstat; + struct disk_stat *disks; + struct partition_stat *partitions; + unsigned long ndisks,i,j,k; + const char format[]="%-5s %6u %6u %7llu %7u %6u %6u %7llu %7u %6u %6u\n"; + if ((fDiskstat=fopen("/proc/diskstats", "rb"))){ + fclose(fDiskstat); + ndisks=getdiskstat(&disks,&partitions); + for(k=0; k 0) + rows = win.ws_row; + + return rows; +} + +//////////////////////////////////////////////////////////////////////////// + +int main(int argc, char *argv[]) { + char partition[16]; + argc=0; /* redefined as number of integer arguments */ + for (argv++;*argv;argv++) { + if ('-' ==(**argv)) { + switch (*(++(*argv))) { + + case 'V': + display_version(); + exit(0); + case 'd': + statMode |= DISKSTAT; + break; + case 'a': + /* active/inactive mode */ + a_option=1; + break; + case 'f': + // FIXME: check for conflicting args + fork_format(); + exit(0); + case 'm': + statMode |= SLABSTAT; + break; + case 'D': + statMode |= DISKSUMSTAT; + break; + case 'n': + /* print only one header */ + moreheaders=FALSE; + break; + case 'p': + statMode |= PARTITIONSTAT; + if (argv[1]){ + char *cp = *++argv; + if(!memcmp(cp,"/dev/",5)) cp += 5; + snprintf(partition, sizeof partition, "%s", cp); + }else{ + fprintf(stderr, "-p requires an argument\n"); + exit(EXIT_FAILURE); + } + break; + case 'S': + if (argv[1]){ + ++argv; + if (!strcmp(*argv, "k")) dataUnit=UNIT_k; + else if (!strcmp(*argv, "K")) dataUnit=UNIT_K; + else if (!strcmp(*argv, "m")) dataUnit=UNIT_m; + else if (!strcmp(*argv, "M")) dataUnit=UNIT_M; + else {fprintf(stderr, "-S requires k, K, m or M (default is kb)\n"); + exit(EXIT_FAILURE); + } + strcpy(szDataUnit, *argv); + }else {fprintf(stderr, "-S requires an argument\n"); + exit(EXIT_FAILURE); + } + break; + case 's': + statMode |= VMSUMSTAT; + break; + default: + /* no other aguments defined yet. */ + usage(); + } + }else{ + argc++; + switch (argc) { + case 1: + if ((sleep_time = atoi(*argv)) == 0) + usage(); + num_updates = ULONG_MAX; + break; + case 2: + num_updates = atol(*argv); + break; + default: + usage(); + } /* switch */ + } +} + if (moreheaders) { + int tmp=winhi()-3; + height=((tmp>0)?tmp:22); + } + setlinebuf(stdout); + switch(statMode){ + case(VMSTAT): new_format(); + break; + case(VMSUMSTAT): sum_format(); + break; + case(DISKSTAT): diskformat(); + break; + case(PARTITIONSTAT): if(diskpartition_format(partition)==-1) + printf("Partition was not found\n"); + break; + case(SLABSTAT): slabformat(); + break; + case(DISKSUMSTAT): disksum_format(); + break; + default: usage(); + break; + } + return 0; +} + + diff --git a/w.1 b/w.1 new file mode 100644 index 0000000..d57a9b5 --- /dev/null +++ b/w.1 @@ -0,0 +1,84 @@ +.\" -*-Nroff-*- +.\" +.TH W 1 "8 Dec 1993 " " " "Linux User's Manual" +.SH NAME +w \- Show who is logged on and what they are doing. +.SH SYNOPSIS +.B w \- +.RB [ husfV ] +.RI [ user ] +.SH DESCRIPTION +.B "w " +displays information about the users currently on the machine, +and their processes. +The header shows, in this order, the current time, +how long the system has been running, +how many users are currently logged on, +and the system load averages for the past 1, 5, and 15 minutes. +.sp +The following entries are displayed for each user: +login name, the tty name, the remote host, login time, idle time, JCPU, PCPU, +and the command line of their current process. +.sp +The JCPU time is the time used by all processes attached to the tty. It +does not include past background jobs, but does include currently +running background jobs. +.sp +The PCPU time is the time used by the current process, named in the "what" +field. + +.PP +.SH "COMMAND\-LINE OPTIONS" +.TP 0.5i +.B "\-h " +Don't print the header. +.TP 0.5i +.B "\-u " +Ignores the username while figuring out the current process and cpu +times. To demonstrate this, do a "su" and do a "w" and a "w -u". +.TP 0.5i +.B "\-s " +Use the short format. +Don't print the login time, JCPU or PCPU times. +.TP 0.5i +.B "\-f " +Toggle printing the +.B from +(remote hostname) field. The default as +released is for the +.B from +field to not be printed, although your system administrator or +distribution maintainer may have compiled a version in which the +.B from +field is shown by default. +.TP 0.5i +.B "\-V " +Display version information. +.TP 0.5i +.B "user " +Show information about the specified user only. + +.SH FILES +.TP +.I /var/run/utmp +information about who is currently logged on +.TP +.I /proc +process information +.PP + +.SH "SEE ALSO" +.BR free (1), +.BR ps (1), +.BR top (1), +.BR uptime (1), +.BR utmp (5), +.BR who (1) + +.SH AUTHORS +.B w +was re-written almost entirely by Charles Blake, based on the version by Larry +Greenfield and Michael K. Johnson +. + +Please send bug reports to diff --git a/w.c b/w.c new file mode 100644 index 0000000..1b2a0fc --- /dev/null +++ b/w.c @@ -0,0 +1,307 @@ +/* w - show what logged in users are doing. Almost entirely rewritten from + * scratch by Charles Blake circa June 1996. Some vestigal traces of the + * original may exist. That was done in 1993 by Larry Greenfield with some + * fixes by Michael K. Johnson. + * + * Changes by Albert Cahalan, 2002. + */ +#include "proc/version.h" +#include "proc/whattime.h" +#include "proc/readproc.h" +#include "proc/devname.h" +#include "proc/procps.h" +#include "proc/sysinfo.h" +#include "proc/escape.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int ignoreuser = 0; /* for '-u' */ +static proc_t **procs; /* our snapshot of the process table */ + +typedef struct utmp utmp_t; + +#ifdef W_SHOWFROM +# define FROM_STRING "on" +#else +# define FROM_STRING "off" +#endif + +/* Uh... same thing as UT_NAMESIZE */ +#define USERSZ (sizeof u->ut_user) + + +/* This routine is careful since some programs leave utmp strings + * unprintable. Always outputs at least 16 chars padded with spaces + * on the right if necessary. + */ +static void print_host(const char *restrict host, int len) { + const char *last; + int width = 0; + + /* FIXME: there should really be a way to configure this... */ + /* for now, we'll just limit it to the 16 that the libc5 version + * of utmp uses. + */ + if (len > 16) len = 16; + last = host + len; + for ( ; host < last ; host++){ + if (isprint(*host) && *host != ' ') { + fputc(*host, stdout); + ++width; + } else { + break; + } + } + // space-fill, and a '-' too if needed to ensure the column exists + if(width < 16) fputs("- "+width, stdout); +} + +/***** compact 7 char format for time intervals (belongs in libproc?) */ +static void print_time_ival7(time_t t, int centi_sec, FILE* fout) { + if((long)t < (long)0){ /* system clock changed? */ + printf(" ? "); + return; + } + if (t >= 48*60*60) /* > 2 days */ + fprintf(fout, " %2ludays", t/(24*60*60)); + else if (t >= 60*60) /* > 1 hour */ + fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60)); + else if (t > 60) /* > 1 minute */ + fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60); + else + fprintf(fout, " %2lu.%02us", t, centi_sec); +} + +/**** stat the device file to get an idle time */ +static time_t idletime(const char *restrict const tty) { + struct stat sbuf; + if (stat(tty, &sbuf) != 0) + return 0; + return time(NULL) - sbuf.st_atime; +} + +/***** 7 character formatted login time */ +static void print_logintime(time_t logt, FILE* fout) { + char weekday[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, + month [][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" }; + time_t curt; + struct tm *logtm, *curtm; + int today; + + curt = time(NULL); + curtm = localtime(&curt); + /* localtime returns a pointer to static memory */ + today = curtm->tm_yday; + logtm = localtime(&logt); + if (curt - logt > 12*60*60 && logtm->tm_yday != today) { + if (curt - logt > 6*24*60*60) + fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon], + logtm->tm_year % 100); + else + fprintf(fout, " %3s%02d ", weekday[logtm->tm_wday], logtm->tm_hour); + } else { + fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min); + } +} + + +/* This function scans the process table accumulating total cpu times for + * any processes "associated" with this login session. It also searches + * for the "best" process to report as "(w)hat" the user for that login + * session is doing currently. This the essential core of 'w'. + */ +static const proc_t *getproc(const utmp_t *restrict const u, const char *restrict const tty, unsigned long long *restrict const jcpu, int *restrict const found_utpid) { + int line; + proc_t **pptr = procs; + const proc_t *best = NULL; + const proc_t *secondbest = NULL; + unsigned uid = ~0U; + + *found_utpid = 0; + if(!ignoreuser){ + char buf[UT_NAMESIZE+1]; + struct passwd *passwd_data; /* pointer to static data */ + strncpy(buf,u->ut_user,UT_NAMESIZE); + buf[UT_NAMESIZE] = '\0'; + passwd_data = getpwnam(buf); + if(!passwd_data) return NULL; + uid = passwd_data->pw_uid; + /* OK to have passwd_data go out of scope here */ + } + line = tty_to_dev(tty); + *jcpu = 0; + for(; *pptr; pptr++) { + const proc_t *restrict const tmp = *pptr; + if(unlikely(tmp->tgid == u->ut_pid)) { + *found_utpid = 1; + best = tmp; + } + if(tmp->tty != line) continue; + (*jcpu) += tmp->utime + tmp->stime; + secondbest = tmp; + /* same time-logic here as for "best" below */ + if(! (secondbest && tmp->start_time <= secondbest->start_time) ){ + secondbest = tmp; + } + if(!ignoreuser && uid != tmp->euid && uid != tmp->ruid) continue; + if(tmp->tgid != tmp->tpgid) continue; + if(best && tmp->start_time <= best->start_time) continue; + best = tmp; + } + return best ? best : secondbest; +} + + +/***** showinfo */ +static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) { + unsigned long long jcpu; + int ut_pid_found; + unsigned i; + char uname[USERSZ + 1] = "", + tty[5 + sizeof u->ut_line + 1] = "/dev/"; + const proc_t *best; + + for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */ + if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/')) + tty[i+5] = u->ut_line[i]; + else + tty[i+5] = '\0'; + + best = getproc(u, tty + 5, &jcpu, &ut_pid_found); + + /* just skip if stale utmp entry (i.e. login proc doesn't exist). If there + * is a desire a cmdline flag could be added to optionally show it with a + * prefix of (stale) in front of cmd or something like that. + */ + if (!ut_pid_found) + return; + + strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */ + if (formtype) { + printf("%-9.8s%-9.8s", uname, u->ut_line); + if (from) + print_host(u->ut_host, sizeof u->ut_host); + print_logintime(u->ut_time, stdout); + if (*u->ut_line == ':') /* idle unknown for xdm logins */ + printf(" ?xdm? "); + else + print_time_ival7(idletime(tty), 0, stdout); + print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout); + if (best) { + unsigned long long pcpu = best->utime + best->stime; + print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout); + } else + printf(" ? "); + } else { + printf("%-9.8s%-9.8s", u->ut_user, u->ut_line); + if (from) + print_host(u->ut_host, sizeof u->ut_host); + if (*u->ut_line == ':') /* idle unknown for xdm logins */ + printf(" ?xdm? "); + else + print_time_ival7(idletime(tty), 0, stdout); + } + fputs(" ", stdout); + if (likely(best)) { + char cmdbuf[512]; + escape_command(cmdbuf, best, sizeof cmdbuf, &maxcmd, ESC_ARGS); + fputs(cmdbuf,stdout); + } else { + printf("-"); + } + fputc('\n', stdout); +} + +/***** main */ +int main(int argc, char **argv) { + char *user = NULL; + utmp_t *u; + struct winsize win; + int header=1, longform=1, from=1, args, maxcmd=80, ch; + +#ifndef W_SHOWFROM + from = 0; +#endif + + setlocale(LC_ALL, ""); + for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++) + switch (ch) { + case 'h': header = 0; break; + case 'l': longform = 1; break; + case 's': longform = 0; break; + case 'f': from = !from; break; + case 'V': display_version(); exit(0); + case 'u': ignoreuser = 1; break; + default: + printf("usage: w -hlsufV [user]\n" + " -h skip header\n" + " -l long listing (default)\n" + " -s short listing\n" + " -u ignore uid of processes\n" + " -f toggle FROM field (default %s)\n" + " -V display version\n", FROM_STRING); + exit(1); + } + + if ((argv[optind])) + user = (argv[optind]); + + if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) + maxcmd = win.ws_col; + if (maxcmd < 71) { + fprintf(stderr, "%d column window is too narrow\n", maxcmd); + exit(1); + } + maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0); + if (maxcmd < 3) + fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col); + + procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT); + + if (header) { /* print uptime and headers */ + print_uptime(); + printf("USER TTY "); + if (from) + printf("FROM "); + if (longform) + printf(" LOGIN@ IDLE JCPU PCPU WHAT\n"); + else + printf(" IDLE WHAT\n"); + } + + utmpname(UTMP_FILE); + setutent(); + if (user) { + for (;;) { + u = getutent(); + if (unlikely(!u)) break; + if (u->ut_type != USER_PROCESS) continue; + if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from); + } + } else { + for (;;) { + u = getutent(); + if (unlikely(!u)) break; + if (u->ut_type != USER_PROCESS) continue; + if (*u->ut_user) showinfo(u, longform, maxcmd, from); + } + } + endutent(); + + return 0; +} diff --git a/watch.1 b/watch.1 new file mode 100644 index 0000000..41323f4 --- /dev/null +++ b/watch.1 @@ -0,0 +1,87 @@ +.TH WATCH 1 "1999 Apr 3" " " "Linux User's Manual" +.SH NAME +watch \- execute a program periodically, showing output fullscreen +.SH SYNOPSIS +.B watch +.I [\-dhvt] [\-n ] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=] [\-\-no\-title] [\-\-version] +.SH DESCRIPTION +.BR watch +runs +.I command +repeatedly, displaying its output (the first screenfull). This allows you to +watch the program output change over time. By default, the program is run +every 2 seconds; use +.I -n +or +.I --interval +to specify a different interval. +.PP +The +.I -d +or +.I --differences +flag will highlight the differences between successive updates. The +.I --cumulative +option makes highlighting "sticky", presenting a running display of all +positions that have ever changed. The +.I -t +or +.I --no-title +option turns off the header showing the interval, command, and current +time at the top of the display, as well as the following blank line. +.PP +.BR watch +will run until interrupted. +.SH NOTE +Note that +.I command +is given to "sh -c" +which means that you may need to use extra quoting to get the desired effect. +.PP +Note that POSIX option processing is used (i.e., option processing stops at +the first non-option argument). This means that flags after +.I command +don't get interpreted by +.BR watch +itself. +.SH EXAMPLES +.PP +To watch for mail, you might do +.IP +watch \-n 60 from +.PP +To watch the contents of a directory change, you could use +.IP +watch \-d ls \-l +.PP +If you're only interested in files owned by user joe, you might use +.IP +watch \-d 'ls \-l | fgrep joe' +.PP +To see the effects of quoting, try these out +.IP +watch echo $$ +.IP +watch echo '$$' +.IP +watch echo "'"'$$'"'" +.PP +You can watch for your administrator to install the latest kernel with +.IP +watch uname -r +.PP +(Just kidding.) +.SH BUGS +Upon terminal resize, the screen will not be correctly repainted until the +next scheduled update. All +.I --differences +highlighting is lost on that update as well. +.PP +Non-printing characters are stripped from program output. Use "cat -v" as +part of the command pipeline if you want to see them. +.SH AUTHORS +The original +.B watch +was written by Tony Rems in 1991, with mods and +corrections by Francois Pinard. It was reworked and new features added by +Mike Coleman in 1999. diff --git a/watch.c b/watch.c new file mode 100644 index 0000000..5841168 --- /dev/null +++ b/watch.c @@ -0,0 +1,326 @@ +/* watch -- execute a program repeatedly, displaying output fullscreen + * + * Based on the original 1991 'watch' by Tony Rems + * (with mods and corrections by Francois Pinard). + * + * Substantially reworked, new features (differences option, SIGWINCH + * handling, unlimited command length, long line handling) added Apr 1999 by + * Mike Coleman . + * + * Changes by Albert Cahalan, 2002-2003. + */ + +#define VERSION "0.2.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/procps.h" + +#ifdef FORCE_8BIT +#undef isprint +#define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) ) +#endif + +static struct option longopts[] = { + {"differences", optional_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"interval", required_argument, 0, 'n'}, + {"no-title", no_argument, 0, 't'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} +}; + +static char usage[] = + "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=] [--no-title] [--version] \n"; + +static char *progname; + +static int curses_started = 0; +static int height = 24, width = 80; +static int screen_size_changed = 0; +static int first_screen = 1; +static int show_title = 2; // number of lines used, 2 or 0 + +#define min(x,y) ((x) > (y) ? (y) : (x)) + +static void do_usage(void) NORETURN; +static void do_usage(void) +{ + fprintf(stderr, usage, progname); + exit(1); +} + +static void do_exit(int status) NORETURN; +static void do_exit(int status) +{ + if (curses_started) + endwin(); + exit(status); +} + +/* signal handler */ +static void die(int notused) NORETURN; +static void die(int notused) +{ + (void) notused; + do_exit(0); +} + +static void +winch_handler(int notused) +{ + (void) notused; + screen_size_changed = 1; +} + +static char env_col_buf[24]; +static char env_row_buf[24]; +static int incoming_cols; +static int incoming_rows; + +static void +get_terminal_size(void) +{ + struct winsize w; + if(!incoming_cols){ // have we checked COLUMNS? + const char *s = getenv("COLUMNS"); + incoming_cols = -1; + if(s && *s){ + long t; + char *endptr; + t = strtol(s, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)666)) incoming_cols = (int)t; + width = incoming_cols; + snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width); + putenv(env_col_buf); + } + } + if(!incoming_rows){ // have we checked LINES? + const char *s = getenv("LINES"); + incoming_rows = -1; + if(s && *s){ + long t; + char *endptr; + t = strtol(s, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)666)) incoming_rows = (int)t; + height = incoming_rows; + snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height); + putenv(env_row_buf); + } + } + if (incoming_cols<0 || incoming_rows<0){ + if (ioctl(2, TIOCGWINSZ, &w) == 0) { + if (incoming_rows<0 && w.ws_row > 0){ + height = w.ws_row; + snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height); + putenv(env_row_buf); + } + if (incoming_cols<0 && w.ws_col > 0){ + width = w.ws_col; + snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width); + putenv(env_col_buf); + } + } + } +} + +int +main(int argc, char *argv[]) +{ + int optc; + int option_differences = 0, + option_differences_cumulative = 0, + option_help = 0, option_version = 0; + double interval = 2; + char *command; + int command_length = 0; /* not including final \0 */ + + setlocale(LC_ALL, ""); + progname = argv[0]; + + while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0)) + != EOF) { + switch (optc) { + case 'd': + option_differences = 1; + if (optarg) + option_differences_cumulative = 1; + break; + case 'h': + option_help = 1; + break; + case 't': + show_title = 0; + break; + case 'n': + { + char *str; + interval = strtod(optarg, &str); + if (!*optarg || *str) + do_usage(); + if(interval < 0.1) + interval = 0.1; + if(interval > ~0u/1000000) + interval = ~0u/1000000; + } + break; + case 'v': + option_version = 1; + break; + default: + do_usage(); + break; + } + } + + if (option_version) { + fprintf(stderr, "%s\n", VERSION); + if (!option_help) + exit(0); + } + + if (option_help) { + fprintf(stderr, usage, progname); + fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr); + fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr); + fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr); + fputs(" -n, --interval=\t\tseconds to wait between updates\n", stderr); + fputs(" -v, --version\t\t\t\tprint the version number\n", stderr); + fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr); + exit(0); + } + + if (optind >= argc) + do_usage(); + + command = strdup(argv[optind++]); + command_length = strlen(command); + for (; optind < argc; optind++) { + char *endp; + int s = strlen(argv[optind]); + command = realloc(command, command_length + s + 2); /* space and \0 */ + endp = command + command_length; + *endp = ' '; + memcpy(endp + 1, argv[optind], s); + command_length += 1 + s; /* space then string length */ + command[command_length] = '\0'; + } + + get_terminal_size(); + + /* Catch keyboard interrupts so we can put tty back in a sane state. */ + signal(SIGINT, die); + signal(SIGTERM, die); + signal(SIGHUP, die); + signal(SIGWINCH, winch_handler); + + /* Set up tty for curses use. */ + curses_started = 1; + initscr(); + nonl(); + noecho(); + cbreak(); + + for (;;) { + time_t t = time(NULL); + char *ts = ctime(&t); + int tsl = strlen(ts); + char *header; + FILE *p; + int x, y; + int oldeolseen = 1; + + if (screen_size_changed) { + get_terminal_size(); + resizeterm(height, width); + clear(); + /* redrawwin(stdscr); */ + screen_size_changed = 0; + first_screen = 1; + } + + if (show_title) { + // left justify interval and command, + // right justify time, clipping all to fit window width + asprintf(&header, "Every %.1fs: %.*s", + interval, min(width - 1, command_length), command); + mvaddstr(0, 0, header); + if (strlen(header) > (size_t) (width - tsl - 1)) + mvaddstr(0, width - tsl - 4, "... "); + mvaddstr(0, width - tsl + 1, ts); + free(header); + } + + if (!(p = popen(command, "r"))) { + perror("popen"); + do_exit(2); + } + + for (y = show_title; y < height; y++) { + int eolseen = 0, tabpending = 0; + for (x = 0; x < width; x++) { + int c = ' '; + int attr = 0; + + if (!eolseen) { + /* if there is a tab pending, just spit spaces until the + next stop instead of reading characters */ + if (!tabpending) + do + c = getc(p); + while (c != EOF && !isprint(c) + && c != '\n' + && c != '\t'); + if (c == '\n') + if (!oldeolseen && x == 0) { + x = -1; + continue; + } else + eolseen = 1; + else if (c == '\t') + tabpending = 1; + if (c == EOF || c == '\n' || c == '\t') + c = ' '; + if (tabpending && (((x + 1) % 8) == 0)) + tabpending = 0; + } + move(y, x); + if (option_differences) { + chtype oldch = inch(); + char oldc = oldch & A_CHARTEXT; + attr = !first_screen + && ((char)c != oldc + || + (option_differences_cumulative + && (oldch & A_ATTRIBUTES))); + } + if (attr) + standout(); + addch(c); + if (attr) + standend(); + } + oldeolseen = eolseen; + } + + pclose(p); + + first_screen = 0; + refresh(); + usleep(interval * 1000000); + } + + endwin(); + + return 0; +}