commit 03a9b5a30fb773ee4d1568064b14e075ba186c90 Author: csmall <> Date: Fri Feb 1 22:47:29 2002 +0000 procps 010114 diff --git a/.cvsignore.patch b/.cvsignore.patch new file mode 100644 index 00000000..216dc03b --- /dev/null +++ b/.cvsignore.patch @@ -0,0 +1,18 @@ +diff -Naur procps-2.0.6/.cvsignore procps-2.0.7/.cvsignore +--- procps-2.0.6/.cvsignore Wed Dec 31 19:00:00 1969 ++++ procps-2.0.7/.cvsignore Fri Jul 14 16:45:01 2000 +@@ -0,0 +1,14 @@ ++skill ++kill ++oldps ++uptime ++tload ++free ++w ++top ++vmstat ++watch ++snice ++pgrep ++pkill ++sysctl diff --git a/BUGS b/BUGS new file mode 100644 index 00000000..df7ced42 --- /dev/null +++ b/BUGS @@ -0,0 +1,81 @@ +BUG REPORTS + +Please read this file before sending in a bug report or patch. + +Also, PLEASE read the documentation first. 90% of the mail I get +complaining about procps is due to the sender not having read the +documentation! + + +Where to send +============= +Send comments, bug reports, patches, etc., to acahalan@cs.uml.edu + + +What to send +============ +It is much more useful to me if a program really crases to recompile it +with make "CC=gcc -ggdb -O", run it with "gdb prog" and "run" and send +me a stack trace ('bt' command). That said, any bug report is still +better than none. + +It might be nice to get rid of miscellaneous compiler warnings, but +don't bend over backwards to do it. + + +Kernel-Dependent Patches +======================== +If you send me patches which are specific to *running* with a particular +kernel version of /proc, please condition them with the runtime determined +variable `linux_version_code' from libproc/kvers.c. It is the same +number as the macro LINUX_VERSION_CODE for which the kernel /proc fs +code was compiled. + +A macro is provide in libproc/version.h to construct the code from its +components, e.g. + if (linux_version_code < LINUX_VERSION(1,1,30)) + /* tty field is only a minor */ +A startup call to set_linux_version may also be necessary. + +Of course, if a bug is due to a change in kernel file formats, it would +be best to first try to generalize the parsing, since the code is then +more resilient against future change. + +If you send me patches which are specific to *compiling* on a particular +version of Linux include a "#if LINUX_VERSION_CODE > 1*0x10000+3*0x100+54" +markup of the patch so that the package may be compiled with older +kernels as well as the "latest and greatest". LINUX_VERSION_CODE is +#define'd in . + +Note that you should not make patches specific to *compiling* on a +particular version of Linux unless there is nothing else you can do. + +Also unified diffs (diff -u) are my preference, context diffs (diff -c ) +are kind of usable, and standard diffs (diff) are more useless than a +generic text description of what you did. Just use + diff -u oldfile newfile +or + diff -Naur old-procps-dir new-procps-dir +to create your diffs and you will make me happy. Also make sure to +include a description of what the diff is for or I'm likely to ignore +it because of general lack of time... + + +Code Structure +============== +My ultimate goal for this package is to be compilable with any kernel +headers and to be able to run under any kernel's /proc. (Don't bother +telling me that I'm not especially close to my ultimate goal... who +is? :-) + +Anyhow, another goal is to encapsulate *all* parsing dependent on /proc +file formats into the libproc library. If the API is general enough +it can hopefully stabilize and then /proc changes might only require +updating libproc.so. Beyond that having the set of utilities be simple +command lines parsers and output formatters and encapsulating all kernel +divergence in libproc is the way to go. + +Hence if you are submitting a new program or are fixing an old one, keep +in mind that adding files to libproc which encapsulate such things is +more desirable than patching the actual driver program. (well, except +to move it toward the API of the library). diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..a43ea212 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 00000000..92b8903f --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..4be90ecc --- /dev/null +++ b/INSTALL @@ -0,0 +1,73 @@ +INSTALL for procps version 2.0.1 +================================ +Please read the NEWS and BUGS files, also. +========================================== + + +Re-compiling the package +======================== + +You want to examine the first 30 or so lines of the Makefile (up to the +configurability note). The destinations of various things attempt FSSTND +compliance, and are pretty standard. The CC/LD flags and which libraries you +use for curses/termcap are here also. It is all annotated there and in a +readily understood format: directories, program to installdir mapping, +sub-packages to build, and compilation options. A few extra points worth +mentioning are: + + o The SUBDIRS variable is essentially just a list of subdirectories to + perform a recursive make or make install in. Right now that's just + ps; the old xproc has been removed because it was entirely redundant. + o There is also an option to build and link against a libproc.so which + reduces 'ps' and 'top' sizes by a large fraction. It is on by default, + so change the value of SHARED if you want. + o 'make libinstall' will install the library and header files into standard + system directories for developers of /proc utilities. There are no + library man pages yet, but the headers are fairly well documented. + o You may need to change the INCDIRS definition if your system is + not very standard. The current definition has been tested to work + on several different systems, though. + +Once you are satisfied with the top-level Makefile options (and possibly those +in subdirectories) you compile and install the package like so: +(ignore innocuous `rcsid' defined-but-not-used warnings.) + + make distclean # clean-out everything to re-make from scratch + make # takes about 0.75 minutes on a PPro 200 with SCSI + su # for write/chown-perms on sys dirs + make install + ldconfig -v # update ld.so to use new libproc if SHARED=1 + exit + make distclean # remove anything that can be rebuilt + + +Miscellaneous Notes +=================== + +COMPATIBILITY + + This code is intended for use with Linux 2.0.xx and 2.2.xx. + Both libc 5 and libc 6 should work. + +PS/TOP WCHAN FIELD + + In past releases, in order to get WCHAN output for ps, you had to + have a psdatabase for each zImage kernel that you generated from + the vmlinux unstripped binary with /sbin/psupdate. With this release, + you should use the System.map automatically generated by Linus' makefile. + 'ps' and 'top' will first use the contents of an environment variable + "PS_SYSTEM_MAP" and then look for System.map files along + the followin search path: + /boot/System.map-V, /boot/System.map, /lib/modules/V/System.map, + Here 'V' is the output of "uname -r" and looks like "2.2.0". + Don't forget the dash! In those same locations, remove psdatabase* files. + + +PARTIAL INSTALLATION + + If you just want to install one program use the targets + install_progname and install_progname.X (where X is the man section) + instead. E.g. + make install_ps install_ps.1 + Likewise for component packages, e.g. + make install_psmisc diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..3cba70f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,220 @@ +# Makefile for procps. Chuck Blake. +# Portions of this are highly dependent upon features specific to GNU make + +export PREFIX = #proc# prefix for program names + +export DESTDIR = / +export MANDIR = /usr/man +export MAN1DIR = $(DESTDIR)$(MANDIR)/man1 +export MAN5DIR = $(DESTDIR)$(MANDIR)/man5 +export MAN8DIR = $(DESTDIR)$(MANDIR)/man8 +export BINDIR = $(DESTDIR)/bin +export SBINDIR = $(DESTDIR)/sbin +export XBINDIR = $(DESTDIR)/usr/X11R6/bin +export USRBINDIR = $(DESTDIR)/usr/bin +export PROCDIR = $(DESTDIR)/usr/bin# /usr/proc/bin for Solaris devotees +export APPLNK = $(DESTDIR)/etc/X11/applnk/Utilities +export OWNERGROUP = --owner 0 --group 0 +export INSTALLBIN = install --mode a=rx --strip +export INSTALLSCT = install --mode a=rx +export INSTALLMAN = install --mode a=r + +BPROG = kill # -> BINDIR +UPROG = oldps uptime tload free w top vmstat watch skill snice # -> USRBINDIR +PPROG = pgrep pkill# -> PROCDIR +SPROG = sysctl +MAN1 = oldps.1 uptime.1 tload.1 free.1 w.1 top.1 watch.1 skill.1 kill.1 snice.1 pgrep.1 pkill.1 +MAN5 = sysctl.conf.5 +MAN8 = vmstat.8 sysctl.8 +DESKTOP = top.desktop +XSCPT = XConsole # -> XBINDIR + +SUBDIRS = ps # sub-packages to build/install + +# easy to command-line override +export INCDIRS = -I/usr/include/ncurses -I/usr/X11R6/include + +export CC = gcc #-ggdb # this gets compiling and linking :-) +export OPT = -O2 +export CFLAGS = -D_GNU_SOURCE $(OPT) -I$(shell pwd) $(INCDIRS) -W -Wall -Wstrict-prototypes -Wshadow -Wcast-align -Wmissing-prototypes + +export SHARED = 1# build/install both a static and ELF shared library +export SHLIBDIR = $(DESTDIR)/lib# where to install the shared library + +export LDFLAGS = -Wl,-warn-common #-s recommended for ELF systems +#LDFLAGS = -qmagic -s# recommended for a.out systems +#LDFLAGS = -Xlinker -qmagic -s# older a.out systems may need this +#LDFLAGS = -N -s# still older a.out systems use this + +#BFD_CAPABLE = -DBFD_CAPABLE +#AOUT_CAPABLE = #-DAOUT_CAPABLE +#ELF_CAPABLE = #-DELF_CAPABLE + +#LIBBFD = -lbfd -liberty +LIBCURSES = -lncurses# watch is the only thing that needs this +#LIBCURSES = -lcurses -ltermcap# BSD Curses requires termcap +LIBTERMCAP = -lncurses# provides perfectly good termcap support +#LIBTERMCAP = -ltermcap +EXTRALIBS = # -lshadow + +W_SHOWFROM = -DW_SHOWFROM# show remote host users are logged in from. + +#----------------------------------------------------# +# End of user-configurable portion of the Makefile. # +# You should not need to modify anything below this. # +#----------------------------------------------------# +BUILD = $(BPROG) $(UPROG) $(PPROG) $(SPROG) $(SUBDIRS) $(DESKTOP) + +# BUILD LIBRARIES + PROGRAMS +all: $(BUILD) + +# INSTALL PROGRAMS + DOCS +install: $(patsubst %,install_%,$(BUILD) $(XSCPT) $(MAN1) $(MAN5) $(MAN8)) +ifeq ($(SHARED),1) + install $(OWNERGROUP) --mode a=rx $(LIB_TGT) $(SHLIBDIR) +endif + +# INSTALL LIBRARIES + HEADERS (OPTIONAL) +libinstall: + $(MAKE) -C proc install $(LIBPROCPASS) + +clean: + $(RM) -f $(OBJ) $(BPROG) $(UPROG) $(PPROG) $(SPROG) + for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done + +distclean: clean + for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done + $(RM) -f $(OBJ) $(BPROG) $(UPROG) $(SPROG) \ + proc/.depend + + +#-----------------------------------------------------# +# End of user-callable make targets. # +# You should not need to read anything below this. # +#-----------------------------------------------------# + +.PHONY: all install libinstall clean distclean +.PHONY: $(patsubst %,install_%, $(BPROG) $(UPROG) $(SPROG)) +.PHONY: proc ps +.PHONY: $(patsubst %,build_%, proc ps) +.PHONY: $(patsubst %,install_%, proc ps) + +VERSION = $(shell awk '/^%define major_version/ { print $$3 }' < procps.spec) +SUBVERSION = $(shell awk '/^%define minor_version/ { print $$3 }' < procps.spec) +MINORVERSION = $(shell awk '/^%define revision/ { print $$3 }' < procps.spec) + +# Note: LIBVERSION may be less than $(VERSION).$(SUBVERSION).$(MINORVERSION) +# LIBVERSION is only set to current $(VERSION).$(SUBVERSION).$(MINORVERSION) +# when an incompatible change is made in libproc. +LIBVERSION = 2.0.7 +ifdef MINORVERSION +LIBPROCPASS = SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) MINORVERSION=$(MINORVERSION) LIBVERSION=$(LIBVERSION) +else +LIBPROCPASS = SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) LIBVERSION=$(LIBVERSION) +endif + +# libproc setup + +ifeq ($(SHARED),1) + LIB_TGT = proc/libproc.so.$(LIBVERSION) +else + LIB_TGT = proc/libproc.a +endif + +$(LIB_TGT): $(wildcard proc/*.[ch]) + $(MAKE) -C proc `basename $(LIB_TGT)` $(LIBPROCPASS) + +# component package setup -- the pattern should be obvious: A build rule and +# unified executable+documentation install rule. (An extra makefile rule is +# needed for those packages which use Imake.) + +ps: build_ps +build_ps: ; $(MAKE) -C ps +install_ps: ps ; $(MAKE) -C ps install + +# executable dependencies +oldps kill skill snice top w uptime tload free vmstat utmp : $(LIB_TGT) + +# static pattern build/link rules: + +%.o : %.c + $(strip $(CC) $(CFLAGS) -c $^) + +oldps w uptime tload free vmstat utmp pgrep: % : %.o + $(strip $(CC) $(LDFLAGS) -o $@ $< $(LIB_TGT) $(EXTRALIBS)) + + +# special instances of link rules (need extra libraries/objects) + +top: % : %.o + $(strip $(CC) $(LDFLAGS) -o $@ $^ $(LIB_TGT) $(LIBTERMCAP) $(EXTRALIBS)) + +watch: % : %.o + $(strip $(CC) $(SLDFLAGS) -o $@ $< $(LIBCURSES) $(EXTRALIBS)) + + +# special instances of compile rules (need extra defines) +w.o: w.c + $(strip $(CC) $(CFLAGS) $(W_SHOWFROM) -c $<) + +top.o: top.c + $(strip $(CC) $(CFLAGS) -fwritable-strings -c $<) + +skill.o: skill.c + $(strip $(CC) $(CFLAGS) -DSYSV -c $<) + +snice: skill + ln -f skill snice + +kill: skill + ln -f skill kill + +pkill: pgrep + ln -f pgrep pkill + +# static pattern installation rules + +$(patsubst %,install_%,$(BPROG)): install_%: % + $(INSTALLBIN) $< $(BINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(SPROG)): install_%: % + $(INSTALLBIN) $< $(SBINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(UPROG)): install_%: % + $(INSTALLBIN) $< $(USRBINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(PPROG)): install_%: % + $(INSTALLBIN) $< $(PROCDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(XSCPT)): install_%: % + $(INSTALLSCT) $< $(XBINDIR)/$(PREFIX)$< +$(patsubst %,install_%,$(MAN1)) : install_%: % + $(INSTALLMAN) $< $(MAN1DIR)/$(PREFIX)$< +$(patsubst %,install_%,$(MAN5)) : install_%: % + $(INSTALLMAN) $< $(MAN5DIR)/$(PREFIX)$< +$(patsubst %,install_%,$(MAN8)) : install_%: % + $(INSTALLMAN) $< $(MAN8DIR)/$(PREFIX)$< +$(patsubst %,install_%,$(DESKTOP)) : install_%: % + $(INSTALLSCT $< $(APPLNK)/$(PREFIX)$< + +# special case install rules +install_snice: snice install_skill + cd $(USRBINDIR) && ln -f skill snice +install_kill: snice install_skill + cd $(USRBINDIR) && ln -f skill kill +install_pkill: pgrep install_pgrep + cd $(USRBINDIR) && ln -f pgrep pkill + +# Find all the source and object files in this directory + +SRC = $(sort $(wildcard *.c)) +OBJ = $(SRC:.c=.o) + +CVSTAG = ps_$(VERSION)_$(SUBVERSION)_$(MINORVERSION) +FILEVERSION = $(VERSION).$(SUBVERSION).$(MINORVERSION) +dist: archive +archive: + @cvs -Q tag -F $(CVSTAG) + @rm -rf /tmp/procps + @cd /tmp; cvs -Q -d $(CVSROOT) export -r$(CVSTAG) procps || echo GRRRrrrrr -- ignore [export aborted] + @mv /tmp/procps /tmp/procps-$(FILEVERSION) + @cd /tmp; tar czSpf procps-$(FILEVERSION).tar.gz procps-$(FILEVERSION) + @cd /tmp; cp procps-$(FILEVERSION)/procps.lsm procps-$(FILEVERSION).lsm + @rm -rf /tmp/procps-$(FILEVERSION) + @echo "The final archive is /tmp/procps-$(FILEVERSION).tar.gz" diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..b1bcf5ff --- /dev/null +++ b/NEWS @@ -0,0 +1,645 @@ +NEWS: what has changed recently with procps, in reverse cronological order. +Please send bug reports to acahalan@cs.uml.edu + +*** THIS FILE DOES NOT INCLUDE RECENT CHANGES *** + + +NEWS for version 2.0.7 of procps + +SMP support has been added to top. This adds one line to the screen +for every processor. There is also an (off by default) field to show +the last processor that a process used that replaced the long-dead +LIB field. Please send feedback, positive and negative, on these +changes to procps-bugs@redhat.com + +The libproc soname has changed to 2.0.7 because of a structure +change required to add SMP support. + +Two programs inspired by Solaris's /usr/proc/bin collection have +been added: pgrep and pkill. By default, for FSSTND/FHS/LSB compliance, +these are installed in /usr/bin; if you want, you can change PROCDIR +in Makefile to move them. You can also make a symlink from +/usr/proc/bin to /usr/bin if you like. + +The Makefiles have been sanitized a bit more; they are now less messy +than they were. There is less duplication between the Makefiles now. + +The man pages that use tables have been fixed to work work broken +versions of man. + +The old wmconfig file (specific to Red Hat) has been replaced with a +desktop file (common across GNOME and KDE). + +sysctl returns an error code in a condition in which it didn't before, +and handles EOF correctly. + +top now only loads System.map when it is actually going to make use +of it. + +vmstat has its buffer size increased and handles page size dynamically. + +w has its year display fixed to show the year 2000 as 00 instead of 0, +and to try harder to find a process to display. + +watch has a fix for ncurses in recent versions of glibc, and expands +tabs so that they display correctly. + +libproc tries to get the default page size from the system header +files, but still has a fallback. It also has been extended to allow +applications to handle their own error reporting in some important +cases. The pwcache has been expanded in size to correctly handle +user names longer than 8 characters. It has been expanded to +provide the lproc field that shows up as top's LC field. A segfault +when /proc is not mounted was fixed. Missing files will cause +applications to exit with an error code instead of good exit code. +A warning when libproc cannot calculate the HZ value (probably due +to a kernel bug) has been supressed by default because it broke +people's scripts unnecessarily. A 64-bit memory size reading +bug was fixed (/proc/meminfo was read incorrectly). + +A couple of error messages in ps had newlines added to them. ps +only opens System.map when it is going to make use of it. The +full-page error message has been replaced with a shorter usage +message; the full-page command summary is available with --help, +and the usage message tells about --help; the full-page summary +is now no longer an error message and so it is sent to stdout +insteada of stderr. Processes with run times longer than a day +now have their runtime displayed correctly. ruser output was fixed. +An attempt was made to support one more piece of BSD syntax in the +command line arguments, where pids can follow options with no +intervening space. The ps man page was made a bit more +internally consistent and typos fixed. Fixed a segfault when the +PS_FORMAT environment variable was set wrong. + + +NEWS for version 2.0.6 of procps + +Support for large-memory systems has been added. + +LIBVERSION has been incremented because of an incompatible change +in the libproc interface, necessitated by the large-memory support. + +A little more error checking in device idle time calculations. + +Fixed an almost-impossible file descriptor leak in libproc that +occasionally showed up in long-running top sessions. + +The fix for top no longer depending on NR_TASKS in 2.0.5 was +broken; top would die with more than 204 tasks. This has been +fixed. + + +NEWS for version 2.0.5 of procps + +procps can build against the 2.3.18 kernel source; top no longer +depends on NR_TASKS. + +sysctl no longer segfaults with -A; has a few parsing fixes + + +NEWS for version 2.0.4 of procps + +sysctl can save/restore settings using /etc/sysctl.conf file + +top has -p option and N and A commands. + +vmstat doesn't mind interrupt counter overflow on long-running machines + +ps can now sort by PCPU. + + +NEWS for version 2.0.3 of procps + +Time calculations fixed (or at least improved...) for SMP machines. +In particular, hertz is calculated correctly. + +ps doesn't mind terminal resizing happening while it runs. + +sysctl is a nifty new program! Try it! Carefully! :-) + +w PCPU and WHAT output fixed. + +top batch mode now works without a tty (for instance, via rsh). + +new version of watch has a few new features like --differences. + +sessreg removed; it belongs where it has been for a long time, in X. + + +NEWS for version 2.0.2 of procps + +Removed xproc entirely; the only thing left there was XConsole, which +was equivalent to xconsole -file /proc/kmsg. Added an XConsole shell +script which does +exec xconsole -file /proc/kmsg "$@" +so that folks who depend on XConsole won't be left behind. This also +fixes the bug that XConsole effectively removed limitations on which +users were allowed to read /proc/kmsg without root having much choice +in the matter other than to remove XConsole... + +Removed unused psupdate code (still available as part of procps-1.2.x +for anyone who wants to play with it). + +Removed sessreg, as it is included in XFree86 and there is no reason +for the duplication. + +Fixed version number generation so that it happens in one place. I'm +tired of releasing versions that misreport their version number... + + +NEWS for version 2.0.1 of procps + +Reverted my changes that had broken Albert's Unix98 compliance. + +Major bugs fixed: + o ps now returns failure for "ps " + o ps h has reverted to old linux behaviour except in BSD personality; + --headers and --no-headers long options have been added + o watch buffer overrun fixed (no, not a security hole). + +top has -b and -n options added. + + +NEWS for version 2.0 of procps + +Thanks to Albert Cahalan for his rewrite of ps, and for making +the time for new development of ps that I just didn't have. His +research into how ps worked on lots of different UNIX systems +really made this version much more usable for a lot of people. + +Read ALL of Albert's comments regarding his 1.9.0 release. If +you do not, you may be surprised when scripts fail. I've tried +to prepare people for the worst of this by giving out the much-detested + warning: `-' deprecated; use `ps aux', not `ps -aux' +message for roughly a year and a half. + + +NEWS for version 1.9.0 of procps + +The ps command now supports simultaneous BSD and Unix98 syntax. +There have been many other changes in ps. (complete rewrite) + +Red Hat users should check /etc/sysconfig/network-scripts/ifdown-ppp +for the bogus command "ps aul" and change it to "ps axl". + +Scripts that make assumptions about character position are and +were broken because fields may overflow. Scripts should parse by +whitespace and use -o to get the best results. Command names for +swapped out processes are now shown in square brackets instead of +parentheses, as required by the Unix98 standard. This problem can +be avoided entirely by using a SysV format without -f, using the +BSD "c" option, or using the format specifier "comm" with -o. + +Some uses of ps in /etc/rc.d/init.d/functions should probably +eventually be changed to stuff somewhat like the following, which +would eliminate the need for awk: + +PS_PERSONALITY=linux dead=`ps -p $pid -o pid=` +PS_PERSONALITY=linux echo -n `ps -C $1 -o pid=` +PS_PERSONALITY=linux pid=`ps -C $1 -o pid=` + +(or use the 'start-stop-daemon' program that Debian has) + +Obsolete files you may have: /etc/psdatabase, /etc/psdevtab, +~/.psdevtab, /usr/man/man8/psupdate.8 and /usr/sbin/psupdate. + + +NEWS for version 1.2.9 of procps + +psupdate has been REMOVED from the default install. You can put it back +if you want to by removing two comment characters in the Makefile, but +I'm not going to tell you which ones. If you can't figure it out, +there's a good chance that it won't build on your machine and I don't +want the bug reports. One of the main reasons I have removed it is that +it is not necessary on a properly-maintained system (that is, System.map +exists and is correct) and 4/5ths of the bug reports I have fielded in +the past are from users who are not able to maintain a working system +complaining that it doesn't work, despite the fact that the INSTALL file +explains how to build it in different ways and how to disable it. +I will delete without response all email asking me how to build psupdate. + +ps now accepts and ignores the g option for those with fingers tied to +the g key from too much exposure to BSD. :-) + +Should build properly on ultrasparc -- two people sent the patch and +the second time I didn't notice that the patch went in reverted. So +I have now reverted the reverted patch to get the right behavior. + +More reliable command-line parsing in vmstat has been added. If vmstat +didn't die for you before, you won't notice the difference... + +Fixed a bug where arbitrary users might have been able to corrupt the +screen of arbitrary other users running top by creating processes with +weird arguments. + +"ps ce" was missing a space character; fixed. + + +NEWS for version 1.2.8 of procps + +procps.spec now uses buildroot, and can be built by non-root users. +This means that the default install targets had to change in the +makefiles so that the default install does not require root privs +otherwise. Also changed all "bin" ownership to "root" ownership, +as kmem-ps has died out and folks won't be automatically doing +"chmod u+s /bin/ps" and making their systems insecure thereby. + +XConsole no longer installed setuid root by default. This is safer, +if less convenient. Change it if you want to use it as non-root. + +top had disgustingly buggy ^Z handling: you could only suspend once, +and if you had suspended, you could no longer read WCHAN information. +Both bugs are now fixed. + +xcpustate removed -- it was a very old, obsolete version. +Get a new version from ftp://ftp.cs.toronto.edu/pub/jdd/xcpustate + +Now looks for kernel symbols in loadable modules. + +psupdate no longer included in RPM package; the theory is that +systems maintained with RPM probably are reasonably recent and +up-to-date and so psupdate is extraneous on those systems, only +adding an unhelpful dependency on a particular version of +binutils. + +top printed two spaces instead of just one between command-line arguments. + + +NEWS for version 1.2.7 of procps + +Security hole in XConsole fixed. + +Works better with very long uptimes. + +Fixed RPM spec file to have correct libproc soname. + +skill -i works. + +Knows about more major numbers for smart serial boards. + +Fixed some small problems in top. + + +NEWS for version 1.2.6 of procps + +Signal handling in top fixed in several ways. Main effect: suspending +works correctly with glibc now. + +Patch from Linus for change in some kernel structures to 64-bit. +This means that libproc has changed soname, as the proc_t data +structure has changed incompatibly. + +File descriptor leaks in libproc were fixed. + + +NEWS for version 1.2.5 of procps + +Potential security hole fixed: if there was no /etc/psdevtab on +a system, a user could put a link from /tmp/psdevtab to a file +owned by another user, and when that other user ran ps, the +file it pointed to would be killed. This wouldn't work for +root because root would be able to create /etc/psdevtab first, +so arbitrary system files would not be killable. This was fixed +by avoiding psdevtab files that are symlinks or have link counts +higher than 1, and by not looking for /tmp/psdevtab anymore, as +the reason for it to exist is really obsolete. + +w -s output has been fixed. Scripts that depend on w -s output +should be examined. The output was broken enough to warrant this +change; it was extremely buggy. + + +NEWS for version 1.2.4 of procps + +I_WANT_A_BROKEN_PS environment variable turns off usage warning. + +"w " sense corrected. + +argument order problem fixed. + +XConsole doesn't dump core on exit any more (only fclose if the +file is open...) + +NEWS for version 1.2.3 of procps + +psupdate moved to /usr/sbin, since it is no longer necessary. + +Added wmconfig for Red Hat systems (others can use it if they +want; if/when wmconfig becomes standard, I'll move it into the +install target of the makefile). + +Ugly hack to get around a problem some people were getting with +a too-small variable. + +The usage message no longer recommends a deprecated syntax. :-) + +NEWS for version 1.2.2 of procps + +Made procps report its version number correctly. +Fixed typo in w.1 man page. + +NEWS for version 1.2.1 of procps + +New address for bug reports against procps: procps-bugs@redhat.com +(That's not an official service of Red Hat Software, just an alias +for me so that bugs get filed properly where I don't lose them...) + +Better memory checks. +Fixed a file descriptor leak. +In top, the space key updates immediately. +Fixed broken signal code in top. +Fixed broken screen size calculations in top. +Fixed broken user count calculations in top. +Fixed broken time difference calculations in w. +Link libproc against libc explicitly for better co-existance with libc. + +NEWS for version 1.2 of procps + +Original author/maintainer, Michael K. Johnson , +has taken over maintenance again. + +psmisc removed, as it is better maintained separately. xload removed, +as it is better maintained as part of XFree86. mknewpty removed, as +it duplicates /dev/MAKEDEV and MAKEDEV is maintained. + +Support for 2.1.x kernels and for glibc 2.0.5. + +Lots of documentation updates. + +BFD support and shared library enabled by default. + +Binaries are no longer included in the archive -- get them from your +favorite Linux distributor. + +free's display fixed in several ways: no such thing as cached swap, +buffer+ display was incorrectly calculated, and buffer calculations +didn't include cached memory. + +Removed mknewpty, because it wasn't perfect and collides with MAKEDEV. + +NEWS for version 1.01 of procps + +top bugfix release. Fixes memory leak, extra line after loadavg and broken +no-idle mode. Also be a little more aggressive utmp 'from host' filtering in w. + +NEWS for version 1.00 of procps + +Fixed the ps -t without a -a segfault bug. Royal screwup on my part. +Updated libproc parsing routine for the new vsize output as of 1.3.91-ish. +Expanded the room for the FLAGS in ps -l due to high order bits now being set. + +Added a brand new top to the distribution. Highly run-time configurable. The +old top is available for at least a release or so as "top.old". See the new man +page for details, or just run it and type 'h'. This is courtesy of Helmut +Geyer. Thanks Helmut! + +egrep -n '\<(tgetent|cm|top_clrtoeol|top_clrtobot|cl|ho|me|md|mr|tgoto)\>' on +top.c and cleaned up all terminal strings being just dumped to stdout. All such +strings are now tputs'd out with putchar. This should fix a lot of problems +people have been having. + +Also cleaned up non-HZ based references to jiffies -> real time conversion. I +may have missed one or two, though... + +Cleaned up some Makefile things. make distclean; make should really work now. +removed function pointer warnings in xcpustate.c. .depend is properly removed +as are the imake generated makefiles. Shared lib generation bug for libproc +fixed. In general the build should be a lot cleaner, but may still have a glitch +or two. + +Fixed a few (but probably not all) Alpha compatibility bugs dealing with memory +alignment. Please let me know if I missed any, or if you like give me a test +account on some Alpha. I don't have access to any Linux other than x86 which is +notoriously forgiving about unaligned memory accesses. Thanks to Alfred Arnold +(a.arnold@kfa-juelich.de) and Harald Koenig for their help so far with this. + +Completely (well almost) re-wrote 'w'. From on by default, J/PCPU display is +accurate to 0.01s. w and top use readproc, so snap.c has gone away completely. + +'skill' should actually work now, but you may have to use '-c', '-u', etc. to +actually get it to parse the command line correctly. + +*** Significant changes in psdevtab inverse device name resolution +------------------------------------------------------------------ +Extended semantics of the -n (numeric) option to output the full device number +in hexadecimal for the tty field. That way if /etc/psdevtab cannot be created +rescanning the /dev directory can be short-circuited. Fixed incorrect file +creation mode for /etc/psdevtab. I just forgot to alter my original 0 mode to +something reasonable like 0664. Added fallback locations for psdevtab. First +it tries /etc/psdevtab, then /tmp/psdevtab and then $HOME/.psdevtab. Also, +decided to go ahead and create the devtab file with regular old write instead of +the rw mmap. I still read it with mmap, but there should be no trouble with +that even in ancient kernels. Also changed the semantics such that if any +devtab file is found, it is assumed to be correct regardless of the relative +timestamps of /dev and the file. Hopefully all this will avoid any unnecessary +slowness. I'm still willing to reactivate the older somewhat broken code to +do the mapping without any file as a fallback if the file doesn't exist. + +*** Significant changes in psdatabase/WCHAN inverse name resolution +------------------------------------------------------------------- +libproc reads directly from System.map, so psupdate and /etc/psdatabase are no +longer necessary. Hopefully this will make kernel configuration management +simpler requiring only the zImage and System.map, which being the stripped +(compressed) kernel and the symbol table before stripping are natural +complements. This is probably how things should have been all along even going +back to kmem ps days four years ago. Oh well. This change should make dealing +with x86, AXP, Sparc, etc binary formats for kernels a lot easier. All that is +required of System.map is that there be exactly 3 space delimited columns: + "address[single space]anything_with_no_spaces[single space]symbol[newline]" +[ Well, ok, the addresses have to be zero padded so that lexicographic order is +the same as numerical order and the addresses all have to be the same ascii +length and I haven't tested to see how resilient it is to bogus internal records +like multiple newlines in a row. Hopefully aren't editting and commenting their +System.map files. ;-) This could actually work on "sort < /proc/ksyms" also if +we generalize the behavior to work with either two or three columns. ] + +We take advantage of the following files in this order: + PS_SYSTEM_MAP # may only point to System.map, not psdatabase + /boot/System.map-`uname -r`, # Note: this is the preferred path + /boot/System.map + /lib/modules/`uname -r`/System.map + /etc/psdatabase + /boot/psdatabase-`uname -r`, + /boot/psdatabase, + /lib/modules/`uname -r`/psdatabase + +The reason for the /lib/modules/*/file location is that I imagine many people +have enough trouble keeping track of kernel version-dependent files in the +filesystem as it is, so I didn't want to invent a new place. I keep things in +/boot myself and don't usually have more than 8 or so kernel versions, so this +works for me. I know not everyone uses modules, but it's just a search path +folks... In case it isn't obvious from all of the above, this means that +psupdate is no longer necessary. I include it in this release and include +recognition of it because I realize that some distributions may have scripts +which depend on the old semantics. I doubt there will ever be a compelling +reason to not include support for generating or reading the old psdatabase +files, but they aren't necessary anymore if you have the System.map files from +the kernel builds. + +I'm not 100% sold on the ordering of the search path, but I think it makes +sense. If you have reasons why it should be different I may be convincable. +Also I'm interested in exactly what the output of 'nm' looks like on Alpha, +Sparc, etc., architectures. It should be easy enough to adapt the code if it is +basically the same format of <0 padded hex> ... \n. + +Thanks to Helmut Geyer for the idea of living off the System.map file natively +and Mike Dean for the idea of using an approximate binary search instead of +padding out the lines of System.map with tons of spaces to get equal record +lengths. The cost of doing it approximately is mild, mostly consisting of lots +of scans to get to the next or last newline and a logarithmically few extra +steps to get to the desired record. Only an order of magnitude guess for the +record length is necessary. The code is all mine, though, so all bugs are due +to me alone. + +NEWS for version 0.99a of procps +================================ +This is a quick bug-fix release to solve a few thorny problems with my probably +overzealous attempt to use the inline-assembly string.h and a Makefile bug or +two. It also fixes free to +/- the cached column too and makes it ignore the +new extra-pretty non-numeric lines that /proc/meminfo is spitting out. It also +fixes the lack of set_linux_verion() in 'w' that caused no command line to be +displayed. I am still working on a much condensed 'w' that should be a lot +easier to maintain and a readproctree that should be usable by both 'ps', +'pstree', and 'w'. + +Apparently memory-mapped files are pretty broken before the early 1.3.X kernels +so I may have to rewrite devname.c to not use MAP_WRITE to create psdevtab. Some +people have been claiming that /dev changes at boot-up in some rc scripts. I'm +not really sure why they would want to do that. Seems kind of paranoid to +continually re-make /dev/log. Anyhow, I'm open to suggestions for psdevtab +behaviors. I've been thinking a /tmp/psdevtab fallback (with a careful world +readable umask to avoid repetition) or maybe a $HOME/.psdevtab fallback too. + +NEWS for version 0.99 of procps +=============================== +This file is a brief catalog of new features or developments in the package. +For general information about using the programs see their man pages. + +NEW PACKAGES OR PROGRAMS +======================== + +LIBPROC + I've modularized some routines and fixed some long standing bugs. Replaced + the regex() recognition of /proc/PID with a simple check of the first + character of the filename being a digit which should be just as safe. + + Added an opendir/readdir/closedir style interface to the process table. The + new interface seems cleaner, more intuitive and generally more applicable + (to me anyway). The only program which uses the new interface is ps. 'w' + will follow soon. 'top' may take a while longer... openproc,readproc, and + closeproc are implemented. I still want to do readproctab and rewindproc, + too though. + + Added some kernel and package versioning things to make it easier to be run + time adaptable. Also updated sysinfo to understand any /proc/meminfo. A + /proc/stat parser should probably be in there as well with the appropriate + updates to vmstat and xcpustate. + + The general direction procps should move in is lightweight command-line or + X11/Motif display/format programs and compartmentalized libproc routines to + parse all of the /proc files. This isolates the utilities from kernel + versioning. + +TTY DEVICE NUMBER TO NAME RESOLUTION + Tty device name <-> number mapping has been completely generalized. It now + stat's every character special file in /dev and builds a memory mapped table + of device names indexed in a way that makes lookup of name from number a + fast, constant time process. The extra overhead incurred by building + /etc/psdevtab is non-negligible if you have a large /dev and permissions to + write the file (or its directory) are required to update the file (which is + done if it does not exist or if /dev is newer than /etc/psdevtab). + + Hence `root' should `ps' shortly after any modification to /dev (or chmod + 666 /etc/psdevtab :-) to avoid ordinary users rebuilding it over and over. + Since such modifications are rare, hopefully having a fallback $HOME + location will not be necessary. If the file is up to date, the overhead + incurred is very small. The generality bought is essentially optimal since + `ps' tailors its notion of name<->devno mapping according to the /dev of the + local system which is the canonical repository of this information. + + In principle the name database could encompass all device majors. The file + would be large, but since I use mmap to access it, only the pages with the + major of interest are ever actually read off the disk. Right now I just use + the majors 2,3,4,5,19,20 which should cover both old and new systems with + both master and slave devices (I know... no reason for the masters... :-) + and the multiport serial devices. Also the 'mknewpty' script is provided + to update your /dev directory to the new pty master/slave devices. + + The tty abbreviation scheme has been rationalized to match device special + files. The leading "tty" or "cu" is stripped, so cua3 -> a3, tty1 -> 1 and + ttyp9 -> p9. The t flag in ps now works with a full device names and to + pick up processes even if they aren't owned by the owner of ps, e.g. + "ps tcua0" picks up gpm for everyone. This seems desirable. + +WATCH + A little program similar to another called 'vis' which simply re-displays + in a polling fashion the output of other programs. "watch ps --sort:utime" + might be dubbed a poor man's 'top'. Though this has been included in procps + for some time it hasn't been built or installed by default. It is now. + +SKILL/SNICE + I have written the necessary machine-dependant file for 'skill' and tested + it somewhat under Linux. It seems to mostly work, but there are probably a + few glitches. This is a generalization of the 'killall' concept. You can + send signals or change priority based upon user, command basename (the same + that 'ps c' gives), terminal, etc. If you have a user named 'satan' "skill + -u satan" will kill all their processes. :-) See the man page for more + details. + + An annoyance of the current implementation is that although permission to + send signals is based upon the real user id, /proc only gives the effective + uids of processes. Hence processes which you *could* kill because they're + suid-root (X say) won't be detected as kill-able. Either /proc + readproc + need to be updated to report the *real* uid to skil or skill needs to try to + send the signal even if the uid doesn't match. + + +CHANGES TO OLD PROGRAMS +======================= + +MAKEFILE + The directory hierarchy has been restructured. It is now easier to have the + multiple components to the suite under nearly autonomous administration. + The library code has also been moved to a subdirectory. The best thing + about the new setup is that things like Imake generated Makefiles with + preconceived notions of 'make install' can be used without getting into the + business of re-writing component package makefiles. + + There is now an option to build a shared libproc.a which reduces 'ps' and + 'top' sizes by about 10K apiece. Simply change the value of SHARED. Also, + one may optionally install the library header files and archive/shared + object files into standard system directories. There are no library man + pages yet, but the headers are fairly descriptive. + +PS + Several long standing bugs have been fixed and much of the internal code was + re-written to use my new directory-style interface to the process table. In + particular if sorting is disabled (with '-o') the process entries are output + to the terminal as soon as possible (making it more helpful under heavy + system load). + + I am considering several new additional features to `ps' including + regex filtering of which processes to list, + "grep -s" silent testing for existing of processes matching criteria, + run-time/user-defined output formats. + I would also like to completely phase the w,top code which uses the snapshot + interface instead of the 'readproc' interface. And of course adding long + options for the rest of the options would be nice too (I may not get around + to doing this anytime really soon so patches which implement any of these + things would most likely be gleefully accepted). + +PSUPDATE + psupdate has been updated to work with ELF kernels. If you compile it as + an ELF binary it will handle both a.out and ELF kernels. If you compile it + as an a.out binary it will only handle a.out kernels. Many thanks to Jeff + Uphoff. + + psupdate has also been updated to work with any binary type supported + by libbfd. This is the default configuration. + Many thanks to David Mossberger-Tang. + +TOP + A user-defined format would be nice here too. Alternate sorting criteria + (top memory users instead of top CPU users, etc.) may be another interesting + alternative. Of course the sorting in ps can do all of that, but it doesn't + have any optimal screen update action going down... :-) diff --git a/TODO b/TODO new file mode 100644 index 00000000..a66d295b --- /dev/null +++ b/TODO @@ -0,0 +1,176 @@ +-------------------------- general ------------------------ + +Implement /usr/proc/bin tools like Solaris has. + +The main Makefile is broken. Try 'touch top.h' to see. +The Makefile is too clever, so it's still not fixed. + +Don't these really belong in the procps package? + killall pstree fuser lsof who +(they are maintained elsewhere, which causes version problems) + +Cache results of dev_to_tty. + +---------------------- kernel ------------------------- + +Add an "adopted child" flag to mark processes that are not +natural children of init. This can make --forest work better. + +Add a thread group ID, to be shared by all tasks that are related by +the clone() system call. This ID might be made unique from boot to +shutdown, perhaps being a 16-bit CPU number and 48-bit per-CPU +serial number. + +Make the kernel group /proc listing output by thread group. +Without this, a thread-aware ps must always sort processes. + +Supply the task ID (the "PID"/"TID") of the thread group leader. +I define "leader" as the first process of a thread group. + +Don't reuse the task ID of a thread group leader until all threads +are dead. Better yet, don't let the leader exit. + +Supply better data for top's CPU state display. Currently top has +to subtract old numbers from new numbers and divide that result by +the number of processors. The kernel won't even supply the number +of processors in a portable way. + +Mark threads, and supply a list of other threads. + +Supply data for the ADDR and JOBC fields. + +Support & supply data for SL and RE. + +Add a /proc/*/tty symlink to eliminate guessing when /proc/*/fd is +not accessable. + +Put unique ID at the top of System.map and in /proc, to make sure +there is never a mismatch. + +Maybe just put the symbols in the kernel like some other systems do. +Raul Miller writes: You might want to implement this as user data which just +happens to come from a wierd source -- instead of just freeing the memory +(like happens with init code), you want it to wind up in the buffer cache. + +Add /proc/*/.bindata files to avoid string parsing. It should be an array +of 64-bit values on all machines. New entries go on the end and obsolete +ones get filled in with something logical -- entries must never be deleted! + +Add all the stuff Solaris has. This would also replace ptrace. + +The kernel should report these task_struct members: + has_cpu + processor + +---------------------- w -------------------------- + +The LOGIN@ column sometimes has a space in it. This makes correct +scripting difficult. + +Verify that DNS control does not give a user the power to specify +arbitrary data for the FROM column. (could set root's VGA color map!) + +---------------------- vmstat -------------------------- + +Extract /proc/stat parsing from vmstat into libproc somewhere. + +--------------------- libproc ---------------------- + +Stop storing fields with duplicate info (often different +units: kB and pages, seconds and jiffies) in the proc_t struct. + +---------------------- top ------------------------- + +Share more stuff with ps. + +---------------- ps for now, maybe move to libproc ------------------ + +With forest output and a tty named /dev/this_is_my_tty, the position +of the command name gets messed up. (we print too many spaces) (fixed?) + +Fix missing stuff for these formats: FB_j FB_l FB_v HP_f HP_*_ HP_fl JFMT OL_m +(jobc,cpu,sl,re,cpu,prmgrp,cp,m_size,m_swap,m_share,vm_lib,m_dt) +Note that "cpu" has two meanings. + +Add Beowulf support. This is ugly, since the current patches use a +daemon to collect info and add a HOST field after the PID field. + +Query optimizer, put cheap/required process selection first. + +Avoid reading both /proc/*/status and /proc/*/stat. Actually, avoid +reading lots of stuff! Avoidance got disabled. (at least don't read the +environment!!!) + +Maybe ps should put a 'C' in front of fields when they are affected +by cumulative mode. Debian ps did that. (move flag to common.h...) + +Support printing the client hostname (the FROM that w(1) uses) in place +of a pty. Maybe do this when PS_PERSONALITY=linux. + +Disgusting color support, for completeness. (Debian ps had it) + +Disambiguate narrow tty info. (/dev/tty7 == /dev/pts/7 now) +1------8 1--4 +ttyS2 S2 +ttyI31 I31 +pts/7 7 Short form could be /999. +pts/9999 9999 Short form could just be trunctuated to /999. +tty7 7 Short form could be vc-7. +tty63 63 Short form could be vc63. + +Internationalization, as specified by XPG3, Volume 1, Commands and Utilities. +(and suggested by Unix98) LC_TIME affects date format. + +----------------------- ps ----------------------- + +Solve the "ps reports wrong date" bug. (recent processes on an SMP system +are reported as being 5 days old, while boot processes look normal) +(reported for procps-2.0.2 in May 1999) + +Show real-time priorities. (type & number) + +Add an option to select all processes that a user can kill. +(related to RUID, EUID, tty, etc. -- but maybe ignore root power) + +Add a nice display option for killing things. +ruser,euser,ppid,pid,pmem,stime,args isn't too bad + +Make the column alignment algorithm support this: + FOO BAR + 8 44444 + 453 45 + 45 2989 + 63666 0 + 34 333 +(useful for the UNIX tty and time values, since the time might look +like 100-10:40:32 for old processes and the tty might have extra room) + +Improve long sort/format specifiers documentation and fill in the missing +code as much as the kernel can support. Make sure that memory amounts are in +pages when they should be and in kB when they should be, not backwards. + +output encoding: UTF8 --nul --null + +Make BSD formats use non-standard BSD time format, at least when it +doesn't violate the "no whitespace" rule. + +Try to make -jl fit in 80 columns. Do we need more than 1000 pty devices, +9 flag bits, etc.? (hmmm, Linux supports 2048 pty devices now, and we +might also want to steal whitespace there when the time column overflows) + +When not in strict Unix98 mode, let foo=8 specify that foo is 8 +characters wide. Debian did that. Then foo=8=bar and foo=bar=8 +could change both header and width. + +Better unmangling of '?' as a tty. The shell destroys '?' when there +is a filename that matches. If the argument seems like garbage, +check for a file that might have screwed up the '?'. + +If the 'O' option is given something already implied by 'O', +assume the user wanted a sorting option. + +Conflict: +Digital THREAD is user,pcpu,pri,scnt,wchan,usertime,systime +AIX THREAD is uname,pid,ppid,tid,S,C,PRI,scount,WCHAN,F,tty,bnd,comm +AIX looks like this: + USER PID PPID TID S C PRI SC WCHAN FLAG TTY BND CMD diff --git a/XConsole b/XConsole new file mode 100755 index 00000000..b4ecf22a --- /dev/null +++ b/XConsole @@ -0,0 +1,5 @@ +#!/bin/sh +# xconsole is capable of doing everything XConsole did, and is maintained... +# it is not setuid root, so we just exit if called as non-root +[ $(id -u) = 0 ] || exit 0 +exec xconsole -file /proc/kmsg "$@" diff --git a/free.1 b/free.1 new file mode 100644 index 00000000..f33b8d10 --- /dev/null +++ b/free.1 @@ -0,0 +1,45 @@ +.\" -*-Nroff-*- +.\" This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu. +.\" Freely distributable under the terms of the GPL +.TH FREE 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual" +.SH NAME +free \- Display amount of free and used memory in the system +.SH SYNOPSIS +.BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s" +.I delay +.RB "] [" "\-t" "] [" "\-V" ] +.SH DESCRIPTION +\fBfree\fP displays the total amount of free and used physical and swap +memory in the system, as well as the buffers used by the kernel. +The shared memory column should be ignored; it is obsolete. +.SS Options +The \fB-b\fP switch displays the amount of memory in bytes; the +\fB-k\fP switch (set by default) displays it in kilobytes; the \fB-m\fP +switch displays it in megabytes. +.PP +The \fB-t\fP switch displays a line containing the totals. +.PP +The \fB-o\fP switch disables the display of a "buffer adjusted" line. Unless +specified \fBfree\fP subtracts/adds buffer memory from/to the used/free memory +reports (respectively!). +.PP +The \fB-s\fP switch activates continuous polling \fIdelay\fP seconds apart. You +may actually specify any floating point number for \fIdelay\fP, +.BR usleep (3) +is used for microsecond resolution delay times. +.PP +The \fB\-V\fP displays version information. +.SH FILES +.ta +.IR /proc/meminfo "\-\- memory information" +.fi + +.SH "SEE ALSO" +.BR ps (1), +.BR top(1) + +.SH AUTHORS +Written by Brian Edmonds. + +Send bug reports to + diff --git a/free.c b/free.c new file mode 100644 index 00000000..c788d79e --- /dev/null +++ b/free.c @@ -0,0 +1,80 @@ +/* free.c - a /proc implementation of free */ +/* Dec14/92 by Brian Edmonds */ +/* Thanks to Rafal Maszkowski for the Total line */ + +#include "proc/sysinfo.h" +#include "proc/version.h" +#include +#include +#include +#include +#include +#include + +#define S(X) ( ((unsigned long long)(X) << 10) >> byteshift) + +static int byteshift = 10; +static int total = 0; + +int main(int argc, char *argv[]){ + int i; + int old_fmt = 0; + int rtime = 0; + + /* check startup flags */ + while( (i = getopt(argc, argv, "bkmos:tV") ) != -1 ) + switch (i) { + case 'b': byteshift = 0; break; + case 'k': byteshift = 10; break; + case 'm': byteshift = 20; break; + case 'o': old_fmt = 1; break; + case 's': rtime = 1000000 * atof(optarg); break; + case 't': total = 1; break; + case 'V': display_version(); exit(0); + default: + fprintf(stderr, "usage: %s [-b|-k|-m] [-o] [-s delay] [-t] [-V]\n", argv[0]); + return 1; + } + + do { + meminfo(); + printf(" total used free shared buffers cached\n"); + printf( + "%-7s %10Ld %10Ld %10Ld %10Ld %10Ld %10Ld\n", "Mem:", + S(kb_main_total), + S(kb_main_used), + S(kb_main_free), + S(kb_main_shared), + S(kb_main_buffers), + S(kb_main_cached) + ); + if(!old_fmt){ + printf( + "-/+ buffers/cache: %10Ld %10Ld\n", + S(kb_main_used-kb_main_buffers-kb_main_cached), + S(kb_main_free+kb_main_buffers+kb_main_cached) + ); + } + printf( + "%-7s %10Ld %10Ld %10Ld\n", "Swap:", + S(kb_swap_total), + S(kb_swap_used), + S(kb_swap_free) + ); + if(total == 1){ + printf( + "%-7s %10Ld %10Ld %10Ld\n", "Total:", + S(kb_main_total + kb_swap_total), + S(kb_main_used + kb_swap_used), + S(kb_main_free + kb_swap_free) + ); + } + if(rtime){ + fputc('\n', stdout); + fflush(stdout); + usleep(rtime); + } + } while(rtime); + + return 0; +} diff --git a/gfinger.p b/gfinger.p new file mode 100644 index 00000000..9ff9b315 --- /dev/null +++ b/gfinger.p @@ -0,0 +1,216 @@ +Return-Path: +Date: Wed, 24 Feb 93 23:37:45 -0500 +From: qpliu@ulch.Princeton.EDU (Quowong P Liu) +To: johnsonm@stolaf.edu +Subject: Suggestions for procps +Reply-To: qpliu@princeton.edu + +[...] + +Since you are collecting programs that use the proc-fs, I have included +some patches to GNU finger that allow it to read the proc information +that it uses. + +[...] + +Here the diffs for GNU finger begin. +-------------BEGIN--------------- +diff -ru finger-1.37.orig/lib/os.c finger-1.37/lib/os.c +--- finger-1.37.orig/lib/os.c Thu Oct 22 17:01:10 1992 ++++ finger-1.37/lib/os.c Mon Feb 8 00:56:32 1993 +@@ -23,7 +23,9 @@ + #include + #include + #include ++#ifndef linux + #include ++#endif + #include + #include + +@@ -43,9 +45,11 @@ + + #ifdef HAVE_PROC_FS + #include ++#ifndef linux + #include + #include + #include ++#endif /*linux*/ + #endif + + #include +@@ -193,6 +197,7 @@ + long idle, current_time, get_last_access (); + char *hostname, *ttyloc, *tty_location (); + #ifdef HAVE_PROC_FS ++#ifndef linux + prpsinfo_t procinfo; + struct procent + { +@@ -202,6 +207,16 @@ + char fname[sizeof (procinfo.pr_fname)]; + } + *proctable, *tableptr; ++#else ++ struct procent ++ { ++ time_t start; ++ int uid; ++ dev_t tty; ++ char fname[15]; ++ } ++ *proctable, *tableptr; ++#endif /* linux */ + int nprocs = 0; + #endif /* HAVE_PROC_FS */ + +@@ -235,6 +250,7 @@ + searching through user list. */ + + #ifdef HAVE_PROC_FS ++#ifndef linux + { + extern char *savedir (); + char *dirstring = savedir ("/proc", 2*1024, &nprocs); +@@ -280,6 +296,64 @@ + free (dirstring); + } + } ++#else /* linux */ ++ { ++ ++ extern char *savedir (); ++ char *dirstring = savedir ("/proc", 2*1024, &nprocs); ++ char *proc; ++ int procfd; ++ char procfile[17], procstatbuffer[1024]; ++ struct stat proc_fs_stat; ++ char procstate; ++ int tmp_tty; /* dev_t is unsigned, -1 means no controlling tty */ ++ ++ /* Allocate process table */ ++ if (nprocs) ++ proctable = (struct procent *) xmalloc (sizeof *proctable * nprocs); ++ ++ /* Walk processes */ ++ if (dirstring && nprocs) ++ { ++ for (tableptr = proctable, proc = dirstring; ++ *proc; proc += strlen (proc) + 1) ++ { ++ sprintf (procfile, "/proc/%.5s/stat", proc); ++ ++ if ((procfd = open (procfile, O_RDONLY)) >= 0 ++ && read (procfd, procstatbuffer, 1024)) ++ { ++ /* see /usr/src/linux/fs/proc/array.c ++ * this is for 0.99.4 ++ */ ++ sscanf (procstatbuffer, "%*d (%14s %c %*d %*d %*d %d " ++ "%*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u " ++ "%*u %hd" /* " %*u %*u %*u %*u %*u %*u %*u %*u %*d %*d " ++ "%*d %*d %*u" */, ++ tableptr->fname, &procstate, &tmp_tty, ++ &tableptr->start); ++ tableptr->fname[strlen(tableptr->fname)-1] = 0; /*trailing )*/ ++ tableptr->tty = tmp_tty; ++ if (fstat (procfd, &proc_fs_stat) == 0) ++ tableptr->uid = proc_fs_stat.st_uid; ++ ++ /* Only collect non-zombies with controlling tty */ ++ if (tmp_tty > 0 && procstate != 'Z') ++ { ++ tableptr++; ++ } ++ else ++ nprocs--; ++ ++ close (procfd); ++ } ++ else ++ nprocs--; ++ } ++ free (dirstring); ++ } ++ } ++#endif /* linux */ + #endif /* HAVE_PROC_FS */ + + +@@ -332,10 +406,14 @@ + + if (stat (device_name, &finfo) != -1) + { ++#ifdef linux ++ dev_t tty = finfo.st_rdev&0xff; ++#else + #ifdef HAVE_ST_RDEV + dev_t tty = finfo.st_rdev; + #else + dev_t tty = finfo.st_dev; ++#endif + #endif + int file; + +diff -ru finger-1.37.orig/lib/packet.c finger-1.37/lib/packet.c +--- finger-1.37.orig/lib/packet.c Wed Oct 21 18:04:38 1992 ++++ finger-1.37/lib/packet.c Mon Feb 8 01:10:00 1993 +@@ -348,7 +348,9 @@ + perror (filename); + } + ++#ifndef linux /* doesn't have fsync (as of .99.4) */ + fsync (file); ++#endif + close (file); + } + +diff -ru finger-1.37.orig/lib/savedir.c finger-1.37/lib/savedir.c +--- finger-1.37.orig/lib/savedir.c Fri Oct 16 21:52:40 1992 ++++ finger-1.37/lib/savedir.c Mon Feb 8 01:06:47 1993 +@@ -132,7 +132,7 @@ + char * + stpcpy (dest, source) + char *dest; +- char *source; ++ const char *source; /* gcc insists on prototype consistency */ + { + while ((*dest++ = *source++) != '\0') + /* Do nothing. */ ; +diff -ru finger-1.37.orig/src/finger.c finger-1.37/src/finger.c +--- finger-1.37.orig/src/finger.c Wed Oct 21 20:41:13 1992 ++++ finger-1.37/src/finger.c Mon Feb 8 00:56:32 1993 +@@ -192,6 +192,9 @@ + long addr; + char *finger_server = NULL; + int suppress_hostname = 0; ++#if defined(linux) /* gethostbyaddr evidently returns pointer to static area */ ++ int do_hostfree = 0; ++#endif /* linux */ + + + username = savestring (arg); +@@ -237,6 +240,9 @@ + { + host = (struct hostent *) xmalloc (sizeof (struct hostent)); + host->h_name = hostname; ++#if defined (linux) ++ do_hostfree = 1; ++#endif /* linux */ + } + } + else +@@ -296,6 +302,11 @@ + if (finger_server) + free (finger_server); + ++#if !defined (linux) + if (host) + free (host); ++#else ++ if (host && do_hostfree) ++ free (host); ++#endif + } +-------------END------------- diff --git a/glibc.patch b/glibc.patch new file mode 100644 index 00000000..5ac7f146 --- /dev/null +++ b/glibc.patch @@ -0,0 +1,32 @@ +From: Nix +Date: Tue, 2 Mar 1999 05:25:08 +0000 (GMT) +Subject: Re: [PATCH] Re: that fputs() thing + + +1999-03-02 Nix + + * stdlib/fputs.c (fputs): Return `len' rather than 0 on success. + +*** 2.1/stdio/fputs.c.orig Tue Mar 2 05:17:12 1999 +--- 2.1/stdio/fputs.c Tue Mar 2 05:18:07 1999 +*************** +*** 27,35 **** + { + const size_t len = strlen (s); + if (len == 1) +! return putc (*s, stream) == EOF ? EOF : 0; + if (fwrite ((void *) s, 1, len, stream) != len) + return EOF; +! return 0; + } + weak_alias (fputs, fputs_unlocked) +--- 27,35 ---- + { + const size_t len = strlen (s); + if (len == 1) +! return putc (*s, stream) == EOF ? EOF : len; + if (fwrite ((void *) s, 1, len, stream) != len) + return EOF; +! return len; + } + weak_alias (fputs, fputs_unlocked) diff --git a/kill.1 b/kill.1 new file mode 100644 index 00000000..66197dfe --- /dev/null +++ b/kill.1 @@ -0,0 +1,96 @@ +,\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for kill. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan; converted to a man page by +.\" Michael K. Johnson +.TH KILL 1 "November 21, 1999" "Linux" "Linux User's Manual" +.SH NAME +kill \- report process status + +.SH SYNOPSIS +.TS +l l. +kill pid ... Send SIGTERM to every process listed. +kill signal pid ... Send a signal to every process listed. +kill -s signal pid ... Send a signal to every process listed. +kill -l List all signal names. +kill -L List all signal names in a nice table. +kill -l signal Convert a signal number into a name. +.TE + +.SH DESCRIPTION +The default signal for kill is TERM. Use -l or -L to list available signals. +Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0. +Alternate signals may be specified in three ways: -9 -SIGKILL -KILL. + +.SH SIGNALS +The signals listed below may be available for use with kill. +When known constant, numbers and default behavior are shown. + +.TS +lB rB lB lB +lfCW r l l. +Name Num Action Description +.TH +ALRM 14 exit +HUP 1 exit +INT 2 exit +KILL 9 exit this signal may not be blocked +PIPE 13 exit +POLL exit +PROF exit +TERM 15 exit +USR1 exit +USR2 exit +VTALRM exit +STKFLT exit may not be implemented +PWR ignore may exit on some systems +WINCH ignore +CHLD ignore +URG ignore +TSTP stop may interact with the shell +TTIN stop may interact with the shell +TTOU stop may interact with the shell +STOP stop this signal may not be blocked +CONT restart continue if stopped, otherwise ignore +ABRT 6 core +FPE 8 core +ILL 4 core +QUIT 3 core +SEGV 11 core +TRAP 5 core +SYS core may not be implemented +EMT core may not be implemented +BUS core core dump may fail +XCPU core core dump may fail +XFSZ core core dump may fail +.TE + +.SH NOTES +Your shell (command line interpreter) may have a built-in kill command. +You may need to run the command described here as /bin/kill to solve +the conflict. + +.SH EXAMPLES +kill -9 -1 +.br +kill -l 11 +.br +kill -L +.br +kill 123 543 2341 3453 + +.SH "SEE ALSO" +top(1) skill(1) kill(2) renice(1) nice(1) + +.SH STANDARDS +This command meets appropriate standards. The -L flag is Linux-specific. + +.SH AUTHOR +Albert Cahalan wrote kill in 1999 to replace the +version that was not standards compliant. Michael K. Johnson + is the current maintainer of the procps collection. + +Please send bug reports to diff --git a/minimal.c b/minimal.c new file mode 100644 index 00000000..0582d348 --- /dev/null +++ b/minimal.c @@ -0,0 +1,486 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* This is a minimal /bin/ps, designed to be smaller than the old ps + * while still supporting some of the more important features of the + * new ps. (for total size, note that this ps does not need libproc) + * It is suitable for Linux-on-a-floppy systems only. + * + * Maintainers: do not compile or install for normal systems. + * Anyone needing this will want to tweak their compiler anyway. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* HZ */ +#include /* PAGE_SIZE */ + +static int P_euid; +static int P_pid; +static char P_cmd[16]; +static char P_state; +static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid; +static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime; +static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value; +static unsigned long P_start_time, P_vsize; +static long P_rss; +static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip; +static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch; +static unsigned long P_wchan, P_nswap, P_cnswap; + + +#if 0 +static int screen_cols = 80; +static int w_count; +#endif + +static int want_one_pid; +static const char *want_one_command; +static int select_notty; +static int select_all; + +static int ps_format; +static int old_h_option; + +/* we only pretend to support this */ +static int show_args; /* implicit with -f and all BSD options */ +static int bsd_c_option; /* this option overrides the above */ + +static int ps_argc; /* global argc */ +static char **ps_argv; /* global argv */ +static int thisarg; /* index into ps_argv */ +static char *flagptr; /* current location in ps_argv[thisarg] */ + + +#ifndef PAGE_SIZE +#warning PAGE_SIZE not defined, assuming it is 4096 +#define PAGE_SIZE 4096 +#endif + +#ifndef HZ +#warning HZ not defined, assuming it is 100 +#define HZ 100 +#endif + + + +static void usage(void){ + fprintf(stderr, + "-C select by command name (minimal ps only accepts one)\n" + "-p select by process ID (minimal ps only accepts one)\n" + "-e all processes (same as ax)\n" + "a all processes w/ tty, including other users\n" + "x processes w/o controlling ttys\n" + "-f full format\n" + "-j,j job control format\n" + "v virtual memory format\n" + "-l,l long format\n" + "u user-oriented format\n" + "-o user-defined format (limited support, only \"ps -o pid=\")\n" + "h no header\n" +/* + "-A all processes (same as ax)\n" + "c true command name\n" + "-w,w wide output\n" +*/ + ); + exit(1); +} + +/* + * Return the next argument, or call the usage function. + * This handles both: -oFOO -o FOO + */ +static const char *get_opt_arg(void){ + const char *ret; + ret = flagptr+1; /* assume argument is part of ps_argv[thisarg] */ + if(*ret) return ret; + if(++thisarg >= ps_argc) usage(); /* there is nothing left */ + /* argument is the new ps_argv[thisarg] */ + ret = ps_argv[thisarg]; + if(!ret || !*ret) usage(); + return ret; +} + + +/* return the PID, or 0 if nothing good */ +static void parse_pid(const char *str){ + char *endp; + int num; + if(!str) goto bad; + num = strtol(str, &endp, 0); + if(*endp != '\0') goto bad; + if(num>0x7fff) goto bad; /* Linux PID limit */ + if(num<1) goto bad; + if(want_one_pid) goto bad; + want_one_pid = num; + return; +bad: + usage(); +} + +/***************** parse SysV options, including Unix98 *****************/ +static void parse_sysv_option(void){ + do{ + switch(*flagptr){ + /**** selection ****/ + case 'C': /* end */ + if(want_one_command) usage(); + want_one_command = get_opt_arg(); + return; /* can't have any more options */ + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + case 'A': + case 'e': + select_all++; + select_notty++; +case 'w': /* here for now, since the real one is not used */ + break; + /**** output format ****/ + case 'f': + show_args = 1; + /* FALL THROUGH */ + case 'j': + case 'l': + if(ps_format) usage(); + ps_format = *flagptr; + break; + case 'o': /* end */ + /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */ + if(strcmp(get_opt_arg(),"pid=")) usage(); + if(ps_format) usage(); + ps_format = 'o'; + old_h_option++; + return; /* can't have any more options */ + /**** other stuff ****/ +#if 0 + case 'w': + w_count++; + break; +#endif + default: + usage(); + } /* switch */ + }while(*++flagptr); +} + +/************************* parse BSD options **********************/ +static void parse_bsd_option(void){ + do{ + switch(*flagptr){ + /**** selection ****/ + case 'a': + select_all++; + break; + case 'x': + select_notty++; + break; + case 'p': /* end */ + parse_pid(get_opt_arg()); + return; /* can't have any more options */ + /**** output format ****/ + case 'j': + case 'l': + case 'u': + case 'v': + if(ps_format) usage(); + ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */ + break; + /**** other stuff ****/ + case 'c': + bsd_c_option++; +#if 0 + break; +#endif + case 'w': +#if 0 + w_count++; +#endif + break; + case 'h': + old_h_option++; + break; + default: + usage(); + } /* switch */ + }while(*++flagptr); +} + +#if 0 +/* not used yet */ +static void choose_dimensions(void){ + struct winsize ws; + char *columns; + /* screen_cols is 80 by default */ + if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col; + columns = getenv("COLUMNS"); + if(columns && *columns){ + long t; + char *endptr; + t = strtol(columns, &endptr, 0); + if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t; + } + if(w_count && (screen_cols<132)) screen_cols=132; + if(w_count>1) screen_cols=999999999; +} +#endif + +static void arg_parse(int argc, char *argv[]){ + int sel = 0; /* to verify option sanity */ + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + /**** iterate over the args ****/ + while(++thisarg < ps_argc){ + flagptr = ps_argv[thisarg]; + switch(*flagptr){ + case '0' ... '9': + show_args = 1; + parse_pid(flagptr); + break; + case '-': + flagptr++; + parse_sysv_option(); + break; + default: + show_args = 1; + parse_bsd_option(); + break; + } + } + /**** sanity check and clean-up ****/ + if(want_one_pid) sel++; + if(want_one_command) sel++; + if(select_notty || select_all) sel++; + if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage(); + if(bsd_c_option) show_args = 0; +} + +/* return 1 if it works, or 0 for failure */ +static int stat2proc(int pid) { + char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */ + int num; + int fd; + char* tmp; + struct stat sb; /* stat() used to get EUID */ + snprintf(buf, 32, "/proc/%d/stat", pid); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0; + num = read(fd, buf, sizeof buf - 1); + fstat(fd, &sb); + P_euid = sb.st_uid; + close(fd); + if(num<80) return 0; + buf[num] = '\0'; + tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "" */ + *tmp = '\0'; /* replace trailing ')' with NUL */ + /* parse these two strings separately, skipping the leading "(". */ + memset(P_cmd, 0, sizeof P_cmd); /* clear */ + sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */ + num = sscanf(tmp + 2, /* skip space after ')' too */ + "%c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %ld %ld " + "%lu %lu " + "%ld " + "%lu %lu %lu %lu %lu %lu " + "%u %u %u %u " /* no use for RT signals */ + "%lu %lu %lu", + &P_state, + &P_ppid, &P_pgrp, &P_session, &P_tty, &P_tpgid, + &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime, + &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_it_real_value, + &P_start_time, &P_vsize, + &P_rss, + &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip, + &P_signal, &P_blocked, &P_sigignore, &P_sigcatch, + &P_wchan, &P_nswap, &P_cnswap + ); +/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + P_vsize /= 1024; + P_rss *= (PAGE_SIZE/1024); + if(num < 30) return 0; + if(P_pid != pid) return 0; + return 1; +} + +static const char *do_time(unsigned long t){ + int hh,mm,ss; + static char buf[32]; + int cnt = 0; + t /= HZ; + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t); + snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss); + return buf; +} + +static const char *do_user(void){ + static char buf[32]; + static struct passwd *p; + static int lastuid = -1; + if(P_euid != lastuid){ + p = getpwuid(P_euid); + if(p) snprintf(buf, sizeof buf, "%-8.8s", p->pw_name); + else snprintf(buf, sizeof buf, "%5d ", P_euid); + } + return buf; +} + +static const char *do_cpu(int longform){ + static char buf[8]; + strcpy(buf," - "); + if(!longform) buf[2] = '\0'; + return buf; +} + +static const char *do_mem(int longform){ + static char buf[8]; + strcpy(buf," - "); + if(!longform) buf[2] = '\0'; + return buf; +} + +static const char *do_stime(void){ + static char buf[32]; + strcpy(buf," - "); + return buf; +} + +static void print_proc(void){ + char tty[16]; + snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty>>8)&0xff, P_tty&0xff); + switch(ps_format){ + case 0: + printf("%5d %s %s", P_pid, tty, do_time(P_utime+P_stime)); + break; + case 'o': + printf("%d\n", P_pid); + return; /* don't want the command */ + case 'l': + printf( + "%03x %c %5d %5d %5d %s %3d %3d - " + "%5ld %06x %s %s", + (unsigned)P_flags&0x777, P_state, P_euid, P_pid, P_ppid, do_cpu(0), + (int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024), + (unsigned)(P_wchan&0xffffff), tty, do_time(P_utime+P_stime) + ); + break; + case 'f': + printf( + "%8s %5d %5d %s %s %s %s", + do_user(), P_pid, P_ppid, do_cpu(0), do_stime(), tty, do_time(P_utime+P_stime) + ); + break; + case 'j': + printf( + "%5d %5d %5d %s %s", + P_pid, P_pgrp, P_session, tty, do_time(P_utime+P_stime) + ); + break; + case 'u'|0x80: + printf( + "%8s %5d %s %s %5ld %4ld %s %c %s %s", + do_user(), P_pid, do_cpu(1), do_mem(1), P_vsize, P_rss, tty, P_state, + do_stime(), do_time(P_utime+P_stime) + ); + break; + case 'v'|0x80: + printf( + "%5d %s %c %s %6d - - %5d %s", + P_pid, tty, P_state, do_time(P_utime+P_stime), (int)P_maj_flt, + (int)P_rss, do_mem(1) + ); + break; + case 'j'|0x80: + printf( + "%5d %5d %5d %5d %s %5d %c %5d %s", + P_ppid, P_pid, P_pgrp, P_session, tty, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime) + ); + break; + case 'l'|0x80: + printf( + "%03x %5d %5d %5d %3d %3d " + "%5ld %4ld %06x %c %s %s", + (unsigned)P_flags&0x777, P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice, + P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, tty, do_time(P_utime+P_stime) + ); + break; + default: + } + if(show_args) printf(" [%s]\n", P_cmd); + else printf(" %s\n", P_cmd); +} + + +int main(int argc, char *argv[]){ + arg_parse(argc, argv); +#if 0 + choose_dimensions(); +#endif + if(!old_h_option){ + const char *head; + switch(ps_format){ + default: /* can't happen */ + case 0: head = " PID TTY TIME CMD"; break; + case 'l': head = " F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break; + case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break; + case 'j': head = " PID PGID SID TTY TIME CMD"; break; + case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break; + case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break; + case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break; + case 'l'|0x80: head = " F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break; + } + printf("%s\n",head); + } + if(want_one_pid){ + if(stat2proc(want_one_pid)) print_proc(); + else exit(1); + }else{ + struct direct *ent; /* dirent handle */ + DIR *dir; + int ouruid; + int found_a_proc; + found_a_proc = 0; + ouruid = getuid(); + dir = opendir("/proc"); + while(( ent = readdir(dir) )){ + if(*ent->d_name<'0' || *ent->d_name>'9') continue; + if(!stat2proc(atoi(ent->d_name))) continue; + if(want_one_command){ + if(strcmp(want_one_command,P_cmd)) continue; + }else{ + if(!select_notty && P_tty==-1) continue; + if(!select_all && P_euid!=ouruid) continue; + } + found_a_proc++; + print_proc(); + } + closedir(dir); + exit(!found_a_proc); + } + return 0; +} diff --git a/oldps.1 b/oldps.1 new file mode 100644 index 00000000..69e6f74f --- /dev/null +++ b/oldps.1 @@ -0,0 +1,356 @@ +.\" This file Copyright 1992, 1997 Michael K. Johnson +.\" and 1996 Charles L. Blake +.\" It may be distributed under the GNU Public License, version 2, or +.\" any higher version. See section COPYING of the GNU Public license +.\" for conditions under which this file may be redistributed. +.TH PS 1 "3 Sep 1997" "Cohesive Systems" "Linux User's Manual" +.SH NAME +ps \- report process status +.SH SYNOPSIS +ps [\fBlujsvmaxScewhrnu\fR] [\fBt\fIxx\fR] \ +[\fBO\fR[\fB+\fR|\fB-\fR]\fIk1\fR[[\fB+\fR|\fB-\fR]\fIk2\fR...]] \ +[\fIpids\fR] + +there are also three long options: + +.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]] + +.B "\-\-help" + +.B "\-\-version" + +More long options are on the way... +.SH DESCRIPTION +.B "ps " +gives a snapshot of the current processes. If you want a repetitive +update of this status, use +.BR top . +This man page documents the +.IR /proc -based +version of +.BR ps , +or tries to. +.PP +.SH "COMMAND-LINE OPTIONS" +The command-line options for this version of +.B ps +are derived from the BSD version of +.BR ps , +not the System V version. + +The command-line arguments should \fBnot\fP be preceeded by a `\-' character, +because in the future, a `\-' will be used to indicate Unix98-standard +command-line arguments, while no `\-' will indicate the current +``extended BSD'' style of command line arguments. + +For now, ps will give you a warning if you use a `\-' for a short option, +but it will still work. If you have shell scripts which use BSD-style +arguments to ps, take heed of the warning and fix them, or else your +scripts will fail to function correctly at some point in the future. +If you want to turn off the warnings, set the +.B I_WANT_A_BROKEN_PS +environment variable. + +There are also some ``long options'' in GNU style; see below for those. +.PP +.PD 0 +.TP 0.5i +.B "l " +long format +.TP 0.5i +.B "u " +user format: gives user name and start time +.TP 0.5i +.B "j " +jobs format: pgid sid +.TP 0.5i +.B "s" +signal format +.TP 0.5i +.B "v " +vm format +.TP 0.5i +.B "m " +displays memory info (combine with +.B p +flag to get number of pages). +.TP 0.5i +.B "f " +"forest" family tree format for command line +.TP 0.5i +.B "a " +show processes of other users too +.TP 0.5i +.B "x " +show processes without controlling terminal +.TP 0.5i +.B "S " +add child cpu time and page faults +.TP 0.5i +.B "c " +command name from task_struct +.TP 0.5i +.B "e " +show environment after command line and ` + ' +.TP 0.5i +.B "w " +wide output: don't truncate command lines to fit on one line. +To be exact, every w that is specified will add another possible +line to the output. If the space isn't needed it isn't used. You +may up to 100 +.BR w 's. +.TP 0.5i +.B "h " +no header +.TP 0.5i +.B "r " +running procs only +.TP 0.5i +.B "n " +numeric output for +.BR USER " and " WCHAN . +.PD 1 +.TP 0.5i +.BI t xx +only procs with controlling tty \fIxx\fR; for \fIxx\fR you may use either the +name of a device file under "/dev" or that name with either +.IR tty " or " cu +sliced off. This is the reverse heuristic that ps uses to print out the +abbreviated tty name in the \fBTT\fR field, e.g. +.BR "ps t1" . +.TP 0.5i +.BR O [ + | - ] \fIk1 [,[ + | - ] \fIk2 [, ... ]] +Order the process listing according to the multi-level sort specified by +the sequence of \fIshort\fR keys from \fBSORT KEYS\fR, \fIk1\fR, \fIk2\fR, ... +Default order specifications exist for each of the various formats of \fBps\fR. +These are over-ridden by a user specified ordering. The `+' is quite optional, +merely re-iterating the default direction on a key. `-' reverses direction only +on the key it precedes. As with \fBt\fR and \fIpids\fR, the O option must be +the last option in a single command argument, but specifications in successive +arguments are catenated. +.TP 0.5i +.I pids +List only the specified processes; they are comma-delimited. The +list must be given immediately after the last option in a single command-line +argument, with no intervening space, e.g. +.BR "ps j1,4,5" . +Lists specified in subsequent arguments are catenated, e.g. +.B ps l 1,2 3,4 5 6 +will list all of the processes 1-6 in long format. If pids are given, they +are listed no matter what. If a tty is given matching processes are listed +no matter what. These two features override the 'a' and 'x' flags. +.SH "LONG COMMAND\-LINE OPTIONS" +These options are preceeded by a double\-hyphen. +.TP 0.5i +.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]] +Choose a \fImulti-letter key\fR from the \fBSORT KEYS\fR section. \fIX\fR may be +any convenient separator character. To be GNU-ish use `='. The `+' is really +optional since default direction is increasing numerical or lexicographic order. +E.g.: +.B ps jax --sort=uid,-ppid,+pid +.TP 0.5i +.B "\-\-help" +Get a help message that summarizes the usage and gives a list of +supported sort keys. This list may be more up to date than this man +page. +.TP 0.5i +.B "\-\-version" +Display version and source of this program. +.SH "SORT KEYS" +Note that the values used in sorting are the internal values \fBps\fR uses and +\fInot\fR the `cooked' values used in some of the output format fields. If +someone wants to volunteer to write special comparison functions for the cooked +values, ... ;-) + +SHORT LONG DESCRIPTION +.PD 0 +.TP 0.5i +c cmd simple name of executable +.TP 0.5i +C cmdline full command line +.TP 0.5i +f flags flags as in long format F field +.TP 0.5i +g pgrp process group ID +.TP 0.5i +G tpgid controlling tty process group ID +.TP 0.5i +j cutime cumulative user time +.TP 0.5i +J cstime cumulative system time +.TP 0.5i +k utime user time +.TP 0.5i +K stime system time +.TP 0.5i +m min_flt number of minor page faults +.TP 0.5i +M maj_flt number of major page faults +.TP 0.5i +n cmin_flt cumulative minor page faults +.TP 0.5i +N cmaj_flt cumulative major page faults +.TP 0.5i +o session session ID +.TP 0.5i +p pid process ID +.TP 0.5i +P ppid parent process ID +.TP 0.5i +r rss resident set size +.TP 0.5i +R resident resident pages +.TP 0.5i +s size memory size in kilobytes +.TP 0.5i +S share amount of shared pages +.TP 0.5i +t tty the minor device number of tty +.TP 0.5i +T start_time time process was started +.TP 0.5i +U uid user ID number +.TP 0.5i +u user user name +.TP 0.5i +v vsize total VM size in bytes +.TP 0.5i +y priority kernel scheduling priority +.PD 1 +.SH "FIELD DESCRIPTIONS" +.TP 0.5i +.B "PRI " +This is the counter field in the task struct. It is the time in +.B HZ +of the process's possible timeslice. +.TP 0.5i +.B "NI " +Standard unix nice value; a positive value means less cpu time. +.TP 0.5i +.B "SIZE " +Virtual image size; size of text+data+stack. +.TP 0.5i +.B "RSS " +Resident set size; kilobytes of program in memory. +.TP 0.5i +.B "WCHAN " +Name of the kernel function where the process is sleeping, with the +.RB ` sys_ ' +stripped from the function name. If +.I /etc/psdatabase +does not exist, it is just a hex number instead. +.TP 0.5i +.B "STAT " +Information about the status of the process. The first field is +.B R +for runnable, +.B S +for sleeping, +.B D +for uninterruptible sleep, +.B T +for stopped or traced, or +.B Z +for a zombie process. The second field contains +.B W +if the process has no resident pages. The third field is +.B N +if the process has a positive nice value +.RB ( NI +field). +.TP 0.5i +.B "TT " +Controlling tty. +.TP 0.5i +.B "PAGEIN " +Number of major page faults (page faults that cause pages to be read +from disk, including pages read from the buffer cache). +.TP 0.5i +.B "TRS " +Text resident size. +.TP 0.5i +.B "SWAP " +Kilobytes (or pages if +.B p +is used) on swap device. +.TP 0.5i +.B "SHARE " +Shared memory. +.SH UPDATING +This +.BR proc -based +.B ps +works by reading the files in the +.B proc +filesystem, mounted on +.BR /proc . +This +.B ps +does not need to be suid +.B kmem +or have any privileges to run. +.I "Do not give this ps any special permissions." +.PP +You will need to put in place the appropriate System.map file +when you install a new kernel in order +to get meaningful information from the +.B WCHAN +field. This should be done every time you compile a new kernel. You should +also run 'ps' as root once and then any time the tty devices in the "/dev" +directory change. + +As of procps-1.00, ps/top read System.map directly if it is available. The +search path for kernel address-to-symbol resolution is: +.nf + $PS_SYSTEM_MAP + /boot/System.map-`uname -r` + /boot/System.map + /lib/modules/`uname -r`/System.map + /etc/psdatabase + /boot/psdatabase-`uname -r` + /boot/psdatabase, + /lib/modules/`uname -r`/psdatabase +.fi +.PP +.SH NOTES +The member +.B used_math +of +.B task_struct +is not shown, since +.B crt0.s +checks to see if math is present. This causes the math flag to be set +for all processes, and so it is worthless. +.PP +Programs swapped out to disk will be shown without command line +arguments, and unless the +.B c +option is given, in parentheses. +.PP +.B %CPU +shows the cputime/realtime percentage. It will not add up to 100% +unless you are lucky. It is time used divided by the time the process +has been running. +.PP +The +.B SIZE +and +.B RSS +fields don't count the page tables and the +.B task_struct +of a proc; this is at least 12k of memory that is always resident. +.B SIZE +is the virtual size of the proc (code+data+stack). +.SH AUTHOR +.B ps +was originally written by Branko Lankester . Michael K. +Johnson re-wrote it significantly to use the proc +filesystem, changing a few things in the process. Michael Shields + added the pid-list feature. Charles +Blake added multi-level sorting, the dirent-style library, the +device name-to-number mmaped database, the approximate binary search directly +on System.map, and many code and documentation cleanups. David Mossberger-Tang +wrote the generic BFD support for psupdate. Michael K. Johnson + is the current maintainer. + +Please send bug reports to diff --git a/oldps.c b/oldps.c new file mode 100644 index 00000000..2ed865f3 --- /dev/null +++ b/oldps.c @@ -0,0 +1,660 @@ +/* + * ps.c - show process status + * + * Copyright (c) 1992 Branko Lankester + * + * Snarfed and HEAVILY modified for procps by Michael K. Johnson, + * (johnsonm@sunsite.unc.edu). What is used is what is required to have a + * common interface. + * + * Massive modifications by Charles Blake (cblake@bbn.com). Rewrite + * of the system process table code, multilevel sorting, device number + * database, forest feature (contributed by ...), environment variables, GNU + * style long options, pid list filtering (contributed by Michael Shields). + * + * Michael K. Johnson again in charge, merging + * patches that have accumulated over a long time. + * + * Changes Copyright (C) 1993, 1994, 1997 Michael K. Johnson, + * and Copyright (C) 1995, 1996 Charles Blake + * See file COPYING for copyright details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static void show_short(char*, proc_t*); +static void show_long (char*, proc_t*); +static void show_user (char*, proc_t*); +static void show_jobs (char*, proc_t*); +static void show_sig (char*, proc_t*); +static void show_vm (char*, proc_t*); +static void show_m (char*, proc_t*); +static void show_regs (char*, proc_t*); + +/* this struct replaces the previously parallel fmt_fnc and hdrs */ +static struct { + void (*format)(char*,proc_t*); + char CL_option_char; + const char* default_sort; + const char* header; +} mode[] = { + { show_short, 0 , "Up", " PID TTY STAT TIME COMMAND" }, + { show_long, 'l', "Pp", " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND" }, + { show_user, 'u', "up", "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND" }, + { show_jobs, 'j', "gPp"," PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND" }, + { show_sig, 's', "p", " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND" }, + { show_vm, 'v', "r", " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND" }, + { show_m, 'm', "r", " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND" }, + { show_regs, 'X', "p", "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND" }, + { NULL, 0, NULL, NULL } +}; + +/* some convenient integer constants corresponding to the above rows. */ +enum PS_MODALITY { PS_D = 0, PS_L, PS_U, PS_J, PS_S, PS_V, PS_M, PS_X }; + +static char * prtime (char *s, unsigned long t, unsigned long rel); +static void usage (char* context); +static void set_cmdspc (int w_opts); +static void show_procs (unsigned maxcmd, int do_header, int, void*,int); +static void show_cmd_env (char* tskcmd, char** cmd, char** env, unsigned maxch); +static void show_a_proc (proc_t* the_proc, unsigned maxcmd); +static void show_time (char *s, proc_t * p); +static void add_node (char *s, proc_t *task); +static int node_cmp (const void *s1, const void *s2); +static void show_tree (int n, int depth, char *continued); +static void show_forest (void); + +static int CL_fmt = 0; + +/* process list filtering command line options */ + +static int CL_all, + CL_kern_comm, + CL_no_ctty, + CL_run_only; +static char * CL_ctty; +static pid_t * CL_pids; /* zero-terminated list, dynamically allocated */ + +/* process display modification command line options */ + +static int CL_show_env, + CL_num_outp, /* numeric fields for user, wchan, tty */ + CL_sort = 1, + CL_forest, + CL_Sum, + CL_pg_shift = (PAGE_SHIFT - 10); /* default: show k instead of pages */ + +/* Globals */ + +static unsigned cmdspc = 80; /* space left for cmd+env after table per row */ +static int GL_current_time; /* some global system parameters */ +static long GL_time_now; +static int GL_wchan_nout; /* this is can also be set on the command-line */ + + +/*****************************/ +int main(int argc, char **argv) { + char *p; + int width = 0, + do_header = 1, + user_ord = 0, + next_arg = 0, + toppid = 0, + pflags, N = 1; + void* args = NULL; + dev_t tty[2] = { 0 }; + uid_t uid[1]; + + do { + --argc; /* shift to next arg. */ + ++argv; + for (p = *argv; p && *p; ++p) { + switch (*p) { + case '-': /* "--" ==> long name options */ + if (*(p+1) == '-') { + if (strncmp(p+2,"sort",4)==0) { + if (parse_long_sort(p+7)) + usage("unrecognized long sort option\n"); + user_ord = 1; + next_arg = 1; + break; + } else if (strncmp(p+2, "help", 4) == 0) { + usage(NULL); + return 0; + } else if (strncmp(p+2, "version", 6) == 0) { + display_version(); + return 0; + } else if (*(p+2) != '\0') /* just '-- '; not an error */ + usage("ps: unknown long option\n"); + } + if (!getenv("I_WANT_A_BROKEN_PS")) { + fprintf(stderr, + "warning: `-' deprecated; use `ps %s', not `ps %s'\n", + (p+1), p); + } + break; + case 'l': CL_fmt = PS_L; /* replaceable by a */ break; + case 'u': CL_fmt = PS_U; /* loop over mode[] */ break; + case 'j': CL_fmt = PS_J; break; + case 's': CL_fmt = PS_S; break; + case 'v': CL_fmt = PS_V; break; + case 'm': CL_fmt = PS_M; break; + case 'X': CL_fmt = PS_X; /* regs */ break; + + case 'r': CL_run_only = 1; /* list filters */ break; + case 'a': CL_all = 1; break; + case 'x': CL_no_ctty = 1; break; + case 't': CL_ctty = p + 1; + next_arg = 1; break; + + case 'e': CL_show_env = 1; /* output modifiers */ break; + case 'f': CL_forest = 1; + CL_kern_comm = 0; break; + case 'c': CL_kern_comm = 1; break; + case 'w': ++width; break; + case 'h': do_header = 0; break; + case 'n': CL_num_outp = 1; + GL_wchan_nout = 1; break; + case 'S': CL_Sum = 1; break; + case 'p': CL_pg_shift = 0; break; + case 'o': CL_sort = !CL_sort; break; + case 'O': + if (parse_sort_opt(p+1)) + usage("short form sort flag parse error\n"); + user_ord = 1; + next_arg = 1; + break; + case 'V': display_version(); exit(0); + default: + /* Step through, reading+alloc space for comma-delim pids */ + if (isdigit(*p)) { + while (isdigit(*p)) { + CL_pids = xrealloc(CL_pids, (toppid + 2)*sizeof(pid_t)); + CL_pids[toppid++] = atoi(p); + while (isdigit(*p)) + p++; + if (*p == ',') + p++; + } + CL_pids[toppid] = 0; + next_arg = 1; + } + if (*p) + usage("unrecognized option or trailing garbage\n"); + } + if (next_arg) { + next_arg = 0; + break; /* end loop over chars in this argument */ + } + } + } while (argc > 1); + + if (!CL_sort) /* since the unsorted mode is intended to be speedy */ + CL_forest = 0; /* turn off the expensive forest option as well. */ + + if (CL_fmt == PS_L) { + if (open_psdb(NULL)) GL_wchan_nout = 1; + } + + set_cmdspc(width); + + if (!(GL_current_time = uptime(0,0))) + return 1; + GL_time_now = time(0L); + + if (CL_sort && !user_ord) + parse_sort_opt(mode[CL_fmt].default_sort); + + /* NOTE: all but option parsing has really been done to enable + * multiple uid/tty/state filtering as well as multiple pid filtering + */ + pflags = PROC_ANYTTY; /* defaults */ + + if (!CL_kern_comm) pflags |= PROC_FILLCMD; /* verbosity flags */ + if (CL_fmt == PS_M) pflags |= PROC_FILLMEM; + if (CL_show_env) pflags |= PROC_FILLENV; + if (!CL_num_outp) pflags |= PROC_FILLUSR; + + if (CL_no_ctty) pflags &= ~PROC_ANYTTY; /* filter flags */ + if (CL_run_only) { pflags |= PROC_STAT; args = "RD"; } + else if (!CL_all) { pflags |= PROC_UID; args = uid; uid[0] = getuid(); pflags &= ~PROC_STAT; } + if (CL_pids) { pflags |= PROC_PID; args = CL_pids; pflags &= ~PROC_UID; pflags &= ~PROC_STAT; } + if (CL_ctty) { + if ((tty[0] = tty_to_dev(CL_ctty)) == (dev_t)-1) { + fprintf(stderr, "the name `%s' is not a tty\n", CL_ctty); + exit(1); + } + pflags = (pflags | PROC_TTY) & ~(PROC_ANYTTY|PROC_STAT|PROC_UID|PROC_PID); + args = tty; + } + show_procs(cmdspc, do_header, pflags, args, N); + return 0; +} + +/***************************/ +/* print a context dependent usage message and maybe exit */ +static void usage(char* context) { + fprintf(stderr, + "%s" + "usage: ps acehjlnrsSuvwx{t|#|O[-]u[-]U..} \\\n" + " --sort:[-]key1,[-]key2,...\n" + " --help gives you this message\n" + " --version prints version information\n", + context ? context : ""); + if (context) + exit(1); /* prevent bad exit status by calling usage(NULL) */ +} + +/*****************************/ +/* set maximum chars displayed on a line based on screen size. + * Always allow for the header, with n+1 lines of output per row. + */ +static void set_cmdspc(int n) { + struct winsize win; + int h = strlen(mode[CL_fmt].header), + c = strlen("COMMAND"); + + if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) + cmdspc = win.ws_col; + if (n > 100) n = 100; /* max of 100 'w' options */ + if (cmdspc > h) + cmdspc = cmdspc*(n+1) - h + c; + else + cmdspc = cmdspc*n + c; +} + +/*****************************/ +static const char *ttyc(int tty, int pid){ + static char outbuf[8]; + if(CL_num_outp) + snprintf(outbuf, sizeof outbuf, "%4.4x", tty); + else + dev_to_tty(outbuf, 4, tty, pid, ABBREV_TTY|ABBREV_DEV|ABBREV_PTS); + return outbuf; +} + +/*****************************/ +/* This is the main driver routine that iterates over the process table. + */ +static void show_procs(unsigned maxcmd, int do_header, int pflags, void* args, int N) { + static proc_t buf; /* less dynamic memory allocation when not sorting */ + PROCTAB* tab; + proc_t **ptable = NULL, *next, *retbuf = NULL; + int n = 0; + + /* initiate process table scan */ + tab = openproc(pflags, args, N); + if (!tab) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + + if (do_header) puts(mode[CL_fmt].header); /* print header */ + + if (!(CL_sort || CL_forest)) /* when sorting and forest are both */ + retbuf = &buf; /* off we can use a static buffer */ + + while ((next = readproc(tab,retbuf))) { /* read next process */ + n++; /* Now either: */ + if (CL_forest) { /* add process to tree */ + /* FIXME: this static is disgusting, but to keep binary + * compatibility within the 1.2 series I'll just go from + * 256 bytes to 1024 bytes, which should be good enough + * for most people.... :-( In the future, the format + * format function should probably dynamically allocate + * strings. + */ + static char s[1024]; + (mode[CL_fmt].format)(s, next); + if (CL_fmt != PS_V && CL_fmt != PS_M) + show_time(s+strlen(s), next); + add_node(s, next); + } else if (CL_sort) { /* add process to table */ + ptable = xrealloc(ptable, n*sizeof(proc_t*)); + ptable[n-1] = next; + } else { /* or show it right away */ + show_a_proc(&buf, maxcmd); + if (buf.cmdline) free((void*)(buf.cmdline[0])); + if (buf.environ) free((void*)(buf.environ[0])); + } + } + if (!n) { + fprintf(stderr, "No processes available.\n"); + exit(1); + } + if (CL_sort && !CL_forest) { /* just print sorted table */ + int i; + qsort(ptable, n, sizeof(proc_t*), (void*)mult_lvl_cmp); + for (i = 0; i < n; i++) { + show_a_proc(ptable[i], maxcmd); + freeproc(ptable[i]); + } + free(ptable); + } else if (CL_forest) + show_forest(); +} + +/*****************************/ +/* show the trailing command and environment in available space. + * use abbreviated cmd if requested, NULL list, or singleton NULL string + */ +static void show_cmd_env(char* tskcmd, char** cmd, char** env, unsigned maxch) { + if (CL_kern_comm) /* no () when explicit request for tsk cmd */ + maxch = print_str(stdout, tskcmd, maxch); + else if (!cmd || !*cmd || (!cmd[1] && !*cmd)) { + /* no /proc//cmdline ==> bounding () */ + if (maxch) { + fputc('(', stdout); + maxch--; + } + maxch = print_str(stdout, tskcmd, maxch); + if (maxch) { + fputc(')', stdout); + maxch--; + } + } else + maxch = print_strlist(stdout, cmd, " ", maxch); + if (CL_show_env && env) + print_strlist(stdout, env, " ", maxch); + fputc('\n', stdout); +} + + +/*****************************/ +/* format a single process for output. + */ +static void show_a_proc(proc_t* p, unsigned maxch) { + static char s[2048]; + (mode[CL_fmt].format)(s, p); + if (CL_fmt != PS_V && CL_fmt != PS_M) + show_time(s+strlen(s), p); + printf("%s", s); + show_cmd_env(p->cmd, p->cmdline, p->environ, maxch); +} + +/****** The format functions for the various formatting modes follow *****/ + +/*****************************/ +static void show_short(char *s, proc_t *p) { + sprintf(s, "%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p)); +} + + +/*****************************/ +static void show_long(char *s, proc_t *p) { + char wchanb[32]; + + if (GL_wchan_nout) + sprintf(wchanb, " %-9lx ", p->wchan & 0xffffffff); + else + sprintf(wchanb, "%-11.11s", wchan(p->wchan)); + sprintf(s, "%6lx %5d %5d %5d %3ld %3ld %6ld %5ld %-11.11s %s%3s", + p->flags, p->euid, p->pid, p->ppid, p->priority, p->nice, + p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), wchanb, status(p), + ttyc(p->tty,p->pid)); +} + + +/*****************************/ +static void show_jobs(char *s, proc_t *p) { + sprintf(s, "%5d %5d %5d %5d %3s %5d %s %5d ", + p->ppid, p->pid, p->pgrp, p->session, ttyc(p->tty,p->pid), p->tpgid, status(p), + p->euid); +} + + +/*****************************/ +static void show_user(char *s, proc_t *p) { + long pmem, total_time, seconds; + time_t start; + unsigned int pcpu; + + if (CL_num_outp) + s += sprintf(s, "%5d ", p->euid); + else + s += sprintf(s, "%-8s ", p->euser); + /* Hertz, being UL, forces 64-bit calculation on 64-bit machines */ + seconds = (((GL_current_time * Hertz) - p->start_time) / Hertz); + start = GL_time_now - seconds; + total_time = (p->utime + p->stime + + (CL_Sum ? p->cutime + p->cstime : 0)); + pcpu = seconds ? + (total_time * 10 * 100 / Hertz) / seconds : + 0; + if (pcpu > 999) pcpu = 999; + pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10)); + sprintf(s, "%5d %2u.%u %2ld.%ld %5ld %5ld %3s %s %.6s ", + p->pid, pcpu / 10, pcpu % 10, pmem / 10, pmem % 10, + p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), ttyc(p->tty,p->pid), status(p), + ctime(&start) + (GL_time_now - start > 3600*24 ? 4 : 10)); +} + + +/*****************************/ +/* TODO: this is broken... but does anyone care? */ +static void show_sig(char *s, proc_t *p) { +#undef SIGNAL_STRING /* not filled in by the function oldps calls */ +#ifdef SIGNAL_STRING +#define TAIL(s) ((s)+(long)strlen(s)-8) + sprintf(s, "%5d %5d %s %s %s %s %s %3s ", + p->euid, p->pid, + TAIL(p->signal), TAIL(p->blocked), TAIL(p->sigignore), TAIL(p->sigcatch), + status(p), ttyc(p->tty,p->pid) + ); +#else + sprintf(s, "%5d %5d %08x %08x %08x %08x %s %3s ", + p->euid, p->pid, + (unsigned)p->signal, (unsigned)p->blocked, (unsigned)p->sigignore, (unsigned)p->sigcatch, + status(p), ttyc(p->tty,p->pid) + ); +#endif +} + + +/*****************************/ +static void show_vm(char *s, proc_t *p) { + int pmem; + + s += sprintf(s,"%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p)); + show_time(s, p); + s += strlen(s); + s += sprintf(s, " %6ld %4ld %4ld %4ld ", + p->maj_flt + (CL_Sum ? p->cmaj_flt : 0), + p->vsize ? (p->end_code - p->start_code) >> 10 : 0, + p->vsize ? (p->vsize - p->end_code + p->start_code) >> 10 : 0, + p->rss << (PAGE_SHIFT - 10)); + if(p->rss_rlim == RLIM_INFINITY) + s += sprintf(s, " xx "); + else + s += sprintf(s, "%5ld ", p->rss_rlim >> 10); + pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10)); + sprintf(s, "%2d.%d ", pmem / 10, pmem % 10); +} + + + +/*****************************/ +static void show_m(char *s, proc_t *p) { + sprintf(s, "%5d %3s %6ld %6ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld %3ld ", + p->pid, ttyc(p->tty,p->pid), + p->maj_flt + (CL_Sum ? p->cmaj_flt : 0), + p->min_flt + (CL_Sum ? p->cmin_flt : 0), + p->trs << CL_pg_shift, + p->drs << CL_pg_shift, + p->size << CL_pg_shift, + (p->size - p->resident) << CL_pg_shift, + p->resident << CL_pg_shift, + p->share << CL_pg_shift, + p->lrs << CL_pg_shift, + p->dt); +} + + +/*****************************/ +static void show_regs(char *s, proc_t *p) { + char time1[16], time2[16]; + + sprintf(s, "%2ld %5d %8lx %8lx %8lx %s %s %s %3s ", + p->start_code >> 26, p->pid, p->start_stack, + p->kstk_esp, p->kstk_eip, + prtime(time1, p->timeout, GL_current_time*Hertz), + prtime(time2, p->it_real_value, 0), + status(p), ttyc(p->tty,p->pid)); +} + + +/*****************************/ +static char *prtime(char *s, unsigned long t, unsigned long rel) { + if (t == 0) { + sprintf(s, " "); + return s; + } + if ((long) t == -1) { + sprintf(s, " xx"); + return s; + } + if ((long) (t -= rel) < 0) + t = 0; + if (t > 9999) + sprintf(s, "%5lu", t / 100); + else + sprintf(s, "%2lu.%02lu", t / 100, t % 100); + return s; +} + + +/*****************************/ +static void show_time(char *s, proc_t * p) { + unsigned t; + t = (p->utime + p->stime) / Hertz; + if (CL_Sum) t += (p->cutime + p->cstime) / Hertz; + sprintf(s, "%3d:%02d ", t / 60, t % 60); +} + + + +/*****************************/ +/* fancy process family tree based cmdline printing. Building the tree + should be relegated to libproc and only the printing logic should + remain here. +*/ +static struct tree_node * node; /* forest mode globals */ +static int nodes = 0; +static int maxnodes = 0; + +/*****************************/ + +static void add_node(char *s, proc_t *task) { + if (maxnodes == 0) { + maxnodes = 64; + node = (struct tree_node *) + xmalloc(sizeof(struct tree_node) * maxnodes); + } + if (nodes >= maxnodes) { + maxnodes *= 2; + node = (struct tree_node *) + xrealloc(node, sizeof(struct tree_node) * maxnodes); + } + node[nodes].proc = task; + node[nodes].pid = task->pid; + node[nodes].ppid = task->ppid; + node[nodes].line = strdup(s); + node[nodes].cmd = task->cmd; + node[nodes].cmdline = task->cmdline; + node[nodes].environ = task->environ; + node[nodes].children = 0; + node[nodes].have_parent = 0; + nodes++; +} + + +/*****************************/ +static int node_cmp(const void *s1, const void *s2) { + struct tree_node *n1 = (struct tree_node *) s1; + struct tree_node *n2 = (struct tree_node *) s2; + return n1->pid - n2->pid; +} + + +/*****************************/ +static void show_tree(int n, int depth, char *continued) { + int i, cols = 0; + + fprintf(stdout, "%s", node[n].line); + for (i = 0; i < depth; i++) { + if (cols + 4 >= cmdspc - 1) + break; + if (i == depth - 1) + printf(" \\_ "); + else if (continued[i]) + printf(" | "); + else + printf(" "); + cols += 4; + } + show_cmd_env(node[n].cmd, node[n].cmdline, node[n].environ, cmdspc - cols); + for (i = 0; i < node[n].children; i++) { + continued[depth] = i != node[n].children - 1; + show_tree(node[n].child[i], depth + 1, continued); + } +} + + +/*****************************/ +static void show_forest() { + register int i, j; + int parent; + char continued[1024]; + + if (CL_sort) + qsort((void*)node, nodes, sizeof(struct tree_node), (void*)node_mult_lvl_cmp); + + for (i = 0; i < nodes; i++) { + if (node[i].ppid > 1 && node[i].pid != node[i].ppid) { + parent = -1; + for (j=0; j= 0) { + node[i].have_parent++; + if (node[parent].children == 0) { + node[parent].child = (int*)xmalloc(16 * sizeof(int*)); + node[parent].maxchildren = 16; + } + else if (node[parent].children == node[parent].maxchildren) { + node[parent].maxchildren *= 2; + node[parent].child = (int*)xrealloc(node[parent].child, + node[parent].maxchildren + * sizeof(int*)); + } + node[parent].child[node[parent].children++] = i; + } + } + + for (i = 0; i < nodes; i++) { + if (!node[i].have_parent) + show_tree(i, 0, continued); + } +} diff --git a/pgrep.1 b/pgrep.1 new file mode 100644 index 00000000..346c5d08 --- /dev/null +++ b/pgrep.1 @@ -0,0 +1,158 @@ +.\" Manual page for pgrep / pkill. +.\" Licensed under version 2 of the GNU General Public License. +.\" Copyright 2000 Kjetil Torgrim Homme +.\" +.TH PGREP 1 "June 25, 2000" "Linux" "Linux User's Manual" +.SH NAME +pgrep, pkill \- look up or signal processes based on name and other attributes + +.SH SYNOPSIS +pgrep [\-flnvx] [\-d \fIdelimiter\fP] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...] +.br + [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...] +.br + [\-t \fIterm\fP,...] [\fIpattern\fP] + +pkill [\-\fIsignal\fP] [\-fnvx] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...] +.br + [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...] +.br + [\-t \fIterm\fP,...] [\fIpattern\fP] + +.SH DESCRIPTION +\fBpgrep\fP looks through the currently running processes and lists the +process IDs which matches the selection criteria to stdout. All +the criteria have to match. For example, + +pgrep -u root sshd + +will only list the processes called \fBsshd\fP AND owned by \fBroot\fP. +On the other hand, + +pgrep -u root,daemon + +will list the processes owned by \fBroot\fP OR \fBdaemon\fP. + +\fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP) +to each process instead of listing them on stdout. + +.SH OPTIONS +.TP +\-d \fIdelimiter\fP +Sets the string used to delimit each process ID in the output (by +default a newline). (\fBpgrep\fP only.) +.TP +\-f +The \fIpattern\fP is normally only matched against the process name. +When \-f is set, the full command line is used. +.TP +\-g \fIpgrp\fP,... +Only match processes in the process group IDs listed. Process group 0 +is translated into \fBpgrep\fP's or \fBpkill\fP's own process group. +.TP +\-G \fIgid\fP,... +Only match processes whose real group ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-l +List the process name as well as the process ID. (\fBpgrep\fP only.) +.TP +\-n +Select only the newest (most recently started) of the matching +processes. +.TP +\-P \fIppid\fP,... +Only match processes whose parent process ID is listed. +.TP +\-s \fIsid\fP,... +Only match processes whose process session ID is listed. Session ID 0 +is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID. +.TP +\-t \fIterm\fP,... +Only match processes whose controlling terminal is listed. The +terminal name should be specified without the "/dev/" prefix. +.TP +\-u \fIeuid\fP,... +Only match processes whose effective user ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-U \fIuid\fP,... +Only match processes whose real user ID is listed. Either the +numerical or symbolical value may be used. +.TP +\-v +Negates the matching. +.TP +\-x +Only match processes whose name (or command line if \-f is specified) +\fBexactly\fP match the \fIpattern\fP. +.TP +\-\fIsignal\fP +Defines the signal to send to each matched process. Either the +numeric or the symbolic signal name can be used. (\fBpkill\fP only.) + +.SH OPERANDS +.TP +\fIpattern\fP +Specifies an Extended Regular Expression for matching against the +process names or command lines. + +.SH EXAMPLES +Example 1: Find the process ID of the \fBnamed\fP daemon: + +unix$ pgrep \-u root named + +Example 2: Make \fBsyslog\fP reread its configuration file: + +unix$ pkill \-HUP syslogd + +Example 3: Give detailed information on all \fBxterm\fP processes: + +unix$ ps \-fp $(pgrep \-d, \-x xterm) + +Example 4: Make all \fBnetscape\fP processes run nicer: + +unix$ renice +4 `pgrep netscape` + +.SH "EXIT STATUS" +.TP +.I "0" +One or more processes matched the criteria. +.TP +.I "1" +No processes matched. +.TP +.I "2" +Syntax error in the command line. +.TP +.I "3" +Fatal error: out of memory etc. + +.SH NOTES +The process name used for matching is limited to the 15 characters +present in the output of /proc/\fIpid\fP/stat. Use the \-f option to +match against the complete command line, /proc/\fIpid\fP/cmdline. + +The running \fBpgrep\fP or \fBpkill\fP process will never report +itself as a match. + +.SH BUGS +The options \-n and \-v can not be combined. Let me know if you need +to do this. + +Defunct processes are reported. + +.SH "SEE ALSO" +ps(1) proc(5) regex(5) + +.SH STANDARDS +\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This +implementation is fully compatible. + +.SH AUTHOR +Kjetil Torgrim Homme + +Michael K. Johnson is the current maintainer of +the procps package. + +Please send bug reports to diff --git a/pgrep.c b/pgrep.c new file mode 100644 index 00000000..91f6a3a5 --- /dev/null +++ b/pgrep.c @@ -0,0 +1,605 @@ +/* emacs settings: -*- c-basic-offset: 8 tab-width: 8 -*- + * + * pgrep/pkill -- utilities to filter the process table + * + * Copyright 2000 Kjetil Torgrim Homme + * + * May be distributed under the conditions of the + * GNU General Public License; a copy is in COPYING + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/readproc.h" +#include "proc/sig.h" +#include "proc/devname.h" +#include "proc/sysinfo.h" + +static int i_am_pkill = 0; +char *progname = "pgrep"; + +union el { + long num; + char * str; +}; + +/* User supplied arguments */ + +static int opt_full = 0; +static int opt_long = 0; +static int opt_newest = 0; +static int opt_negate = 0; +static int opt_exact = 0; +static int opt_signal = SIGTERM; + +static char *opt_delim = "\n"; +static union el *opt_pgrp = NULL; +static union el *opt_gid = NULL; +static union el *opt_ppid = NULL; +static union el *opt_sid = NULL; +static union el *opt_term = NULL; +static union el *opt_euid = NULL; +static union el *opt_uid = NULL; +static char *opt_pattern = NULL; + +/* Prototypes */ + +static union el *split_list (const char *, char, int (*)(const char *, union el *)); +static int conv_uid (const char *, union el *); +static int conv_gid (const char *, union el *); +static int conv_sid (const char *, union el *); +static int conv_pgrp (const char *, union el *); +static int conv_num (const char *, union el *); +static int conv_str (const char *, union el *); +static int match_numlist (long, const union el *); +static int match_strlist (const char *, const union el *); + + +static int +usage (int opt) +{ + if (i_am_pkill) + fprintf (stderr, "Usage: pgrep [-flnvx] [-d DELIM] "); + else + fprintf (stderr, "Usage: pkill [-SIGNAL] [-fnvx] "); + fprintf (stderr, "[-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n" + "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] " + "[PATTERN]\n"); + exit (opt == '?' ? 0 : 2); +} + + +static void +parse_opts (int argc, char **argv) +{ + char opts[32] = ""; + int opt; + int criteria_count = 0; + + if (strstr (argv[0], "pkill")) { + i_am_pkill = 1; + progname = "pkill"; + /* Look for a signal name or number as first argument */ + if (argc > 1 && argv[1][0] == '-') { + int sig; + sig = signal_name_to_number (argv[1] + 1); + if (sig == -1 && isdigit (argv[1][1])) + sig = atoi (argv[1] + 1); + if (sig != -1) { + int i; + for (i = 2; i < argc; i++) + argv[i-1] = argv[i]; + --argc; + opt_signal = sig; + } + } + } else { + /* These options are for pgrep only */ + strcat (opts, "ld:"); + } + + strcat (opts, "fnvxP:g:s:u:U:G:t:?"); + + while ((opt = getopt (argc, argv, opts)) != -1) { + switch (opt) { + case 'f': + opt_full = 1; + break; + case 'l': + opt_long = 1; + break; + case 'n': + opt_newest = 1; + ++criteria_count; + break; + case 'v': + opt_negate = 1; + break; + case 'x': + opt_exact = 1; + break; + case 'd': + opt_delim = strdup (optarg); + break; + case 'P': + opt_ppid = split_list (optarg, ',', conv_num); + if (opt_ppid == NULL) + usage (opt); + ++criteria_count; + break; + case 'g': + opt_pgrp = split_list (optarg, ',', conv_pgrp); + if (opt_pgrp == NULL) + usage (opt); + break; + case 's': + opt_sid = split_list (optarg, ',', conv_sid); + if (opt_sid == NULL) + usage (opt); + ++criteria_count; + break; + case 'u': + opt_euid = split_list (optarg, ',', conv_uid); + if (opt_euid == NULL) + usage (opt); + ++criteria_count; + break; + case 'U': + opt_uid = split_list (optarg, ',', conv_uid); + if (opt_uid == NULL) + usage (opt); + ++criteria_count; + break; + case 'G': + opt_gid = split_list (optarg, ',', conv_gid); + if (opt_gid == NULL) + usage (opt); + ++criteria_count; + break; + case 't': + opt_term = split_list (optarg, ',', conv_str); + if (opt_term == NULL) + usage (opt); + ++criteria_count; + break; + case '?': + usage (opt); + break; + } + } + if (argc - optind == 1) + opt_pattern = argv[optind]; + else if (argc - optind > 1) + usage (0); + else if (criteria_count == 0) { + fprintf (stderr, "%s: No matching criteria specified\n", + progname); + usage (0); + } +} + + +static union el * +split_list (const char *str, char sep, int (*convert)(const char *, union el *)) +{ + char *copy = strdup (str); + char *ptr = copy; + char *sep_pos; + int i = 1, size = 32; + union el *list; + + list = malloc (size * sizeof (union el)); + if (list == NULL) + exit (3); + + do { + sep_pos = strchr (ptr, sep); + if (sep_pos) + *sep_pos = 0; + if (convert (ptr, &list[i])) + ++i; + else + exit (2); + if (i == size) { + size *= 2; + list = realloc (list, size * sizeof (union el)); + if (list == NULL) + exit (3); + } + if (sep_pos) + ptr = sep_pos + 1; + } while (sep_pos); + + free (copy); + if (i == 1) { + free (list); + list = NULL; + } else { + list[0].num = i - 1; + } + return (list); +} + +/* strict_atol returns a Boolean: TRUE if the input string contains a + plain number, FALSE if there are any non-digits. */ + +static int +strict_atol (const char *str, long *value) +{ + int res = 0; + int sign = 1; + + if (*str == '+') + ++str; + else if (*str == '-') { + ++str; + sign = -1; + } + + for ( ; *str; ++str) { + if (! isdigit (*str)) + return (0); + res *= 10; + res += *str - '0'; + } + *value = sign * res; + return (1); +} + +static int +conv_uid (const char *name, union el *e) +{ + struct passwd *pwd; + + if (strict_atol (name, &e->num)) + return (1); + + pwd = getpwnam (name); + if (pwd == NULL) { + fprintf (stderr, "%s: invalid user name: %s\n", + progname, name); + return (0); + } + e->num = pwd->pw_uid; + return (1); +} + + +static int +conv_gid (const char *name, union el *e) +{ + struct group *grp; + + if (strict_atol (name, &e->num)) + return (1); + + grp = getgrnam (name); + if (grp == NULL) { + fprintf (stderr, "%s: invalid group name: %s\n", + progname, name); + return (0); + } + e->num = grp->gr_gid; + return (1); +} + + +static int +conv_pgrp (const char *name, union el *e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: invalid process group: %s\n", + progname, name); + return (0); + } + if (e->num == 0) + e->num = getpgrp (); + return (1); +} + + +static int +conv_sid (const char *name, union el *e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: invalid session id: %s\n", + progname, name); + return (0); + } + if (e->num == 0) + e->num = getsid (0); + return (1); +} + + +static int +conv_num (const char *name, union el *e) +{ + if (! strict_atol (name, &e->num)) { + fprintf (stderr, "%s: not a number: %s\n", + progname, name); + return (0); + } + return (1); +} + + +static int +conv_str (const char *name, union el *e) +{ + e->str = strdup (name); + return (1); +} + + +static int +match_numlist (long value, const union el *list) +{ + int found = 0; + if (list == NULL) + found = 0; + else { + int i; + for (i = list[0].num; i > 0; i--) { + if (list[i].num == value) + found = 1; + } + } + return (found); +} + +static int +match_strlist (const char *value, const union el *list) +{ + int found = 0; + if (list == NULL) + found = 0; + else { + int i; + for (i = list[0].num; i > 0; i--) { + if (! strcmp (list[i].str, value)) + found = 1; + } + } + return (found); +} + +static void +output_numlist (const union el *list) +{ + int i; + for (i = 1; i < list[0].num; i++) + printf ("%ld%s", list[i].num, opt_delim); + + if (list[0].num) + printf ("%ld\n", list[i].num); +} + +static void +output_strlist (const union el *list) +{ + int i; + for (i = 1; i < list[0].num; i++) + printf ("%s%s", list[i].str, opt_delim); + + if (list[0].num) + printf ("%s\n", list[i].str); +} + +static PROCTAB * +do_openproc () +{ + PROCTAB *ptp; + int flags = PROC_FILLANY; + + if (opt_pattern || opt_full) + flags |= PROC_FILLCMD; + if (opt_uid) + flags |= PROC_FILLSTATUS; + if (opt_euid && !opt_negate) { + int num = opt_euid[0].num; + int i = num; + uid_t *uids = malloc (num * sizeof (uid_t)); + if (uids == NULL) + exit (3); + while (i-- > 0) { + uids[i] = opt_euid[i+1].num; + } + flags |= PROC_UID; + ptp = openproc (flags, uids, num); + } else { + ptp = openproc (flags); + } + return (ptp); +} + +static regex_t * +do_regcomp () +{ + regex_t *preg = NULL; + + if (opt_pattern) { + char *re; + char errbuf[256]; + int re_err; + + preg = malloc (sizeof (regex_t)); + if (preg == NULL) + exit (3); + if (opt_exact) { + re = malloc (strlen (opt_pattern) + 5); + if (re == NULL) + exit (3); + sprintf (re, "^(%s)$", opt_pattern); + } else { + re = opt_pattern; + } + + re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB); + if (re_err) { + regerror (re_err, preg, errbuf, sizeof(errbuf)); + fprintf (stderr, errbuf); + exit (2); + } + } + return preg; +} + +#ifdef NOT_USED +static time_t +jiffies_to_time_t (long jiffies) +{ + static time_t time_of_boot = 0; + if (time_of_boot == 0) { + time_of_boot = time (NULL) - uptime (0, 0); + } + return (time_of_boot + jiffies / Hertz); +} +#endif + +static union el * +select_procs () +{ + PROCTAB *ptp; + proc_t task; + long newest_start_time = 0; + pid_t newest_pid = 0; + int matches = 0; + int size = 32; + regex_t *preg; + pid_t myself = getpid(); + union el *list; + char cmd[4096]; + + list = malloc (size * sizeof (union el)); + if (list == NULL) + exit (3); + + ptp = do_openproc (); + preg = do_regcomp (); + + memset (&task, 0, sizeof (task)); + while (readproc (ptp, &task)) { + int match = 1; + + if (task.pid == myself) + continue; + else if (opt_newest && task.start_time < newest_start_time) + match = 0; + else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid)) + match = 0; + else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp)) + match = 0; + else if (opt_euid && ! match_numlist (task.euid, opt_euid)) + match = 0; + else if (opt_uid && ! match_numlist (task.ruid, opt_uid)) + match = 0; + else if (opt_gid && ! match_numlist (task.rgid, opt_gid)) + match = 0; + else if (opt_sid && ! match_numlist (task.session, opt_sid)) + match = 0; + else if (opt_term) { + if (task.tty == -1) { + match = 0; + } else { + char tty[256]; + dev_to_tty (tty, sizeof(tty) - 1, + task.tty, task.pid, ABBREV_DEV); + match = match_strlist (tty, opt_term); + } + } + if (opt_long || (match && opt_pattern)) { + if (opt_full && task.cmdline) { + int i = 0; + int bytes = sizeof (cmd) - 1; + + /* make sure it is always NUL-terminated */ + cmd[bytes] = 0; + /* make room for SPC in loop below */ + --bytes; + + strncpy (cmd, task.cmdline[i], bytes); + bytes -= strlen (task.cmdline[i++]); + while (task.cmdline[i] && bytes > 0) { + strncat (cmd, " ", bytes); + strncat (cmd, task.cmdline[i], bytes); + bytes -= strlen (task.cmdline[i++]) + 1; + } + } else { + strcpy (cmd, task.cmd); + } + } + + if (match && opt_pattern) { + if (regexec (preg, cmd, 0, NULL, 0) != 0) + match = 0; + } + + if (match ^ opt_negate) { /* Exclusive OR is neat */ + if (opt_newest) { + if (newest_start_time == task.start_time && + newest_pid > task.pid) + continue; + newest_start_time = task.start_time; + newest_pid = task.pid; + matches = 0; + } + if (opt_long) { + char buff[4096]; + sprintf (buff, "%d %s", task.pid, cmd); + list[++matches].str = strdup (buff); + } else { + list[++matches].num = task.pid; + } + if (matches == size) { + size *= 2; + list = realloc (list, + size * sizeof (union el)); + if (list == NULL) + exit (3); + } + } + + memset (&task, 0, sizeof (task)); + } + closeproc (ptp); + + list[0].num = matches; + return (list); +} + + +int +main (int argc, char **argv) +{ + union el *procs; + + parse_opts (argc, argv); + + procs = select_procs (); + if (i_am_pkill) { + int i; + for (i = 1; i <= procs[0].num; i++) { + if (kill (procs[i].num, opt_signal) == -1) + fprintf (stderr, "pkill: %ld - %s\n", + procs[i].num, strerror (errno)); + } + } else { + if (opt_long) + output_strlist (procs); + else + output_numlist (procs); + } + return ((procs[0].num) == 0 ? 1 : 0); +} diff --git a/pkill.1 b/pkill.1 new file mode 100644 index 00000000..0e94b52c --- /dev/null +++ b/pkill.1 @@ -0,0 +1 @@ +.so man1/pgrep.1 diff --git a/pmap.c b/pmap.c new file mode 100644 index 00000000..ad01e1ec --- /dev/null +++ b/pmap.c @@ -0,0 +1,34 @@ +#include +#include +#include + +void usage(void){ + fprintf(stderr, + "Usage: pmap [-r] [-x] pid...\n" + "-r ignored (compatibility option)\n" + "-x show details\n" + ); + exit(1); +} + +int one_proc(unsigned pid){ + char cmdbuf[64]; + char buf[32]; + int fd; + sprintf(buf,"/proc/%u/cmdline",pid); + if( ((fd=open(buf)) == -1) return 1; + count = read(fd, cmdbuf, sizeof(cmdbuf)-1); + if(count<1) return 1; + cmdbuf[count] = '\0'; + while(count--) if(!isprint(cmdbuf[count])) cmdbuf[count]=' '; + close(fd); + sprintf(buf,"/proc/%u/maps",pid); + if( ((fd=open(buf)) == -1) return 1; + printf("%u: %s\n", pid, cmdbuf); + if(x_option) + printf("Address kB Resident Shared Private Permissions Name\n"); +@@@ FIXME FIXME FIXME @@@ +} + +int main(int argc, char *argv[]){ +} diff --git a/proc/.cvsignore.patch b/proc/.cvsignore.patch new file mode 100644 index 00000000..f12054b0 --- /dev/null +++ b/proc/.cvsignore.patch @@ -0,0 +1,7 @@ +diff -Naur procps-2.0.6/proc/.cvsignore procps-2.0.7/proc/.cvsignore +--- procps-2.0.6/proc/.cvsignore Wed Dec 31 19:00:00 1969 ++++ procps-2.0.7/proc/.cvsignore Fri Jul 14 16:45:01 2000 +@@ -0,0 +1,3 @@ ++.depend ++signames.h ++libproc.so.* diff --git a/proc/COPYING b/proc/COPYING new file mode 100644 index 00000000..92b8903f --- /dev/null +++ b/proc/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/proc/Makefile b/proc/Makefile new file mode 100644 index 00000000..bc41bf14 --- /dev/null +++ b/proc/Makefile @@ -0,0 +1,98 @@ +# Auto-adaptive C library Makefile adapted for libproc, Chuck Blake. +# Assumptions are basically that all the .c files in the CWD are modules +# for the library and that all .h files are the interface to the library. + +# PROJECT SPECIFIC MACROS +NAME = proc + +# INSTALLATION OPTIONS +TOPDIR = /usr +HDRDIR = $(TOPDIR)/include/$(NAME)# where to put .h files +LIBDIR = $(TOPDIR)/lib# where to put library files +SHLIBDIR = /lib# where to put shared library files +HDROWN = $(OWNERGROUP) # owner of header files +LIBOWN = $(OWNERGROUP) # owner of library files +INSTALL = install + +# ----------------------------------------------------------------# +# The rest is the auto-magic section -- highly GNU make dependent # +# You should never need to edit this. # +# ----------------------------------------------------------------# + +VC_SUF = ,v +VC_PFX = RCS/ +RCSFILES = $(patsubst $(VC_PFX)%$(VC_SUF),%,$(wildcard $(VC_PFX)*$(VC_SUF))) + +# We take the union of RCS files and other files in CWD so that new files do +# not need to alter this makefile. 'sort' removes duplicates. This allows the +# convenience of compiling and testing new files before the initial check-in. + +SRC = $(sort $(wildcard *.c) $(filter %.c,$(RCSFILES))) +HDR = $(sort $(wildcard *.h) $(filter %.h,$(RCSFILES))) + +OBJ = $(SRC:.c=.o) +SONAME = lib$(NAME).so.$(LIBVERSION) + +ifeq ($(SHARED),1) +CFLAGS += -fpic +all: lib$(NAME).a $(SONAME) +else +all: lib$(NAME).a +endif + +lib$(NAME).a: $(OBJ) + $(AR) rcs $@ $^ + +$(SONAME): $(OBJ) + gcc -shared -Wl,-soname,$(SONAME) -o $@ $^ -lc + ln -sf $(SONAME) lib$(NAME).so + +# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT + +.depend: + $(strip $(CC) $(CFLAGS) -MM -MG $(SRC) > .depend) +-include .depend + +# INSTALLATION + +install: all + if ! [ -d $(HDRDIR) ] ; then mkdir $(HDRDIR) ; fi + $(INSTALL) $(HDROWN) $(HDR) $(TOPDIR)/include/$(NAME) + $(INSTALL) $(LIBOWN) lib$(NAME).a $(LIBDIR) +ifeq ($(SHARED),1) + $(INSTALL) $(LIBOWN) $(SONAME) $(SHLIBDIR) + cd $(SHLIBDIR) && ln -sf $(SONAME) lib$(NAME).so + ldconfig +endif + +# VARIOUS SHORT CUT TARGETS +.PHONY: all install dep clean distclean checkout checkclean + +dep: .depend + +clean: + $(RM) lib$(NAME).* *.o DEADJOE + +distclean: clean + $(RM) .depend *~ + +checkout: + $(CO) $(RCSFILES) + +checkclean: + $(RM) $(RCSFILES) + +# CUSTOM c -> o rule so that command-line has minimal whitespace + +%.o : %.c + $(strip $(CC) $(CFLAGS) -c $<) + +# PROJECT SPECIFIC DEPENDENCIES/BUILD RULES + + +version.o: version.c version.h +ifdef MINORVERSION + $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c version.c) +else + $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -c version.c) +endif diff --git a/proc/alloc.c b/proc/alloc.c new file mode 100644 index 00000000..0a616cec --- /dev/null +++ b/proc/alloc.c @@ -0,0 +1,49 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* This file is placed under the conditions of the GNU Library * +* General Public License, version 2, or any later version. * +* See file COPYING for information on distribution conditions. * +\***********************************************************************/ +#include +#include + +void *xcalloc(void *pointer, int size) { + void * ret; + if (pointer) + free(pointer); + if (!(ret = calloc(1, size))) { + fprintf(stderr, "xcalloc: allocation error, size = %d\n", size); + exit(1); + } else { + return ret; + } +} + +void *xmalloc(unsigned int size) { + void *p; + + if (size == 0) + ++size; + p = malloc(size); + if (!p) { + fprintf(stderr, "xmalloc: malloc(%d) failed", size); + perror(NULL); + exit(1); + } + return(p); +} + +void *xrealloc(void *oldp, unsigned int size) { + void *p; + + if (size == 0) + ++size; + p = realloc(oldp, size); + if (!p) { + fprintf(stderr, "xrealloc: realloc(%d) failed", size); + perror(NULL); + exit(1); + } + return(p); +} diff --git a/proc/compare.c b/proc/compare.c new file mode 100644 index 00000000..46695ac5 --- /dev/null +++ b/proc/compare.c @@ -0,0 +1,304 @@ +/* + * + * Copyright 1994 Charles Blake and Michael K. Johnson + * This file is a part of procps, which is distributable + * under the conditions of the GNU Library General Public License. + * See the file COPYING for details. + * + */ + +#include /* for strcmp */ +#include /* for parse error output */ + +#include "proc/readproc.h" /* for proc_t */ +#include "proc/tree.h" /* for struct tree_node */ + +#include "proc/compare.h" /* for this code */ + + +/* + This module was written by Charles Blake for procps. + +mult_lvl_cmp: + slick general purpose multi-level compare function I invented. +sort_depth: + the number of levels of functions *to use*. This means many more levels + can be defined than mult_lvl_cmp tres out. If this is 1 then mult_lvl_cmp + is just a trivial wrapper around (*sort_function[0]). +sort_direction: + multiplicative factor for the output of cmp_whatever. + 1 ==> default order, -1 ==> reverse order, 0 ==> forced equality + The 0 bit is the neat part. Since a value of zero is the code for equality + multiplying the output of cmp_foo(a,b) forces a==b to be true. This is a + convenient way to turn sorting off in middle levels of a multi-level sort. + If time is a problem, reforming the whole sort_function array to not include + these unsorted middle levels will be faster since then cmp_foo won't even + be called. It might simplify some code depending upon how you organize it. +sort_function[]: + array of function pointers that points to our family of comparison functions + (I have named them cmp_* but mult_lvl_cmp doesn't care what they're named). + This may be declared and initialized like so: + int (*sort_function[])(void* a, void* b)={&cmp_foo, &cmp_bar, &cmp_hiho}; + You could also use my command line '-O' parser below. + +Note that we only descend levels until the order is determined. If we descend +all levels, that means that the items are equal at all levels, so we return 0. +Otherwise we return whatever the level's cmp_foo function would have returned. +This allows whatever default behavior you want for cmp_foo. sort_direction[] +reverses this default behavior, but mult_lvl_cmp doesn't decide that ascending +or descending is the default. That is the job of your cmp_foo's. +*/ + +/* the only reason these are global is because qsort(3) likes it that way. + It's also a little more efficient if mult_lvl_cmp() is called many times. +*/ + +static int sort_depth = 0; +static int sort_direction[10]; /* storage for 10 levels, but 4 would be plenty!*/ +static int (*sort_function[10])(void* a, void* b); + +int mult_lvl_cmp(void* a, void* b) { + int i, cmp_val; + for(i = 0; i < sort_depth; i++) { + cmp_val = sort_direction[i] * (*sort_function[i])(a,b); + if (cmp_val != 0) + return cmp_val; + } + return 0; +} + +int node_mult_lvl_cmp(void* a, void* b) { + int i, cmp_val; + for(i = 0; i < sort_depth; i++) { + cmp_val = sort_direction[i] * (*sort_function[i])(&(((struct tree_node *)a)->proc),&(((struct tree_node *)b)->proc)); + if (cmp_val != 0) + return cmp_val; + } + return 0; +} + +/* qsort(3) compliant comparison functions for all members of the ps_proc + structure (in the same order in which they appear in the proc_t declaration) + return is {-1,0,1} as {ab} + default ordering is ascending for all members. (flip 1,-1 to reverse) +*/ +/* pre-processor macros to cut down on source size (and typing!) + Note the use of the string concatenation operator ## +*/ +#define CMP_STR(NAME) \ +static int cmp_ ## NAME(proc_t** P, proc_t** Q) { \ + return strcmp((*P)->NAME, (*Q)->NAME); \ +} + +#define CMP_INT(NAME) \ +static int cmp_ ## NAME (proc_t** P, proc_t** Q) { \ + if ((*P)->NAME < (*Q)->NAME) return -1; \ + if ((*P)->NAME > (*Q)->NAME) return 1; \ + return 0; \ +} + +/* Define the (46!) cmp_ functions with the above macros for every element + of proc_t. If the binary gets too big, we could nuke inessentials. +*/ + +/* CMP_STR(cmdline) */ +CMP_STR(ruser) +CMP_STR(euser) +CMP_STR(cmd) +/* CMP_INT(state) */ +/* CMP_STR(ttyc) */ +CMP_INT(euid) +CMP_INT(pid) +CMP_INT(ppid) +CMP_INT(pgrp) +CMP_INT(session) +CMP_INT(tty) +CMP_INT(tpgid) +CMP_INT(utime) +CMP_INT(stime) +CMP_INT(cutime) +CMP_INT(cstime) +/* CMP_INT(priority) */ +CMP_INT(nice) +CMP_INT(start_time) +/* CMP_INT(signal) */ +/* CMP_INT(blocked) */ +/* CMP_INT(sigignore) */ +/* CMP_INT(sigcatch) */ +CMP_INT(flags) +CMP_INT(min_flt) +CMP_INT(cmin_flt) +CMP_INT(maj_flt) +CMP_INT(cmaj_flt) +/* CMP_INT(timeout) */ +CMP_INT(vsize) +CMP_INT(rss) +/* CMP_INT(rss_rlim) */ +/* CMP_INT(start_code) */ +/* CMP_INT(end_code) */ +/* CMP_INT(start_stack) */ +/* CMP_INT(kstk_esp) */ +/* CMP_INT(kstk_eip) */ +/* CMP_INT(wchan) */ +CMP_INT(pcpu) +CMP_INT(size) +CMP_INT(resident) +CMP_INT(share) +/* CMP_INT(trs) */ +/* CMP_INT(lrs) */ +/* CMP_INT(drs) */ +/* CMP_INT(dt) */ + +/* define user interface to sort keys. Fairly self-explanatory. */ + +static struct cmp_fun_struct { + char letter; /* single option-letter for key */ + char name[15]; /* long option name for key */ + int (*fun)(proc_t**, proc_t**); /* pointer to cmp_key */ +} cmp[] = { +/* { '?', "cmdline", &cmp_cmdline }, */ + { 'u', "user", &cmp_euser }, +/* { '?', "ruser", &cmp_ruser }, */ + { 'c', "cmd", &cmp_cmd }, +/* { '?', "state", &cmp_state }, */ +/* { '?', "ttyc", &cmp_ttyc }, */ + { 'U', "uid", &cmp_euid }, + { 'p', "pid", &cmp_pid }, + { 'P', "ppid", &cmp_ppid }, + { 'g', "pgrp", &cmp_pgrp }, + { 'o', "session", &cmp_session }, + { 't', "tty", &cmp_tty }, + { 'G', "tpgid", &cmp_tpgid }, + { 'k', "utime", &cmp_utime }, + { 'K', "stime", &cmp_stime }, + { 'j', "cutime", &cmp_cutime }, + { 'J', "cstime", &cmp_cstime }, +/* { '?', "counter", &cmp_counter }, */ + { 'y', "priority", &cmp_nice }, + { 'T', "start_time", &cmp_start_time }, +/* { '?', "signal", &cmp_signal }, */ +/* { '?', "blocked", &cmp_blocked }, */ +/* { '?', "sigignore", &cmp_sigignore }, */ +/* { '?', "sigcatch", &cmp_sigcatch }, */ + { 'f', "flags", &cmp_flags }, + { 'm', "min_flt", &cmp_min_flt }, + { 'n', "cmin_flt", &cmp_cmin_flt }, + { 'M', "maj_flt", &cmp_maj_flt }, + { 'N', "cmaj_flt", &cmp_cmaj_flt }, +/* { 'C', "timeout", &cmp_timeout }, */ + { 'v', "vsize", &cmp_vsize }, + { 'r', "rss", &cmp_rss }, +/* { '?', "rss_rlim", &cmp_rss_rlim }, */ +/* { '?', "start_code", &cmp_start_code }, */ +/* { '?', "end_code", &cmp_end_code }, */ +/* { '?', "start_stack", &cmp_start_stack }, */ +/* { '?', "kstk_esp", &cmp_kstk_esp }, */ +/* { '?', "kstk_eip", &cmp_kstk_eip }, */ +/* { '?', "wchan", &cmp_wchan }, */ + { 'C', "pcpu", &cmp_pcpu }, + { 's', "size", &cmp_size }, + { 'R', "resident", &cmp_resident }, + { 'S', "share", &cmp_share }, +/* { '?', "trs", &cmp_trs }, */ +/* { '?', "lrs", &cmp_lrs }, */ +/* { '?', "drs", &cmp_drs }, */ +/* { '?', "dt", &cmp_dt }, */ + { '\0',"terminator", NULL } +}; + +/* command line option parsing. Assign sort_{depth,direction[],function[]} + based upon a string of the form: + [+-]a[+-]b[+-]c... + with a,b,c,... being letter flags corresponding to a particular sort + key and the optional '-' specifying a reverse sort on that key. + doesn't + mean anything, but it keeps things looking balanced... +*/ +const char *parse_sort_opt(const char* opt) { + int i, next_dir=1; + for(; *opt ; ++opt) { + if (*opt == '-' || *opt == '+') { + if (*opt == '-') + next_dir = -1; + opt++; + continue; + } + for (i = 0; cmp[i].letter; i++) + if (*opt == cmp[i].letter) + break; + if (!cmp[i].letter) { /* failed, clear and return */ + sort_depth=0; + for (i=0;i<10;i++){ + sort_direction[i]=0; + sort_function[i]=(cmp_t)NULL; + } + return "Unknown sort key."; + } else { +#ifdef DEBUG + fprintf(stderr, + "sort level %d: key %s, direction % d\n", + sort_depth, cmp[i].name, next_dir); +#endif + sort_function[sort_depth] = (cmp_t)cmp[i].fun; + sort_direction[sort_depth++] = next_dir; + next_dir = 1; + } + } + return NULL; +} + +const char *parse_long_sort(const char* opt) { + char* comma; + int i, more_keys, next_dir=1; + do { + if (*opt == '-' || *opt == '+') { + if (*opt == '-') + next_dir = -1; + more_keys = 1; + opt++; + continue; + } + more_keys = ((comma=index(opt,',')) != NULL); + /* keys are ',' delimited */ + if (more_keys) + *comma='\0'; /* terminate for strcmp() */ + for(i = 0; cmp[i].letter; ++i) + if (strcmp(opt, cmp[i].name) == 0) + break; + if (!cmp[i].letter) { /* failed, clear and return */ + sort_depth=0; + for (i=0;i<10;i++){ + sort_direction[i]=0; + sort_function[i]=(cmp_t)NULL; + } + return "Unknown sort key."; + } else { +#ifdef DEBUG + fprintf(stderr, + "sort level %d: key %s, direction % d\n", + sort_depth, cmp[i].name, next_dir); +#endif + sort_function[sort_depth] = (cmp_t)cmp[i].fun; + sort_direction[sort_depth++] = next_dir; + next_dir = 1; + } + opt = comma + 1; /* do next loop on next key, if more keys, else done*/ + } while (more_keys); + return NULL; +} + +void reset_sort_options (void) +{ + int i; + + sort_depth=0; + for (i=0;i<10;i++){ + sort_direction[i]=0; + sort_function[i]=(cmp_t)NULL; + } +} + +void register_sort_function (int dir, cmp_t func) +{ + sort_function[sort_depth] = func; + sort_direction[sort_depth++] = dir; +} diff --git a/proc/compare.h b/proc/compare.h new file mode 100644 index 00000000..153717a9 --- /dev/null +++ b/proc/compare.h @@ -0,0 +1,9 @@ +typedef int (*cmp_t)(void*,void*); /* for function pointer casts */ + +extern void register_sort_function (int dir, cmp_t func); +extern void reset_sort_options(void); +extern int mult_lvl_cmp(void* a, void* b); +extern int node_mult_lvl_cmp(void* a, void* b); +extern const char *parse_sort_opt(const char* opt); +extern const char *parse_long_sort(const char* opt); + diff --git a/proc/devname.c b/proc/devname.c new file mode 100644 index 00000000..0a2520d9 --- /dev/null +++ b/proc/devname.c @@ -0,0 +1,211 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "devname.h" + +#include +#ifndef PAGE_SIZE +#define PAGE_SIZE (sizeof(long)*1024) +#endif + +/* Who uses what: + * + * tty_to_dev oldps, w (there is a fancy version in ps) + * dev_to_tty oldps, top, ps + */ + +typedef struct tty_map_node { + struct tty_map_node *next; + int major_number; /* not unsigned! Ugh... */ + char name[4]; +} tty_map_node; + +static tty_map_node *tty_map = NULL; + +/* Load /proc/tty/drivers for device name mapping use. */ +static void load_drivers(void){ + char buf[10000]; + char *p; + int fd; + int bytes; + fd = open("/proc/tty/drivers",O_RDONLY); + if(fd == -1) goto fail; + bytes = read(fd, buf, 9999); + if(bytes == -1) goto fail; + buf[bytes] = '\0'; + p = buf; + while(( p = strstr(p, " /dev/tty") )){ + tty_map_node *tmn; + int len; + p += 9; + len = strspn(p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if(!len) continue; + if(len>3) continue; + if((len=1) && (*p=='S')) continue; + tmn = malloc(sizeof(tty_map_node)); + tmn->next = tty_map; + tty_map = tmn; + memset(tmn->name, '\0', 4); + strncpy(tmn->name, p, len); + p += len; + tmn->major_number = atoi(p); + } +fail: + if(fd != -1) close(fd); + if(!tty_map) tty_map = (tty_map_node *)-1; +} + +/* Try to guess the device name from /proc/tty/drivers info. */ +static int driver_name(char * const buf, int maj, int min){ + struct stat sbuf; + tty_map_node *tmn; + if(!tty_map) load_drivers(); + if(tty_map == (tty_map_node *)-1) return 0; + tmn = tty_map; + for(;;){ + if(!tmn) return 0; + if(tmn->major_number == maj) break; + tmn = tmn->next; + } + sprintf(buf, "/dev/tty%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */ + if(stat(buf, &sbuf) < 0) return 0; + if(min != minor(sbuf.st_rdev)) return 0; + if(maj != major(sbuf.st_rdev)) return 0; + return 1; +} + +/* Try to guess the device name (useful until /proc/PID/tty is added) */ +static int guess_name(char * const buf, int maj, int min){ + struct stat sbuf; + int t0, t1; + int tmpmin = min; + switch(maj){ + case 4: + if(min<64){ + sprintf(buf, "/dev/tty%d", min); + break; + } + if(min<128){ /* to 255 on newer systems */ + sprintf(buf, "/dev/ttyS%d", min-64); + break; + } + tmpmin = min & 0x3f; /* FALL THROUGH */ + case 3: /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */ + t0 = "pqrstuvwxyzabcde"[tmpmin>>4]; + t1 = "0123456789abcdef"[tmpmin&0x0f]; + sprintf(buf, "/dev/tty%c%c", t0, t1); + break; + case 17: sprintf(buf, "/dev/ttyH%d", min); break; + case 19: sprintf(buf, "/dev/ttyC%d", min); break; + case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */ + case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */ + case 24: sprintf(buf, "/dev/ttyE%d", min); break; + case 32: sprintf(buf, "/dev/ttyX%d", min); break; + case 43: sprintf(buf, "/dev/ttyI%d", min); break; + case 46: sprintf(buf, "/dev/ttyR%d", min); break; + case 48: sprintf(buf, "/dev/ttyL%d", min); break; + case 57: sprintf(buf, "/dev/ttyP%d", min); break; + case 71: sprintf(buf, "/dev/ttyF%d", min); break; + case 75: sprintf(buf, "/dev/ttyW%d", min); break; + case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */ + case 105: sprintf(buf, "/dev/ttyV%d", min); break; + case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */ + /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */ + case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break; + case 148: sprintf(buf, "/dev/ttyT%d", min); break; + case 154: sprintf(buf, "/dev/ttySR%d", min); break; + case 156: sprintf(buf, "/dev/ttySR%d", min+256); break; + case 164: sprintf(buf, "/dev/ttyCH%d", min); break; + case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */ + case 172: sprintf(buf, "/dev/ttyMX%d", min); break; + case 174: sprintf(buf, "/dev/ttySI%d", min); break; + case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */ + default: return 0; + } + if(stat(buf, &sbuf) < 0) return 0; + if(min != minor(sbuf.st_rdev)) return 0; + if(maj != major(sbuf.st_rdev)) return 0; + return 1; +} + +/* Linux 2.2 can give us filenames that might be correct. + * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected) + * and in /proc/PID/fd/255 (used by bash to remember the tty). + */ +static int link_name(char * const buf, int maj, int min, int pid, char *name){ + struct stat sbuf; + char path[32]; + int count; + sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */ + count = readlink(path,buf,PAGE_SIZE-1); + if(count == -1) return 0; + buf[count] = '\0'; + if(stat(buf, &sbuf) < 0) return 0; + if(min != minor(sbuf.st_rdev)) return 0; + if(maj != major(sbuf.st_rdev)) return 0; + return 1; +} + +/* number --> name */ +int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags) { + static char buf[PAGE_SIZE]; + char *tmp = buf; + int i = 0; + int c; + if((short)dev == (short)-1) goto fail; + if( link_name(tmp, major(dev), minor(dev), pid, "tty" )) goto abbrev; + if( link_name(tmp, major(dev), minor(dev), pid, "fd/2" )) goto abbrev; + if( guess_name(tmp, major(dev), minor(dev) )) goto abbrev; + if( link_name(tmp, major(dev), minor(dev), pid, "fd/255")) goto abbrev; + if(driver_name(tmp, major(dev), minor(dev) )) goto abbrev; +fail: + strcpy(ret, "?"); + return 1; +abbrev: + if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5; + if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3; + if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4; + tmp[chop] = '\0'; + for(;;){ + c = *tmp; + tmp++; + if(!c) break; + i++; + if(c<=' ') c = '?'; + if(c>126) c = '?'; + *ret = c; + ret++; + } + *ret = '\0'; + return i; +} + +/* name --> number */ +int tty_to_dev(char *name) { + struct stat sbuf; + static char buf[32]; + if(stat(name, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/tty%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + snprintf(buf,32,"/dev/pts/%s",name); + if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev; + return -1; +} diff --git a/proc/devname.h b/proc/devname.h new file mode 100644 index 00000000..f7b60f1f --- /dev/null +++ b/proc/devname.h @@ -0,0 +1,7 @@ +#define ABBREV_DEV 1 /* remove /dev/ */ +#define ABBREV_TTY 2 /* remove tty */ +#define ABBREV_PTS 4 /* remove pts/ */ + +int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags); + +int tty_to_dev(char *name); diff --git a/proc/ksym.c b/proc/ksym.c new file mode 100644 index 00000000..8925bd5a --- /dev/null +++ b/proc/ksym.c @@ -0,0 +1,548 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/procps.h" +#include "proc/version.h" +#include "proc/sysinfo.h" /* smp_num_cpus */ + +#define KSYMS_FILENAME "/proc/ksyms" + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/would/be/nice/to/have/this/file" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked" +#define linux_version_code 131598 /* ? */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.12" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12" +#define linux_version_code 131852 /* 2.3.12 */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS" +#define linux_version_code 131858 /* 2.3.18ac8 */ +#define smp_num_cpus 2 +#endif + +#if 0 +#undef KSYMS_FILENAME +#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS" +#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS" +#define linux_version_code 131858 /* 2.3.18ac8 */ +#define smp_num_cpus 2 +#endif + +/* These are the symbol types, with relative popularity: + * ? w machine type junk for Alpha -- odd syntax + * ? S not for i386 + * 4 W not for i386 + * 60 R + * 100 A + * 125 r + * 363 s not for i386 + * 858 B + * 905 g generated by modutils? + * 929 G generated by modutils? + * 1301 b + * 2750 D + * 4481 d + * 11417 ? + * 13666 t + * 15442 T + * + * For i386, that is: "RArBbDd?tT" + */ + +#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS" + +/* + * '?' is a symbol type + * '.' is part of a name (versioning?) + * "\t[]" are for the module name in /proc/ksyms + */ +#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +/* System.map lines look like: + * hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n + * + * Alpha systems can start with a few lines that have the address replaced + * by space padding and a 'w' for the type. For those lines, the last space + * is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv + * (just one of those, always with a "_mv", then the newline) + * + * The /proc/ksyms lines are like System.map lines w/o the symbol type char. + * When odd features are used, the name part contains: + * "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}" + * It is likely that more crap will be added... + */ + +typedef struct symb { + const char *name; + unsigned long addr; +} symb; + +static const symb fail = { "?", 0 }; +static const char *dash = "-"; + +/* These mostly rely on POSIX to make them zero. */ + +static const symb hashtable[256]; + +static char *sysmap_data; +static unsigned sysmap_room; +static symb *sysmap_index; +static unsigned sysmap_count; + +static char *ksyms_data; +static unsigned ksyms_room = 4096; +static symb *ksyms_index; +static unsigned ksyms_count; +static int idx_room; + +/*********************************/ + +/* Kill this: _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$ + * We kill: (_R[^A-Z]*[0-9a-f]{8,})+$ + * + * The loop should almost never be taken, but it has to be there. + * It gets rid of anything that _looks_ like a version code, even + * if a real version code has already been found. This is because + * the inability to perfectly recognize a version code may lead to + * symbol mangling, which in turn leads to mismatches between the + * /proc/ksyms and System.map data files. + */ +#if 0 +static void chop_version(char *arg){ + char *cp; + cp = strchr(arg,'\t'); + if(cp) *cp = '\0'; /* kill trailing module name first */ + for(;;){ + char *p; + int len = 0; + cp = strrchr(arg, 'R'); + if(!cp || cp<=arg+1 || cp[-1]!='_') break; + for(p=cp; *++p; ){ + switch(*p){ + default: + return; + case '0' ... '9': + case 'a' ... 'f': + len++; + continue; + case 'g' ... 'z': + case '_': + len=0; + continue; + } + } + if(len<8) break; + cp[-1] = '\0'; + } +} +#endif +static void chop_version(char *arg){ + char *cp; + cp = strchr(arg,'\t'); + if(cp) *cp = '\0'; /* kill trailing module name first */ + for(;;){ + int len; + cp = strrchr(arg, 'R'); + if(!cp || cp<=arg+1 || cp[-1]!='_') break; + len=strlen(cp); + if(len<9) break; + if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break; + if(strspn(cp+len-8,"0123456789abcdef")!=8) break; + cp[-1] = '\0'; + } +} + +/***********************************/ + +static const symb *search(unsigned long address, symb *idx, unsigned count){ + unsigned left; + unsigned mid; + unsigned right; + if(!idx) return NULL; /* maybe not allocated */ + if(address < idx[0].addr) return NULL; + if(address >= idx[count-1].addr) return idx+count-1; + left = 0; + right = count-1; + for(;;){ + mid = (left + right) / 2; + if(address >= idx[mid].addr) left = mid; + if(address <= idx[mid].addr) right = mid; + if(right-left <= 1) break; + } + if(address == idx[right].addr) return idx+right; + return idx+left; +} + +/*********************************/ + +/* allocate if needed, read, and return buffer size */ +static void read_file(const char *filename, char **bufp, unsigned *roomp) { + int fd = 0; + ssize_t done; + char *buf; + ssize_t total = 0; + unsigned room = *roomp; + buf = *bufp; + if(!room) goto hell; /* failed before */ + if(!buf) buf = malloc(room); + if(!buf) goto hell; +open_again: + fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0){ + switch(errno){ + case EINTR: goto open_again; + default: _exit(101); + case EACCES: /* somebody screwing around? */ + /* FIXME: set a flag to disable symbol lookup? */ + case ENOENT: /* no module support */ + } + goto hell; + } + for(;;){ + done = read(fd, buf+total, room-total-1); + if(done==0) break; /* nothing left */ + if(done==-1){ + if(errno==EINTR) continue; /* try again */ + perror(""); + goto hell; + } + if(done==(ssize_t)room-total-1){ + char *tmp; + total += done; + /* more to go, but no room in buffer */ + room *= 2; + tmp = realloc(buf, room); + if(!tmp) goto hell; + buf = tmp; + continue; + } + if(done>0 && done<(ssize_t)room-total-1){ + total += done; + continue; /* OK, we read some. Go do more. */ + } + fprintf(stderr,"%ld can't happen\n", (long)done); + /* FIXME: memory leak */ + _exit(42); + } + *bufp = buf; + *roomp = room; + close(fd); + return; +hell: + if(buf) free(buf); + *bufp = NULL; + *roomp = 0; /* this function will never work again */ + total = 0; + close(fd); + return; +} + +/*********************************/ + +static int parse_ksyms(void) { + char *endp; + if(!ksyms_room || !ksyms_data) goto quiet_goodbye; + endp = ksyms_data; + ksyms_count = 0; + if(idx_room) goto bypass; /* some space already allocated */ + idx_room = 512; + for(;;){ + void *vp; + idx_room *= 2; + vp = realloc(ksyms_index, sizeof(symb)*idx_room); + if(!vp) goto bad_alloc; + ksyms_index = vp; +bypass: + for(;;){ + char *saved; + if(!*endp) return 1; + saved = endp; + ksyms_index[ksyms_count].addr = strtoul(endp, &endp, 16); + if(endp==saved || *endp != ' ') goto bad_parse; + endp++; + ksyms_index[ksyms_count].name = endp; + saved = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + chop_version(saved); + ++endp; + if(++ksyms_count >= idx_room) break; /* need more space */ + } + } + + if(0){ +bad_alloc: + fprintf(stderr, "Warning: not enough memory available\n"); + } + if(0){ +bad_parse: + fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n"); + } +quiet_goodbye: + idx_room = 0; + if(ksyms_data) free(ksyms_data) , ksyms_data = NULL; + ksyms_room = 0; + if(ksyms_index) free(ksyms_index) , ksyms_index = NULL; + ksyms_count = 0; + return 0; +} + +/*********************************/ + +#define VCNT 16 + +static int sysmap_mmap(const char *filename, void (*message)(const char *, ...)) { + struct stat sbuf; + char *endp; + int fd; + char Version[32]; + fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK); + if(fd<0) return 0; + if(fstat(fd, &sbuf) < 0) goto bad_open; + if(!S_ISREG(sbuf.st_mode)) goto bad_open; + if(sbuf.st_size < 5000) goto bad_open; /* if way too small */ + /* Would be shared read-only, but we want '\0' after each name. */ + endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + sysmap_data = endp; + while(*endp==' '){ /* damn Alpha machine types */ + if(strncmp(endp," w ", 19)) goto bad_parse; + endp += 19; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse; + endp++; + } + if(sysmap_data == (caddr_t) -1) goto bad_open; + close(fd); + fd = -1; + sprintf(Version, "Version_%d", linux_version_code); + sysmap_room = 512; + for(;;){ + void *vp; + sysmap_room *= 2; + vp = realloc(sysmap_index, sizeof(symb)*sysmap_room); + if(!vp) goto bad_alloc; + sysmap_index = vp; + for(;;){ + char *vstart; + if(!*endp){ /* if we reached the end */ + int i = VCNT; /* check VCNT times to verify this file */ + if(*Version) goto bad_version; + if(!ksyms_index) return 1; /* if can not verify, assume success */ + while(i--){ +#if 1 + const symb *findme; + const symb *map_symb; + /* Choose VCNT entries from /proc/ksyms to test */ + findme = ksyms_index + (ksyms_count*i/VCNT); + /* Search for them in the System.map */ + map_symb = search(findme->addr, sysmap_index, sysmap_count); + if(map_symb){ + if(map_symb->addr != findme->addr) continue; + /* backup to first matching address */ + while (map_symb != sysmap_index){ + if (map_symb->addr != (map_symb-1)->addr) break; + map_symb--; + } + /* search for name in symbols with same address */ + while (map_symb != (sysmap_index+sysmap_count)){ + if (map_symb->addr != findme->addr) break; + if (!strcmp(map_symb->name,findme->name)) goto good_match; + map_symb++; + } + map_symb--; /* backup to last symbol with matching address */ + message("{%s} {%s}\n",map_symb->name,findme->name); + goto bad_match; + } +good_match:; +#endif + } + return 1; /* success */ + } + sysmap_index[sysmap_count].addr = strtoul(endp, &endp, 16); + if(*endp != ' ') goto bad_parse; + endp++; + if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse; + endp++; + if(*endp != ' ') goto bad_parse; + endp++; + sysmap_index[sysmap_count].name = endp; + vstart = endp; + endp = strchr(endp,'\n'); + if(!endp) goto bad_parse; /* no newline */ + *endp = '\0'; + ++endp; + chop_version(vstart); + if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0'; + if(++sysmap_count >= sysmap_room) break; /* need more space */ + } + } + + if(0){ +bad_match: + message("Warning: %s does not match kernel data.\n", filename); + } + if(0){ +bad_version: + message("Warning: %s has an incorrect kernel version.\n", filename); + } + if(0){ +bad_alloc: + message("Warning: not enough memory available\n"); + } + if(0){ +bad_parse: + message("Warning: %s not parseable as a System.map\n", filename); + } + if(0){ +bad_open: + message("Warning: %s could not be opened as a System.map\n", filename); + } + + sysmap_room=0; + sysmap_count=0; + if(sysmap_index) free(sysmap_index); + sysmap_index = NULL; + if(fd>=0) close(fd); + if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1); + sysmap_data = NULL; + return 0; +} + +/*********************************/ + +static void read_and_parse(void){ + static time_t stamp; /* after data gets old, load /proc/ksyms again */ + if(time(NULL) != stamp){ + read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room); + parse_ksyms(); + memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */ + stamp = time(NULL); + } +} + +/*********************************/ + +static void default_message(const char *format, ...) { + va_list arg; + + va_start (arg, format); + vfprintf (stderr, format, arg); + va_end (arg); +} + +/*********************************/ + +int open_psdb_message(const char *override, void (*message)(const char *, ...)) { + static const char *sysmap_paths[] = { + "/boot/System.map-%s", + "/boot/System.map", + "/lib/modules/%s/System.map", + "/usr/src/linux/System.map", + "/System.map", + NULL + }; + struct utsname uts; + char path[64]; + const char **fmt = sysmap_paths; + const char *env; + read_and_parse(); +#ifdef SYSMAP_FILENAME /* debug feature */ + override = SYSMAP_FILENAME; +#endif + if(override){ /* ought to search some path */ + if(sysmap_mmap(override, message)) return 0; + return -1; /* ought to return "Namelist not found." */ + /* failure is better than ignoring the user & using bad data */ + } + /* Arrrgh, the old man page and code did not match. */ + if ((env = getenv("PS_SYSMAP")) && sysmap_mmap(env, message)) return 0; + if ((env = getenv("PS_SYSTEM_MAP")) && sysmap_mmap(env, message)) return 0; + uname(&uts); + do{ + snprintf(path, sizeof path, *fmt, uts.release); + if (sysmap_mmap(path, message)) return 0; + }while(*++fmt); + /* TODO: Without System.map, no need to keep ksyms loaded. */ + return -1; +} + +/***************************************/ + +int open_psdb(const char *override) { + return open_psdb_message(override, default_message); +} + +/***************************************/ + +#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */ + +/* return pointer to temporary static buffer with function name */ +const char * wchan(unsigned long address) { + const symb *mod_symb; + const symb *map_symb; + const symb *good_symb; + const char *ret; + unsigned hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */ + if(!address) return dash; + read_and_parse(); + + if(hashtable[hash].addr == address) return hashtable[hash].name; + mod_symb = search(address, ksyms_index, ksyms_count); + if(!mod_symb) mod_symb = &fail; + map_symb = search(address, sysmap_index, sysmap_count); + if(!map_symb) map_symb = &fail; + + /* which result is closest? */ + good_symb = (mod_symb->addr > map_symb->addr) + ? mod_symb + : map_symb + ; + if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail; + + /* good_symb->name has the data, but needs to be trimmed */ + ret = good_symb->name; + switch(*ret){ + case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break; + case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break; + case '_': while(*ret=='_') ret++; break; + } + /* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */ + + /* cache name after abbreviation */ + hashtable[hash].addr = address; + hashtable[hash].name = ret; + + return ret; +} diff --git a/proc/output.c b/proc/output.c new file mode 100644 index 00000000..66e4e407 --- /dev/null +++ b/proc/output.c @@ -0,0 +1,52 @@ +/* + Some output conversion routines for libproc + Copyright (C) 1996, Charles Blake. See COPYING for details. +*/ +#include +#include +#include + +/* output a string, converting unprintables to octal as we go, and stopping after + processing max chars of output (accounting for expansion due to octal rep). +*/ +unsigned print_str(FILE* file, char *s, unsigned max) { + int i; + for (i=0; s[i] && i < max; i++) + if (isprint(s[i]) || s[i] == ' ') + fputc(s[i], file); + else { + if (max - i > 3) { + fprintf(file, "\\%03o", s[i]); + i += 3; /* 4 printed, but i counts one */ + } else + return max - i; + } + return max - i; +} + +/* output an argv style NULL-terminated string list, converting unprintables + to octal as we go, separating items of the list by 'sep' and stopping after + processing max chars of output (accounting for expansion due to octal rep). +*/ +unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max) { + int i, n, seplen = strlen(sep); + for (n=0; *strs && n < max; strs++) { + for (i=0; strs[0][i] && n+i < max; i++) + if (isprint(strs[0][i]) || strs[0][i] == ' ') + fputc(strs[0][i], file); + else { + if (max-(n+i) > 3) { + fprintf(file, "\\%03o", strs[0][i]); + n += 3; /* 4 printed, but i counts one */ + } else + return max - n; + } + n += i; + if (n + seplen < max) { + fputs(sep, file); + n += seplen; + } else + return max - n; + } + return max - n; +} diff --git a/proc/procps.h b/proc/procps.h new file mode 100644 index 00000000..44497503 --- /dev/null +++ b/proc/procps.h @@ -0,0 +1,27 @@ +/* The shadow of the original with only common prototypes now. */ +#include +#include + +/* The HZ constant from is replaced by the Hertz variable + * available from "proc/sysinfo.h". + */ + +/* get page info */ +#include + +void *xrealloc(void *oldp, unsigned int size); +void *xmalloc(unsigned int size); +void *xcalloc(void *pointer, int size); + +int mult_lvl_cmp(void* a, void* b); +int node_mult_lvl_cmp(void* a, void* b); + +char *user_from_uid(uid_t uid); +char *group_from_gid(gid_t gid); + +const char * wchan(unsigned long address); +int open_psdb(const char *override); +int open_psdb_message(const char *override, void (*message)(const char *, ...)); + +unsigned print_str (FILE* file, char *s, unsigned max); +unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max); diff --git a/proc/pwcache.c b/proc/pwcache.c new file mode 100644 index 00000000..8fff60b3 --- /dev/null +++ b/proc/pwcache.c @@ -0,0 +1,74 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* This file is placed under the conditions of the GNU Library * +* General Public License, version 2, or any later version. * +* See file ../COPYING for information on distribution conditions. * +\***********************************************************************/ + +#include +#include +#include +#include +#include "proc/procps.h" +#include + +#define HASHSIZE 16 /* power of 2 */ +#define HASH(x) ((x) & (HASHSIZE - 1)) + +#define NAMESIZE 16 +#define NAMELENGTH "15" + +static struct pwbuf { + uid_t uid; + char name[NAMESIZE]; + struct pwbuf *next; +} *pwhash[HASHSIZE]; + +char *user_from_uid(uid_t uid) +{ + struct pwbuf **p; + struct passwd *pw; + + p = &pwhash[HASH(uid)]; + while (*p) { + if ((*p)->uid == uid) + return((*p)->name); + p = &(*p)->next; + } + *p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf)); + (*p)->uid = uid; + if ((pw = getpwuid(uid)) == NULL) + sprintf((*p)->name, "#%d", uid); + else + sprintf((*p)->name, "%-." NAMELENGTH "s", pw->pw_name); + (*p)->next = NULL; + return((*p)->name); +} + +static struct grpbuf { + gid_t gid; + char name[NAMESIZE]; + struct grpbuf *next; +} *grphash[HASHSIZE]; + +char *group_from_gid(gid_t gid) +{ + struct grpbuf **g; + struct group *gr; + + g = &grphash[HASH(gid)]; + while (*g) { + if ((*g)->gid == gid) + return((*g)->name); + g = &(*g)->next; + } + *g = (struct grpbuf *) malloc(sizeof(struct grpbuf)); + (*g)->gid = gid; + if ((gr = getgrgid(gid)) == NULL) + sprintf((*g)->name, "#%d", gid); + else + sprintf((*g)->name, "%-." NAMELENGTH "s", gr->gr_name); + (*g)->next = NULL; + return((*g)->name); +} diff --git a/proc/readproc.c b/proc/readproc.c new file mode 100644 index 00000000..21a3996e --- /dev/null +++ b/proc/readproc.c @@ -0,0 +1,543 @@ +/* + * New Interface to Process Table -- PROCTAB Stream (a la Directory streams) + * Copyright (C) 1996 Charles L. Blake. + * Copyright (C) 1998 Michael K. Johnson + * May be distributed under the conditions of the + * GNU Library General Public License; a copy is in COPYING + */ +#include "proc/version.h" +#include "proc/readproc.h" +#include "proc/devname.h" +#include "proc/procps.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define Do(x) (flags & PROC_ ## x) /* convenient shorthand */ + +/* initiate a process table scan + */ +PROCTAB* openproc(int flags, ...) { + va_list ap; + PROCTAB* PT = xmalloc(sizeof(PROCTAB)); + + if (Do(PID)) + PT->procfs = NULL; + else if (!(PT->procfs = opendir("/proc"))) + return NULL; + PT->flags = flags; + va_start(ap, flags); /* Init args list */ + if (Do(PID)) + PT->pids = va_arg(ap, pid_t*); + else if (Do(TTY)) + PT->ttys = va_arg(ap, dev_t*); + else if (Do(UID)) { + PT->uids = va_arg(ap, uid_t*); + PT->nuid = va_arg(ap, int); + } else if (Do(STAT)) + PT->stats = va_arg(ap, char*); + va_end(ap); /* Clean up args list */ + if (Do(ANYTTY) && Do(TTY)) + PT->flags = PT->flags & ~PROC_TTY; /* turn off TTY flag */ + return PT; +} + +/* terminate a process table scan + */ +void closeproc(PROCTAB* PT) { + if (PT){ + if (PT->procfs) closedir(PT->procfs); + free(PT); + } +} + +/* deallocate the space allocated by readproc if the passed rbuf was NULL + */ +void freeproc(proc_t* p) { + if (!p) /* in case p is NULL */ + return; + /* ptrs are after strings to avoid copying memory when building them. */ + /* so free is called on the address of the address of strvec[0]. */ + if (p->cmdline) + free((void*)*p->cmdline); + if (p->environ) + free((void*)*p->environ); + free(p); +} + + + +static void status2proc (char* S, proc_t* P, int fill) { + char* tmp; + if (fill == 1) { + memset(P->cmd, 0, sizeof P->cmd); + sscanf (S, "Name:\t%15c", P->cmd); + tmp = strchr(P->cmd,'\n'); + *tmp='\0'; + tmp = strstr (S,"State"); + sscanf (tmp, "State:\t%c", &P->state); + } + + tmp = strstr (S,"Pid:"); + if(tmp) sscanf (tmp, + "Pid:\t%d\n" + "PPid:\t%d\n", + &P->pid, + &P->ppid + ); + else fprintf(stderr, "Internal error!\n"); + + tmp = strstr (S,"Uid:"); + if(tmp) sscanf (tmp, + "Uid:\t%d\t%d\t%d\t%d", + &P->ruid, &P->euid, &P->suid, &P->fuid + ); + else fprintf(stderr, "Internal error!\n"); + + tmp = strstr (S,"Gid:"); + if(tmp) sscanf (tmp, + "Gid:\t%d\t%d\t%d\t%d", + &P->rgid, &P->egid, &P->sgid, &P->fgid + ); + else fprintf(stderr, "Internal error!\n"); + + tmp = strstr (S,"VmSize:"); + if(tmp) sscanf (tmp, + "VmSize: %lu kB\n" + "VmLck: %lu kB\n" + "VmRSS: %lu kB\n" + "VmData: %lu kB\n" + "VmStk: %lu kB\n" + "VmExe: %lu kB\n" + "VmLib: %lu kB\n", + &P->vm_size, &P->vm_lock, &P->vm_rss, &P->vm_data, + &P->vm_stack, &P->vm_exe, &P->vm_lib + ); + else /* looks like an annoying kernel thread */ + { + P->vm_size = 0; + P->vm_lock = 0; + P->vm_rss = 0; + P->vm_data = 0; + P->vm_stack = 0; + P->vm_exe = 0; + P->vm_lib = 0; + } + + tmp = strstr (S,"SigPnd:"); + if(tmp) sscanf (tmp, +#ifdef SIGNAL_STRING + "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s", + P->signal, P->blocked, P->sigignore, P->sigcatch +#else + "SigPnd: %Lx SigBlk: %Lx SigIgn: %Lx %*s %Lx", + &P->signal, &P->blocked, &P->sigignore, &P->sigcatch +#endif + ); + else fprintf(stderr, "Internal error!\n"); +} + + + +/* stat2proc() makes sure it can handle arbitrary executable file basenames + * for `cmd', i.e. those with embedded whitespace or embedded ')'s. + * Such names confuse %s (see scanf(3)), so the string is split and %39c + * is used instead. (except for embedded ')' "(%[^)]c)" would work. + */ +static void stat2proc(char* S, proc_t* P) { + int num; + char* tmp = strrchr(S, ')'); /* split into "PID (cmd" and "" */ + *tmp = '\0'; /* replace trailing ')' with NUL */ + /* fill in default values for older kernels */ + P->exit_signal = SIGCHLD; + P->processor = 0; + /* parse these two strings separately, skipping the leading "(". */ + memset(P->cmd, 0, sizeof P->cmd); /* clear even though *P xcalloc'd ?! */ + sscanf(S, "%d (%15c", &P->pid, P->cmd); /* comm[16] in kernel */ + num = sscanf(tmp + 2, /* skip space after ')' too */ + "%c " + "%d %d %d %d %d " + "%lu %lu %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %ld %ld " + "%lu %lu " + "%ld " + "%lu %lu %lu %lu %lu %lu " + "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */ + "%lu %lu %lu " + "%d %d", + &P->state, + &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid, + &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, &P->utime, &P->stime, + &P->cutime, &P->cstime, &P->priority, &P->nice, &P->timeout, &P->it_real_value, + &P->start_time, &P->vsize, + &P->rss, + &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip, +/* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */ + &P->wchan, &P->nswap, &P->cnswap, +/* -- Linux 2.0.35 ends here -- */ + &P->exit_signal, &P->processor /* 2.2.1 ends with "exit_signal" */ +/* -- Linux 2.2.8 and 2.3.47 end here -- */ + ); + + /* fprintf(stderr, "stat2proc converted %d fields.\n",num); */ + if (P->tty == 0) + P->tty = -1; /* the old notty val, update elsewhere bef. moving to 0 */ +} + +static void statm2proc(char* s, proc_t* P) { + int num; + num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld", + &P->size, &P->resident, &P->share, + &P->trs, &P->lrs, &P->drs, &P->dt); +/* fprintf(stderr, "statm2proc converted %d fields.\n",num); */ +} + +static int file2str(char *directory, char *what, char *ret, int cap) { + static char filename[80]; + int fd, num_read; + + sprintf(filename, "%s/%s", directory, what); + if ( (fd = open(filename, O_RDONLY, 0)) == -1 ) return -1; + if ( (num_read = read(fd, ret, cap - 1)) <= 0 ) num_read = -1; + else ret[num_read] = 0; + close(fd); + return num_read; +} + +static char** file2strvec(char* directory, char* what) { + char buf[2048]; /* read buf bytes at a time */ + char *p, *rbuf = 0, *endbuf, **q, **ret; + int fd, tot = 0, n, c, end_of_file = 0; + int align; + + sprintf(buf, "%s/%s", directory, what); + if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return NULL; + + /* read whole file into a memory buffer, allocating as we go */ + while ((n = read(fd, buf, sizeof buf - 1)) > 0) { + if (n < sizeof buf - 1) + end_of_file = 1; + if (n == 0 && rbuf == 0) + return NULL; /* process died between our open and read */ + if (n < 0) { + if (rbuf) + free(rbuf); + return NULL; /* read error */ + } + if (end_of_file && buf[n-1]) /* last read char not null */ + buf[n++] = '\0'; /* so append null-terminator */ + rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */ + memcpy(rbuf + tot, buf, n); /* copy buffer into it */ + tot += n; /* increment total byte ctr */ + if (end_of_file) + break; + } + close(fd); + if (n <= 0 && !end_of_file) { + if (rbuf) free(rbuf); + return NULL; /* read error */ + } + endbuf = rbuf + tot; /* count space for pointers */ + align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1)); + for (c = 0, p = rbuf; p < endbuf; p++) + if (!*p) + c += sizeof(char*); + c += sizeof(char*); /* one extra for NULL term */ + + rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */ + endbuf = rbuf + tot; /* addr just past data buf */ + q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */ + *q++ = p = rbuf; /* point ptrs to the strings */ + endbuf--; /* do not traverse final NUL */ + while (++p < endbuf) + if (!*p) /* NUL char implies that */ + *q++ = p+1; /* next string -> next char */ + + *q = 0; /* null ptr list terminator */ + return ret; +} + + +/* These are some nice GNU C expression subscope "inline" functions. + * The can be used with arbitrary types and evaluate their arguments + * exactly once. + */ + +/* Test if item X of type T is present in the 0 terminated list L */ +# define XinL(T, X, L) ( { \ + T x = (X), *l = (L); \ + while (*l && *l != x) l++; \ + *l == x; \ + } ) + +/* Test if item X of type T is present in the list L of length N */ +# define XinLN(T, X, L, N) ( { \ + T x = (X), *l = (L); \ + int i = 0, n = (N); \ + while (i < n && l[i] != x) i++; \ + i < n && l[i] == x; \ + } ) + +/* readproc: return a pointer to a proc_t filled with requested info about the + * next process available matching the restriction set. If no more such + * processes are available, return a null pointer (boolean false). Use the + * passed buffer instead of allocating space if it is non-NULL. */ + +/* This is optimized so that if a PID list is given, only those files are + * searched for in /proc. If other lists are given in addition to the PID list, + * the same logic can follow through as for the no-PID list case. This is + * fairly complex, but it does try to not to do any unnecessary work. + * Unfortunately, the reverse filtering option in which any PID *except* the + * ones listed is pursued. + */ +proc_t* readproc(PROCTAB* PT, proc_t* rbuf) { + static struct direct *ent; /* dirent handle */ + static struct stat sb; /* stat buffer */ + static char path[32], sbuf[512]; /* bufs for stat,statm */ + int allocated = 0, matched = 0; /* flags */ + proc_t *p = NULL; + + /* loop until a proc matching restrictions is found or no more processes */ + /* I know this could be a while loop -- this way is easier to indent ;-) */ +next_proc: /* get next PID for consideration */ + +/*printf("PT->flags is 0x%08x\n", PT->flags);*/ +#define flags (PT->flags) + + if (Do(PID)) { + if (!*PT->pids) /* set to next item in pids */ + return NULL; + sprintf(path, "/proc/%d", *(PT->pids)++); + matched = 1; + } else { /* get next numeric /proc ent */ + while ((ent = readdir(PT->procfs)) && + (*ent->d_name < '0' || *ent->d_name > '9')) + ; + if (!ent || !ent->d_name) + return NULL; + sprintf(path, "/proc/%s", ent->d_name); + } + if (stat(path, &sb) == -1) /* no such dirent (anymore) */ + goto next_proc; + if (Do(UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid)) + goto next_proc; /* not one of the requested uids */ + + if (!allocated) { /* assign mem for return buf */ + p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */ + allocated = 1; /* remember space is set up */ + } + p->euid = sb.st_uid; /* need a way to get real uid */ + + if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1) + goto next_proc; /* error reading /proc/#/stat */ + stat2proc(sbuf, p); /* parse /proc/#/stat */ + + if (!matched && Do(TTY) && !XinL(dev_t, p->tty, PT->ttys)) + goto next_proc; /* not one of the requested ttys */ + + if (!matched && Do(ANYTTY) && p->tty == -1) + goto next_proc; /* no controlling terminal */ + + if (!matched && Do(STAT) && !strchr(PT->stats,p->state)) + goto next_proc; /* not one of the requested states */ + + if (Do(FILLMEM)) { /* read, parse /proc/#/statm */ + if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 ) + statm2proc(sbuf, p); /* ignore statm errors here */ + } /* statm fields just zero */ + + if (Do(FILLSTATUS)) { /* read, parse /proc/#/status */ + if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){ + status2proc(sbuf, p, 0 /*FIXME*/); + } + } + + /* some number->text resolving which is time consuming */ + if (Do(FILLUSR)){ + strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser); + strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup); + if(Do(FILLSTATUS)) { + strncpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser); + strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup); + strncpy(p->suser, user_from_uid(p->suid), sizeof p->suser); + strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup); + strncpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser); + strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup); + } + } + + if (Do(FILLCMD)) /* read+parse /proc/#/cmdline */ + p->cmdline = file2strvec(path, "cmdline"); + if (Do(FILLENV)) /* read+parse /proc/#/environ */ + p->environ = file2strvec(path, "environ"); + + if (p->state == 'Z') /* fixup cmd for zombies */ + strncat(p->cmd," ", sizeof p->cmd); + + return p; +} +#undef flags + +/* ps_readproc: return a pointer to a proc_t filled with requested info about the + * next process available matching the restriction set. If no more such + * processes are available, return a null pointer (boolean false). Use the + * passed buffer instead of allocating space if it is non-NULL. */ + +/* This is optimized so that if a PID list is given, only those files are + * searched for in /proc. If other lists are given in addition to the PID list, + * the same logic can follow through as for the no-PID list case. This is + * fairly complex, but it does try to not to do any unnecessary work. + * Unfortunately, the reverse filtering option in which any PID *except* the + * ones listed is pursued. + */ +proc_t* ps_readproc(PROCTAB* PT, proc_t* rbuf) { + static struct direct *ent; /* dirent handle */ + static struct stat sb; /* stat buffer */ + static char path[32], sbuf[512]; /* bufs for stat,statm */ + int allocated = 0 /* , matched = 0 */ ; /* flags */ + proc_t *p = NULL; + + /* loop until a proc matching restrictions is found or no more processes */ + /* I know this could be a while loop -- this way is easier to indent ;-) */ +next_proc: /* get next PID for consideration */ + +/*printf("PT->flags is 0x%08x\n", PT->flags);*/ +#define flags (PT->flags) + + while ((ent = readdir(PT->procfs)) && + (*ent->d_name < '0' || *ent->d_name > '9')) + ; + if (!ent || !ent->d_name) + return NULL; + sprintf(path, "/proc/%s", ent->d_name); + + if (stat(path, &sb) == -1) /* no such dirent (anymore) */ + goto next_proc; + + if (!allocated) { /* assign mem for return buf */ + p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */ + allocated = 1; /* remember space is set up */ + } + p->euid = sb.st_uid; /* need a way to get real uid */ + + if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1) + goto next_proc; /* error reading /proc/#/stat */ + stat2proc(sbuf, p); /* parse /proc/#/stat */ + +/* if (Do(FILLMEM)) {*/ /* read, parse /proc/#/statm */ + if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 ) + statm2proc(sbuf, p); /* ignore statm errors here */ +/* } */ /* statm fields just zero */ + + /* if (Do(FILLSTATUS)) { */ /* read, parse /proc/#/status */ + if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){ + status2proc(sbuf, p, 0 /*FIXME*/); + } +/* }*/ + + /* some number->text resolving which is time consuming */ + /* if (Do(FILLUSR)){ */ + strncpy(p->euser, user_from_uid(p->euid), sizeof p->euser); + strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup); +/* if(Do(FILLSTATUS)) { */ + strncpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser); + strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup); + strncpy(p->suser, user_from_uid(p->suid), sizeof p->suser); + strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup); + strncpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser); + strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup); +/* }*/ +/* }*/ + +/* if (Do(FILLCMD)) */ /* read+parse /proc/#/cmdline */ + p->cmdline = file2strvec(path, "cmdline"); +/* if (Do(FILLENV)) */ /* read+parse /proc/#/environ */ + p->environ = file2strvec(path, "environ"); + + if (p->state == 'Z') /* fixup cmd for zombies */ + strncat(p->cmd," ", sizeof p->cmd); + + return p; +} +#undef flags + + +void look_up_our_self(proc_t *p) { + static char path[32], sbuf[512]; /* bufs for stat,statm */ + sprintf(path, "/proc/%d", getpid()); + file2str(path, "stat", sbuf, sizeof sbuf); + stat2proc(sbuf, p); /* parse /proc/#/stat */ + file2str(path, "statm", sbuf, sizeof sbuf); + statm2proc(sbuf, p); /* ignore statm errors here */ + file2str(path, "status", sbuf, sizeof sbuf); + status2proc(sbuf, p, 0 /*FIXME*/); +} + + +/* Convenient wrapper around openproc and readproc to slurp in the whole process + * tree subset satisfying the constraints of flags and the optional PID list. + * Free allocated memory with freeproctree(). The tree structure is a classic + * left-list children + right-list siblings. The algorithm is a two-pass of the + * process table. Since most process trees will have children with strictly + * increasing PIDs, most of the structure will be picked up in the first pass. + * The second loop then cleans up any nodes which turn out to have preceeded + * their parent in /proc order. + */ + +/* Traverse tree 't' breadth-first looking for a process with pid p */ +static proc_t* LookupPID(proc_t* t, pid_t p) { + proc_t* tmp = NULL; + if (!t) + return NULL; + if (t->pid == p) /* look here/terminate recursion */ + return t; + if ((tmp = LookupPID(t->l, p))) /* recurse over children */ + return tmp; + for (; t; t=t->r) /* recurse over siblings */ + if ((tmp = LookupPID(tmp, p))) + return tmp; + return NULL; +} + +/* Convenient wrapper around openproc and readproc to slurp in the whole process + * table subset satisfying the constraints of flags and the optional PID list. + * Free allocated memory with freeproctab(). Access via tab[N]->member. The + * pointer list is NULL terminated. + */ +proc_t** readproctab(int flags, ...) { + PROCTAB* PT = NULL; + proc_t** tab = NULL; + int n = 0; + va_list ap; + + va_start(ap, flags); /* pass through args to openproc */ + if (Do(UID)) { + /* temporary variables to ensure that va_arg() instances + * are called in the right order + */ + uid_t* u; + int i; + + u = va_arg(ap, uid_t*); + i = va_arg(ap, int); + PT = openproc(flags, u, i); + } + else if (Do(PID) || Do(TTY) || Do(STAT)) + PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */ + else + PT = openproc(flags); + va_end(ap); + do { /* read table: */ + tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */ + tab[n] = readproc(PT, NULL); /* final null to terminate */ + } while (tab[n++]); /* stop when NULL reached */ + closeproc(PT); + return tab; +} diff --git a/proc/readproc.h b/proc/readproc.h new file mode 100644 index 00000000..81195944 --- /dev/null +++ b/proc/readproc.h @@ -0,0 +1,200 @@ +#ifndef PROCPS_PROC_READPROC_H +#define PROCPS_PROC_READPROC_H +/* + * New Interface to Process Table -- PROCTAB Stream (a la Directory streams) + * Copyright (C) 1996 Charles L. Blake. + * Copyright (C) 1998 Michael K. Johnson + * May be distributed under the terms of the + * GNU Library General Public License, a copy of which is provided + * in the file COPYING + */ + + +#define SIGNAL_STRING + + +/* + ld cutime, cstime, priority, nice, timeout, it_real_value, rss, + c state, + d ppid, pgrp, session, tty, tpgid, + s signal, blocked, sigignore, sigcatch, + lu flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime, + lu rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip, + lu start_time, vsize, wchan, nswap, cnswap, +*/ + +/* Basic data structure which holds all information we can get about a process. + * (unless otherwise specified, fields are read from /proc/#/stat) + * + * Most of it comes from task_struct in linux/sched.h + */ +typedef struct proc_s { +#ifdef SIGNAL_STRING + char + /* Linux 2.1.7x and up have more signals. This handles 88. */ + signal[24], /* mask of pending signals */ + blocked[24], /* mask of blocked signals */ + sigignore[24], /* mask of ignored signals */ + sigcatch[24]; /* mask of caught signals */ +#else + long long + /* Linux 2.1.7x and up have more signals. This handles 64. */ + signal, /* mask of pending signals */ + blocked, /* mask of blocked signals */ + sigignore, /* mask of ignored signals */ + sigcatch; /* mask of caught signals */ +#endif + long + cutime, /* cumulative utime of process and reaped children */ + cstime, /* cumulative stime of process and reaped children */ + priority, /* kernel scheduling priority */ + timeout, /* ? */ + nice, /* standard unix nice level of process */ + rss, /* resident set size from /proc/#/stat (pages) */ + it_real_value, /* ? */ + /* the next 7 members come from /proc/#/statm */ + size, /* total # of pages of memory */ + resident, /* number of resident set (non-swapped) pages (4k) */ + share, /* number of pages of shared (mmap'd) memory */ + trs, /* text resident set size */ + lrs, /* shared-lib resident set size */ + drs, /* data resident set size */ + dt; /* dirty pages */ + unsigned long + /* FIXME: are these longs? Maybe when the alpha does PCI bounce buffers */ + vm_size, /* same as vsize in kb */ + vm_lock, /* locked pages in kb */ + vm_rss, /* same as rss in kb */ + vm_data, /* data size */ + vm_stack, /* stack size */ + vm_exe, /* executable size */ + vm_lib, /* library size (all pages, not just used ones) */ + vsize, /* number of pages of virtual memory ... */ + rss_rlim, /* resident set size limit? */ + flags, /* kernel flags for the process */ + min_flt, /* number of minor page faults since process start */ + maj_flt, /* number of major page faults since process start */ + cmin_flt, /* cumulative min_flt of process and child processes */ + cmaj_flt, /* cumulative maj_flt of process and child processes */ + nswap, /* ? */ + cnswap, /* cumulative nswap ? */ + utime, /* user-mode CPU time accumulated by process */ + stime, /* kernel-mode CPU time accumulated by process */ + start_code, /* address of beginning of code segment */ + end_code, /* address of end of code segment */ + start_stack, /* address of the bottom of stack for the process */ + kstk_esp, /* kernel stack pointer */ + kstk_eip, /* kernel instruction pointer */ + start_time, /* start time of process -- seconds since 1-1-70 */ + wchan; /* address of kernel wait channel proc is sleeping in */ + struct proc_s *l, /* ptrs for building arbitrary linked structs */ + *r; /* (i.e. singly/doubly-linked lists and trees */ + char + **environ, /* environment string vector (/proc/#/environ) */ + **cmdline; /* command line string vector (/proc/#/cmdline) */ + char + /* Be compatible: Digital allows 16 and NT allows 14 ??? */ + ruser[16], /* real user name */ + euser[16], /* effective user name */ + suser[16], /* saved user name */ + fuser[16], /* filesystem user name */ + rgroup[16], /* real group name */ + egroup[16], /* effective group name */ + sgroup[16], /* saved group name */ + fgroup[16], /* filesystem group name */ + cmd[16]; /* basename of executable file in call to exec(2) */ + int + ruid, rgid, /* real */ + euid, egid, /* effective */ + suid, sgid, /* saved */ + fuid, fgid, /* fs (used for file access only) */ + pid, /* process id */ + ppid, /* pid of parent process */ + pgrp, /* process group id */ + session, /* session id */ + tty, /* full device number of controlling terminal */ + tpgid, /* terminal process group id */ + exit_signal, /* might not be SIGCHLD */ + processor; /* current (or most recent?) CPU */ + unsigned + pcpu; /* %CPU usage (is not filled in by readproc!!!) */ + char + state; /* single-char code for process state (S=sleeping) */ +} proc_t; + +/* PROCTAB: data structure holding the persistent information readproc needs + * from openproc(). The setup is intentionally similar to the dirent interface + * and other system table interfaces (utmp+wtmp come to mind). + */ +#include +#include +#include +typedef struct { + DIR* procfs; + int flags; + pid_t* pids; /* pids of the procs */ + dev_t* ttys; /* devnos of the cttys */ + uid_t* uids; /* uids of procs */ + int nuid; /* cannot really sentinel-terminate unsigned short[] */ + char* stats; /* status chars (actually output into /proc//stat) */ +} PROCTAB; + +/* initialize a PROCTAB structure holding needed call-to-call persistent data + */ +PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ ); + + +/* Convenient wrapper around openproc and readproc to slurp in the whole process + * table subset satisfying the constraints of flags and the optional PID list. + * Free allocated memory with freeproctab(). Access via tab[N]->member. The + * pointer list is NULL terminated. + */ +proc_t** readproctab(int flags, ... /* same as openproc */ ); + +/* clean-up open files, etc from the openproc() + */ +void closeproc(PROCTAB* PT); + +/* retrieve the next process matching the criteria set by the openproc() + */ +proc_t* readproc(PROCTAB* PT, proc_t* return_buf); +proc_t* ps_readproc(PROCTAB* PT, proc_t* return_buf); + +void look_up_our_self(proc_t *p); + +/* deallocate space allocated by readproc + */ +void freeproc(proc_t* p); + +/* openproc/readproctab: + * + * Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be + * opened.) By default readproc will consider all processes as valid to parse + * and return, but not actually fill in the cmdline, environ, and /proc/#/statm + * derived memory fields. + * + * `flags' (a bitwise-or of PROC_* below) modifies the default behavior. The + * "fill" options will cause more of the proc_t to be filled in. The "filter" + * options all use the second argument as the pointer to a list of objects: + * process status', process id's, user id's, and tty device numbers. The third + * argument is the length of the list (currently only used for lists of user + * id's since unsigned short[] supports no convenient termination sentinel.) + */ +#define PROC_FILLANY 0x00 /* either stat or status will do */ +#define PROC_FILLMEM 0x01 /* read statm into the appropriate proc_t entries */ +#define PROC_FILLCMD 0x02 /* alloc and fill in `cmdline' part of proc_t */ +#define PROC_FILLENV 0x04 /* alloc and fill in `environ' part of proc_t */ +#define PROC_FILLUSR 0x08 /* resolve user id number -> user name via passwd */ +#define PROC_FILLSTATUS 0x10 +#define PROC_FILLSTAT 0x20 +#define PROC_FILLBUG 0x3f /* No idea what we need */ + + +/* Obsolete, consider only processes with one of the passed: */ +#define PROC_PID 0x0100 /* process id numbers ( 0 terminated) */ +#define PROC_TTY 0x0200 /* ctty device nos. ( 0 terminated) */ +#define PROC_UID 0x0400 /* user id numbers ( length needed ) */ +#define PROC_STAT 0x0800 /* status fields ('\0' terminated) */ +#define PROC_ANYTTY 0x1000 /* proc must have a controlling terminal */ + +#endif diff --git a/proc/sig.c b/proc/sig.c new file mode 100644 index 00000000..46dd8470 --- /dev/null +++ b/proc/sig.c @@ -0,0 +1,234 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include + +/* Linux signals: + * + * SIGSYS is required by Unix98. + * SIGEMT is part of SysV, BSD, and ancient UNIX tradition. + * + * They are provided by these Linux ports: alpha, mips, sparc, and sparc64. + * You get SIGSTKFLT and SIGUNUSED instead on i386, m68k, ppc, and arm. + * (this is a Linux & libc bug -- both must be fixed) + * + * Total garbage: SIGIO SIGINFO SIGIOT SIGLOST SIGCLD + * (popular ones are handled as aliases) + * Nearly garbage: SIGSTKFLT SIGUNUSED (nothing else to fill slots) + */ + +/* Linux 2.3.29 replaces SIGUNUSED with the standard SIGSYS signal */ +#ifndef SIGSYS +# warning Standards require that define SIGSYS +# define SIGSYS SIGUNUSED +#endif + +/* If we see both, it is likely SIGSTKFLT (junk) was replaced. */ +#ifdef SIGEMT +# undef SIGSTKFLT +#endif + +#ifndef SIGRTMIN +# warning Standards require that define SIGRTMIN; assuming 32 +# define SIGRTMIN 32 +#endif + +/* It seems the SPARC libc does not know the kernel supports SIGPWR. */ +#ifndef SIGPWR +# warning Your header files lack SIGPWR. (assuming it is number 29) +# define SIGPWR 29 +#endif + +typedef struct mapstruct { + char *name; + int num; +} mapstruct; + + +static mapstruct sigtable[] = { + {"ABRT", SIGABRT}, /* IOT */ + {"ALRM", SIGALRM}, + {"BUS", SIGBUS}, + {"CHLD", SIGCHLD}, /* CLD */ + {"CONT", SIGCONT}, +#ifdef SIGEMT + {"EMT", SIGEMT}, +#endif + {"FPE", SIGFPE}, + {"HUP", SIGHUP}, + {"ILL", SIGILL}, + {"INT", SIGINT}, + {"KILL", SIGKILL}, + {"PIPE", SIGPIPE}, + {"POLL", SIGPOLL}, /* IO */ + {"PROF", SIGPROF}, + {"PWR", SIGPWR}, + {"QUIT", SIGQUIT}, + {"SEGV", SIGSEGV}, +#ifdef SIGSTKFLT + {"STKFLT", SIGSTKFLT}, +#endif + {"STOP", SIGSTOP}, + {"SYS", SIGSYS}, /* UNUSED */ + {"TERM", SIGTERM}, + {"TRAP", SIGTRAP}, + {"TSTP", SIGTSTP}, + {"TTIN", SIGTTIN}, + {"TTOU", SIGTTOU}, + {"URG", SIGURG}, + {"USR1", SIGUSR1}, + {"USR2", SIGUSR2}, + {"VTALRM", SIGVTALRM}, + {"WINCH", SIGWINCH}, + {"XCPU", SIGXCPU}, + {"XFSZ", SIGXFSZ} +}; + +static const int number_of_signals = sizeof(sigtable)/sizeof(mapstruct); + +static int compare_signal_names(const void *a, const void *b){ + return strcasecmp( ((mapstruct*)a)->name, ((mapstruct*)b)->name ); +} + +/* return -1 on failure */ +int signal_name_to_number(char *name){ + const mapstruct *ptr; + mapstruct ms; + long val; + char *endp; + int offset; + + /* clean up name */ + if(!strncasecmp(name,"SIG",3)) name += 3; + + if(!strcasecmp(name,"CLD")) return SIGCHLD; + if(!strcasecmp(name,"IO")) return SIGPOLL; + if(!strcasecmp(name,"IOT")) return SIGABRT; + + /* search the table */ + ms.name = name; + ptr = bsearch(&ms, sigtable, number_of_signals, + sizeof(mapstruct), compare_signal_names + ); + if(ptr) return ptr->num; + + if(!strcasecmp(name,"RTMIN")) return SIGRTMIN; + if(!strcasecmp(name,"EXIT")) return 0; + if(!strcasecmp(name,"NULL")) return 0; + + offset = 0; + if(!strncasecmp(name,"RTMIN+",6)){ + name += 6; + offset = SIGRTMIN; + } + + /* not found, so try as a number */ + val = strtol(name,&endp,10); + if(*endp) return -1; /* not valid */ + if(val+SIGRTMIN>127) return -1; /* not valid */ + return val+offset; +} + +static const char *signal_number_to_name(int signo){ + static char buf[32]; + int n = number_of_signals; + signo &= 0x7f; /* need to process exit values too */ + while(n--){ + if(sigtable[n].num==signo) return sigtable[n].name; + } + if(signo == SIGRTMIN) return "RTMIN"; + if(signo) sprintf(buf, "RTMIN+%d", signo-SIGRTMIN); + else strcpy(buf,"0"); /* AIX has NULL; Solaris has EXIT */ + return buf; +} + +int print_given_signals(int argc, char *argv[], int max_line){ + char buf[1280]; /* 128 signals, "RTMIN+xx" is largest */ + int ret = 0; /* to be used as exit code by caller */ + int place = 0; /* position on this line */ + int amt; + if(argc > 128) return 1; + while(argc--){ + char tmpbuf[16]; + char *txt; /* convenient alias */ + txt = *argv; + if(*txt >= '0' && *txt <= '9'){ + long val; + char *endp; + val = strtol(txt,&endp,10); + if(*endp){ + fprintf(stderr, "Signal \"%s\" not known.\n", txt); + ret = 1; + goto end; + } + amt = sprintf(tmpbuf, "%s", signal_number_to_name(val)); + }else{ + int sno; + sno = signal_name_to_number(txt); + if(sno == -1){ + fprintf(stderr, "Signal \"%s\" not known.\n", txt); + ret = 1; + goto end; + } + amt = sprintf(tmpbuf, "%d", sno); + } + + if(!place){ + strcpy(buf,tmpbuf); + place = amt; + goto end; + } + if(amt+place+1 > max_line){ + printf("%s\n", buf); + strcpy(buf,tmpbuf); + place = amt; + goto end; + } + sprintf(buf+place, " %s", tmpbuf); + place += amt+1; +end: + argv++; + } + if(place) printf("%s\n", buf); + return ret; +} + +void pretty_print_signals(void){ + int i = 0; + while(++i <= number_of_signals){ + int n; + n = printf("%2d %s", i, signal_number_to_name(i)); + if(i%7) printf(" \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + n); + else printf("\n"); + } + if((i-1)%7) printf("\n"); +} + +void unix_print_signals(void){ + int pos = 0; + int i = 0; + while(++i <= number_of_signals){ + if(i-1) printf("%c", (pos>73)?(pos=0,'\n'):(pos++,' ') ); + pos += printf("%s", signal_number_to_name(i)); + } + printf("\n"); +} + +/* sanity check */ +static int init_signal_list(void) __attribute__((constructor)); +static int init_signal_list(void){ + if(number_of_signals != 31){ + fprintf(stderr, "WARNING: %d signals -- adjust and recompile.\n", number_of_signals); + } + return 0; +} diff --git a/proc/sig.h b/proc/sig.h new file mode 100644 index 00000000..3f232c57 --- /dev/null +++ b/proc/sig.h @@ -0,0 +1,19 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* return -1 on failure */ +extern int signal_name_to_number(char *name); + +extern int print_given_signals(int argc, char *argv[], int max_line); + +extern void pretty_print_signals(void); + +extern void unix_print_signals(void); diff --git a/proc/status.c b/proc/status.c new file mode 100644 index 00000000..712743aa --- /dev/null +++ b/proc/status.c @@ -0,0 +1,29 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* This file is placed under the conditions of the GNU Library * +* General Public License, version 2, or any later version. * +* See file COPYING for information on distribution conditions. * +\***********************************************************************/ + + +#include "proc/procps.h" +#include "proc/readproc.h" + +char * status(proc_t* task) { + static char buf[4] = " "; + + buf[0] = task->state; + if (task->rss == 0 && task->state != 'Z') + buf[1] = 'W'; + else + buf[1] = ' '; + if (task->nice < 0) + buf[2] = '<'; + else if (task->nice > 0) + buf[2] = 'N'; + else + buf[2] = ' '; + + return(buf); +} diff --git a/proc/status.h b/proc/status.h new file mode 100644 index 00000000..562efae4 --- /dev/null +++ b/proc/status.h @@ -0,0 +1,4 @@ +#ifndef __PROC_STATUS_H +#define __PROC_STATUS_H +extern char *status(proc_t* task); +#endif diff --git a/proc/sysinfo.c b/proc/sysinfo.c new file mode 100644 index 00000000..fbb14daf --- /dev/null +++ b/proc/sysinfo.c @@ -0,0 +1,325 @@ +/***********************************************************************\ +* Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com * +* * +* This file is placed under the conditions of the GNU Library * +* General Public License, version 2, or any later version. * +* See file COPYING for information on distribution conditions. * +\***********************************************************************/ + +/* File for parsing top-level /proc entities. */ + +#include +#include +#include +#include + +#include +#include +#include "proc/version.h" +#include "proc/sysinfo.h" /* include self to verify prototypes */ + +#ifndef HZ +#include /* htons */ +#endif + +long smp_num_cpus; /* number of CPUs */ + +#define BAD_OPEN_MESSAGE \ +"Error: /proc must be mounted\n" \ +" To mount /proc at boot you need an /etc/fstab line like:\n" \ +" /proc /proc proc defaults\n" \ +" In the meantime, mount /proc /proc -t proc\n" + +#define STAT_FILE "/proc/stat" +static int stat_fd = -1; +#define UPTIME_FILE "/proc/uptime" +static int uptime_fd = -1; +#define LOADAVG_FILE "/proc/loadavg" +static int loadavg_fd = -1; +#define MEMINFO_FILE "/proc/meminfo" +static int meminfo_fd = -1; + +static char buf[1024]; + +/* This macro opens filename only if necessary and seeks to 0 so + * that successive calls to the functions are more efficient. + * It also reads the current contents of the file into the global buf. + */ +#define FILE_TO_BUF(filename, fd) do{ \ + static int local_n; \ + if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \ + fprintf(stderr, BAD_OPEN_MESSAGE); \ + fflush(NULL); \ + _exit(102); \ + } \ + lseek(fd, 0L, SEEK_SET); \ + if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ + perror(filename); \ + fflush(NULL); \ + _exit(103); \ + } \ + buf[local_n] = '\0'; \ +}while(0) + +/* evals 'x' twice */ +#define SET_IF_DESIRED(x,y) do{ if(x) *(x) = (y); }while(0) + + +/***********************************************************************/ +int uptime(double *uptime_secs, double *idle_secs) { + double up=0, idle=0; + + FILE_TO_BUF(UPTIME_FILE,uptime_fd); + if (sscanf(buf, "%lf %lf", &up, &idle) < 2) { + fprintf(stderr, "bad data in " UPTIME_FILE "\n"); + return 0; + } + SET_IF_DESIRED(uptime_secs, up); + SET_IF_DESIRED(idle_secs, idle); + return up; /* assume never be zero seconds in practice */ +} + +/*********************************************************************** + * Some values in /proc are expressed in units of 1/HZ seconds, where HZ + * is the kernel clock tick rate. One of these units is called a jiffy. + * The HZ value used in the kernel may vary according to hacker desire. + * According to Linus Torvalds, this is not true. He considers the values + * in /proc as being in architecture-dependant units that have no relation + * to the kernel clock tick rate. Examination of the kernel source code + * reveals that opinion as wishful thinking. + * + * In any case, we need the HZ constant as used in /proc. (the real HZ value + * may differ, but we don't care) There are several ways we could get HZ: + * + * 1. Include the kernel header file. If it changes, recompile this library. + * 2. Use the sysconf() function. When HZ changes, recompile the C library! + * 3. Ask the kernel. This is obviously correct... + * + * Linus Torvalds won't let us ask the kernel, because he thinks we should + * not know the HZ value. Oh well, we don't have to listen to him. + * Someone smuggled out the HZ value. :-) + * + * This code should work fine, even if Linus fixes the kernel to match his + * stated behavior. The code only fails in case of a partial conversion. + * + */ +unsigned long Hertz; +static void init_Hertz_value(void) __attribute__((constructor)); +static void init_Hertz_value(void){ + unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */ + double up_1, up_2, seconds; + unsigned long jiffies, h; + smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF); + if(smp_num_cpus==-1) smp_num_cpus=1; + do{ + FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_1); + /* uptime(&up_1, NULL); */ + FILE_TO_BUF(STAT_FILE,stat_fd); + sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j); + FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_2); + /* uptime(&up_2, NULL); */ + } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */ + jiffies = user_j + nice_j + sys_j + other_j; + seconds = (up_1 + up_2) / 2; + h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus ); + /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ + switch(h){ + case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */ + case 48 ... 52 : Hertz = 50; break; + case 58 ... 62 : Hertz = 60; break; + case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */ + case 95 ... 105 : Hertz = 100; break; /* normal Linux */ + case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */ + case 195 ... 204 : Hertz = 200; break; /* normal << 1 */ + case 253 ... 260 : Hertz = 256; break; + case 393 ... 408 : Hertz = 400; break; /* normal << 2 */ + case 790 ... 808 : Hertz = 800; break; /* normal << 3 */ + case 990 ... 1010 : Hertz = 1000; break; /* ARM */ + case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */ + case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */ + default: +#ifdef HZ + Hertz = (unsigned long)HZ; /* */ +#else + /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */ + Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL; +#endif + fprintf(stderr, "Unknown HZ value! (%ld) Assume %ld.\n", h, Hertz); + } +} + +/*********************************************************************** + * The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we + * recover jiffies by adding up the 4 numbers we are given. SMP kernels + * (as of pre-2.4 era) can report idle time going backwards, perhaps due + * to non-atomic reads and updates. There is no locking for these values. + */ +#ifndef NAN +#define NAN (-0.0) +#endif +#define JT unsigned long +void four_cpu_numbers(double *uret, double *nret, double *sret, double *iret){ + double tmp_u, tmp_n, tmp_s, tmp_i; + double scale; /* scale values to % */ + static JT old_u, old_n, old_s, old_i; + JT new_u, new_n, new_s, new_i; + JT ticks_past; /* avoid div-by-0 by not calling too often :-( */ + + FILE_TO_BUF(STAT_FILE,stat_fd); + sscanf(buf, "cpu %lu %lu %lu %lu", &new_u, &new_n, &new_s, &new_i); + ticks_past = (new_u+new_n+new_s+new_i)-(old_u+old_n+old_s+old_i); + if(ticks_past){ + scale = 100.0 / (double)ticks_past; + tmp_u = ( (double)new_u - (double)old_u ) * scale; + tmp_n = ( (double)new_n - (double)old_n ) * scale; + tmp_s = ( (double)new_s - (double)old_s ) * scale; + tmp_i = ( (double)new_i - (double)old_i ) * scale; + }else{ + tmp_u = NAN; + tmp_n = NAN; + tmp_s = NAN; + tmp_i = NAN; + } + SET_IF_DESIRED(uret, tmp_u); + SET_IF_DESIRED(nret, tmp_n); + SET_IF_DESIRED(sret, tmp_s); + SET_IF_DESIRED(iret, tmp_i); + old_u=new_u; + old_n=new_n; + old_s=new_s; + old_i=new_i; +} +#undef JT + +/***********************************************************************/ +void loadavg(double *av1, double *av5, double *av15) { + double avg_1=0, avg_5=0, avg_15=0; + + FILE_TO_BUF(LOADAVG_FILE,loadavg_fd); + if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) { + fprintf(stderr, "bad data in " LOADAVG_FILE "\n"); + exit(1); + } + SET_IF_DESIRED(av1, avg_1); + SET_IF_DESIRED(av5, avg_5); + SET_IF_DESIRED(av15, avg_15); +} + +/***********************************************************************/ +/* + * Copyright 1999 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +typedef struct mem_table_struct { + const char *name; /* memory type name */ + const unsigned *slot; /* slot in return struct */ +} mem_table_struct; + +static int compare_mem_table_structs(const void *a, const void *b){ + return strcmp(((mem_table_struct*)a)->name,((mem_table_struct*)b)->name); +} + +/* example data, following junk, with comments added: + * + * MemTotal: 61768 kB old + * MemFree: 1436 kB old + * MemShared: 0 kB old (now always zero; not calculated) + * Buffers: 1312 kB old + * Cached: 20932 kB old + * Active: 12464 kB new + * Inact_dirty: 7772 kB new + * Inact_clean: 2008 kB new + * Inact_target: 0 kB new + * HighTotal: 0 kB + * HighFree: 0 kB + * LowTotal: 61768 kB + * LowFree: 1436 kB + * SwapTotal: 122580 kB old + * SwapFree: 60352 kB old + */ + +/* obsolete */ +unsigned kb_main_shared; +/* old but still kicking -- the important stuff */ +unsigned kb_main_buffers; +unsigned kb_main_cached; +unsigned kb_main_free; +unsigned kb_main_total; +unsigned kb_swap_free; +unsigned kb_swap_total; +/* recently introduced */ +unsigned kb_high_free; +unsigned kb_high_total; +unsigned kb_low_free; +unsigned kb_low_total; +/* 2.4.xx era */ +unsigned kb_active; +unsigned kb_inact_dirty; +unsigned kb_inact_clean; +unsigned kb_inact_target; +/* derived values */ +unsigned kb_swap_used; +unsigned kb_main_used; + +void meminfo(void){ + char namebuf[16]; /* big enough to hold any row name */ + mem_table_struct findme = { namebuf, NULL}; + mem_table_struct *found; + char *head; + char *tail; + static const mem_table_struct mem_table[] = { + {"Active", &kb_active}, + {"Buffers", &kb_main_buffers}, + {"Cached", &kb_main_cached}, + {"HighFree", &kb_high_free}, + {"HighTotal", &kb_high_total}, + {"Inact_clean", &kb_inact_clean}, + {"Inact_dirty", &kb_inact_dirty}, + {"Inact_target", &kb_inact_target}, + {"LowFree", &kb_low_free}, + {"LowTotal", &kb_low_total}, + {"MemFree", &kb_main_free}, + {"MemShared", &kb_main_shared}, + {"MemTotal", &kb_main_total}, + {"SwapFree", &kb_swap_free}, + {"SwapTotal", &kb_swap_total} + }; + const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct); + + FILE_TO_BUF(MEMINFO_FILE,meminfo_fd); + + head = buf; + for(;;){ + tail = strchr(head, ':'); + if(!tail) break; + *tail = '\0'; + if(strlen(head) >= sizeof(namebuf)){ + head = tail+1; + goto nextline; + } + strcpy(namebuf,head); + found = bsearch(&findme, mem_table, mem_table_count, + sizeof(mem_table_struct), compare_mem_table_structs + ); + head = tail+1; + if(!found) goto nextline; + *(found->slot) = strtoul(head,&tail,10); +nextline: + tail = strchr(head, '\n'); + if(!tail) break; + head = tail+1; + } + if(!kb_low_total){ /* low==main except with large-memory support */ + kb_low_total = kb_main_total; + kb_low_free = kb_main_free; + } + kb_swap_used = kb_swap_total - kb_swap_free; + kb_main_used = kb_main_total - kb_main_free; +} diff --git a/proc/sysinfo.h b/proc/sysinfo.h new file mode 100644 index 00000000..50064da1 --- /dev/null +++ b/proc/sysinfo.h @@ -0,0 +1,40 @@ +#ifndef SYSINFO_H +#define SYSINFO_H + +extern unsigned long Hertz; /* clock tick frequency */ +extern long smp_num_cpus; /* number of CPUs */ + +#define JT double +extern void four_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret); +#undef JT + +extern int uptime (double *uptime_secs, double *idle_secs); +extern void loadavg(double *av1, double *av5, double *av15); + + +/* obsolete */ +extern unsigned kb_main_shared; +/* old but still kicking -- the important stuff */ +extern unsigned kb_main_buffers; +extern unsigned kb_main_cached; +extern unsigned kb_main_free; +extern unsigned kb_main_total; +extern unsigned kb_swap_free; +extern unsigned kb_swap_total; +/* recently introduced */ +extern unsigned kb_high_free; +extern unsigned kb_high_total; +extern unsigned kb_low_free; +extern unsigned kb_low_total; +/* 2.4.xx era */ +extern unsigned kb_active; +extern unsigned kb_inact_dirty; +extern unsigned kb_inact_clean; +extern unsigned kb_inact_target; +/* derived values */ +extern unsigned kb_swap_used; +extern unsigned kb_main_used; + +extern void meminfo(void); + +#endif /* SYSINFO_H */ diff --git a/proc/tree.h b/proc/tree.h new file mode 100644 index 00000000..f834fa28 --- /dev/null +++ b/proc/tree.h @@ -0,0 +1,15 @@ +/* for oldps.c and proc/compare.c */ +struct tree_node { + proc_t *proc; + pid_t pid; + pid_t ppid; + char *line; + char *cmd; + char **cmdline; + char **environ; + int children; + int maxchildren; + int *child; + int have_parent; +}; + diff --git a/proc/version.c b/proc/version.c new file mode 100644 index 00000000..aee0bc32 --- /dev/null +++ b/proc/version.c @@ -0,0 +1,43 @@ +/* Suite version information for procps utilities + * Copyright (c) 1995 Martin Schulze + * Ammended by cblake to only export the function symbol. + * Redistributable under the terms of the + * GNU Library General Public License; see COPYING + */ +#include +#include + +#ifdef MINORVERSION +char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION; +#else +char procps_version[] = "procps version " VERSION "." SUBVERSION; +#endif + +void display_version(void) { + fprintf(stdout, "%s\n", procps_version); +} + +/* Linux kernel version information for procps utilities + * Copyright (c) 1996 Charles Blake + */ +#include + +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) + +int linux_version_code = 0; + +static void init_Linux_version(void) __attribute__((constructor)); +static void init_Linux_version(void) { + static struct utsname uts; + int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */ + + if (linux_version_code) return; + if (uname(&uts) == -1) /* failure implies impending death */ + exit(1); + if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3) + fprintf(stderr, /* *very* unlikely to happen by accident */ + "Non-standard uts for running kernel:\n" + "release %s=%d.%d.%d gives version code %d\n", + uts.release, x, y, z, LINUX_VERSION(x,y,z)); + linux_version_code = LINUX_VERSION(x, y, z); +} diff --git a/proc/version.h b/proc/version.h new file mode 100644 index 00000000..7b58cdbc --- /dev/null +++ b/proc/version.h @@ -0,0 +1,23 @@ +#ifndef PROC_VERSION_H +#define PROC_VERSION_H + +/* Suite version information for procps utilities + * Copyright (c) 1995 Martin Schulze + * Linux kernel version information for procps utilities + * Copyright (c) 1996 Charles Blake + * Distributable under the terms of the GNU Library General Public License + */ + +extern void display_version(void); /* display suite version */ +extern char procps_version[]; /* global buf for suite version */ + +extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE + in /usr/include/linux/version.h */ + +/* Convenience macros for composing/decomposing version codes */ +#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z) +#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF) +#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF) +#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF) + +#endif /* PROC_VERSION_H */ diff --git a/proc/whattime.c b/proc/whattime.c new file mode 100644 index 00000000..61e26013 --- /dev/null +++ b/proc/whattime.c @@ -0,0 +1,89 @@ +/* This is a trivial uptime program. I hereby release this program + * into the public domain. I disclaim any responsibility for this + * program --- use it at your own risk. (as if there were any.. ;-) + * -michaelkjohnson (johnsonm@sunsite.unc.edu) + * + * Modified by Larry Greenfield to give a more traditional output, + * count users, etc. (greenfie@gauss.rutgers.edu) + * + * Modified by mkj again to fix a few tiny buglies. + * + * Modified by J. Cowley to add printing the uptime message to a + * string (for top) and to optimize file handling. 19 Mar 1993. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/whattime.h" +#include "proc/sysinfo.h" + +static char buf[128]; +static double av[3]; + +char *sprint_uptime(void) { + struct utmp *utmpstruct; + int upminutes, uphours, updays; + int pos; + struct tm *realtime; + time_t realseconds; + int numuser; + double uptime_secs, idle_secs; + +/* first get the current time */ + + time(&realseconds); + realtime = localtime(&realseconds); + pos = sprintf(buf, " %2d:%02d%s ", + realtime->tm_hour%12 ? realtime->tm_hour%12 : 12, + realtime->tm_min, realtime->tm_hour > 11 ? "pm" : "am"); + +/* read and calculate the amount of uptime */ + + uptime(&uptime_secs, &idle_secs); + + updays = (int) uptime_secs / (60*60*24); + strcat (buf, "up "); + pos += 3; + if (updays) + pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : ""); + upminutes = (int) uptime_secs / 60; + uphours = upminutes / 60; + uphours = uphours % 24; + upminutes = upminutes % 60; + if(uphours) + pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes); + else + pos += sprintf(buf + pos, "%d min, ", upminutes); + +/* count the number of users */ + + numuser = 0; + setutent(); + while ((utmpstruct = getutent())) { + if ((utmpstruct->ut_type == USER_PROCESS) && + (utmpstruct->ut_name[0] != '\0')) + numuser++; + } + endutent(); + + pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s"); + + loadavg(&av[0], &av[1], &av[2]); + + pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f", + av[0], av[1], av[2]); + + return buf; +} + +void print_uptime(void) +{ + printf("%s\n", sprint_uptime()); +} diff --git a/proc/whattime.h b/proc/whattime.h new file mode 100644 index 00000000..3d37bfa2 --- /dev/null +++ b/proc/whattime.h @@ -0,0 +1,9 @@ +/* whattime.h --- see whattime.c for explanation */ + +#ifndef __WHATTIME_H +#define __WHATTIME_H + +extern void print_uptime(void); +extern char *sprint_uptime(void); + +#endif diff --git a/procps.lsm b/procps.lsm new file mode 100644 index 00000000..4e76e738 --- /dev/null +++ b/procps.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: procps +Version: 010114 +Entered-date: 14JAN01 +Description: Procps is a library which parses the textual /proc filesystem + and a suite of utilites which use the library. +Keywords: procps /proc libproc + ps uptime tload free w top vmstat watch skill snice kill pgrep pkill +Author: Michael K. Johnson, Charles Blake, Albert Cahalan, many others. +Maintained-by: various +Primary-site: http://www.cs.uml.edu/~acahalan/procps/ + 185kB procps-010114.tar.gz +Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html + 185kB procps-010114.tar.gz +Copying-policy: mixed +End diff --git a/procps.spec b/procps.spec new file mode 100644 index 00000000..a3b212d3 --- /dev/null +++ b/procps.spec @@ -0,0 +1,105 @@ +Summary: Utilities for monitoring your system and processes on your system. +Name: procps +%define major_version 2 +%define minor_version 0 +%define revision 7 +%define version %{major_version}.%{minor_version}.%{revision} +Version: %{version} +Release: 1 +Copyright: GPL +Group: Applications/System +Source: ftp://people.redhat.com/johnsonm/procps/procps-%{version}.tar.gz +BuildRoot: /var/tmp/procps-root + +%package X11 +Group: Applications/System +Summary: X based process monitoring utilities. + +%description +The procps package contains a set of system utilities which provide +system information. Procps includes ps, free, sysctl, skill, snice, +tload, top, uptime, vmstat, w, and watch. The ps command displays +a snapshot of running processes. The top command provides a +repetitive update of the statuses of running processes. The free +command displays the amounts of free and used memory on your system. +Sysctl is a simple program for managing system configuration entries. +The skill command sends a terminate command (or another +specified signal) to a specified set of processes. The snice +command is used to change the scheduling priority of specified +processes. The tload command prints a graph of the current system +load average to a specified tty. The uptime command displays the +current time, how long the system has been running, how many users +are logged on and system load averages for the past one, five and +fifteen minutes. The w command displays a list of the users who +are currently logged on and what they're running. The watch program +watches a running program. The vmstat command displays virtual +memory statistics about processes, memory, paging, block I/O, traps +and CPU activity. + +%description X11 +The procps-X11 package contains the XConsole shell script, a backwards +compatibility wrapper for the xconsole program. + +%prep +%setup + +%build +PATH=/usr/X11R6/bin:$PATH + +make CC="gcc $RPM_OPT_FLAGS" LDFLAGS=-s + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/bin $RPM_BUILD_ROOT/usr/bin $RPM_BUILD_ROOT/sbin +mkdir -p $RPM_BUILD_ROOT/usr/man/man1 $RPM_BUILD_ROOT/usr/man/man8 +mkdir -p $RPM_BUILD_ROOT/lib $RPM_BUILD_ROOT/usr/X11R6/bin + +make DESTDIR=$RPM_BUILD_ROOT OWNERGROUP= MANDIR=/usr/share/man install + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +# add libproc to the cache +/sbin/ldconfig +# remove obsolete files +rm -f /etc/psdevtab /etc/psdatabase + +%files +%defattr(0644,root,root,755) +%attr(0644,root,root) %config(missingok) /etc/X11/applnk/Utilities/top.desktop +%doc NEWS BUGS TODO +%attr(755,root,root) /lib/libproc.so.2.0.7 +%attr(555,root,root) /bin/ps +%attr(555,root,root) /sbin/sysctl +%attr(555,root,root) /usr/bin/oldps +%attr(555,root,root) /usr/bin/uptime +%attr(555,root,root) /usr/bin/tload +%attr(555,root,root) /usr/bin/free +%attr(555,root,root) /usr/bin/w +%attr(555,root,root) /usr/bin/top +%attr(555,root,root) /usr/bin/vmstat +%attr(555,root,root) /usr/bin/watch +%attr(555,root,root) /usr/bin/skill +%attr(555,root,root) /usr/bin/snice +%attr(555,root,root) /usr/bin/pgrep +%attr(555,root,root) /usr/bin/pkill + +%attr(0644,root,root) /usr/man/man1/free.1 +%attr(0644,root,root) /usr/man/man1/ps.1 +%attr(0644,root,root) /usr/man/man1/oldps.1 +%attr(0644,root,root) /usr/man/man1/sessreg.1 +%attr(0644,root,root) /usr/man/man1/skill.1 +%attr(0644,root,root) /usr/man/man1/snice.1 +%attr(0644,root,root) /usr/man/man1/tload.1 +%attr(0644,root,root) /usr/man/man1/top.1 +%attr(0644,root,root) /usr/man/man1/uptime.1 +%attr(0644,root,root) /usr/man/man1/w.1 +%attr(0644,root,root) /usr/man/man1/watch.1 +%attr(0644,root,root) /usr/man/man1/pgrep.1 +%attr(0644,root,root) /usr/man/man1/pkill.1 +%attr(0644,root,root) /usr/man/man8/vmstat.8 +%attr(0644,root,root) /usr/man/man8/sysctl.8 + +%files X11 +%attr(0755,root,root) /usr/X11R6/bin/XConsole diff --git a/ps/.cvsignore.patch b/ps/.cvsignore.patch new file mode 100644 index 00000000..7c386a25 --- /dev/null +++ b/ps/.cvsignore.patch @@ -0,0 +1,5 @@ +diff -Naur procps-2.0.6/ps/.cvsignore procps-2.0.7/ps/.cvsignore +--- procps-2.0.6/ps/.cvsignore Wed Dec 31 19:00:00 1969 ++++ procps-2.0.7/ps/.cvsignore Fri Jul 14 16:45:01 2000 +@@ -0,0 +1 @@ ++ps diff --git a/ps/COPYING b/ps/COPYING new file mode 100644 index 00000000..92b8903f --- /dev/null +++ b/ps/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ps/HACKING b/ps/HACKING new file mode 100644 index 00000000..ffae92c5 --- /dev/null +++ b/ps/HACKING @@ -0,0 +1,53 @@ +Warning: + +This code must corrctly handle lots of picky little details to meet +the Unix98 standard while simultaneously being as compatible as +possible with the original Linux ps. Don't "fix" something without +considering the impact on all the special-case code. For example, +the "tty" format _must_ use "TT" as the header, even though the SysV +output formats _must_ use "TTY". + +File overview: + +display.c main(), debug code, iterates over processes +escape.c Does stuff like \202 and < to command and environment. +global.c Data + code to init it. +help.c Help message. +output.c Giant tables and lots of output functions. +parser.c Initial command parsing. +select.c want_this_proc() checks a process against flags & lists +sortformat.c Parses sort & format specifier lists. Picks output format. +stacktrace.c Debug code, not normally used. +../proc/* Library used to gather data. +regression Regression tests that ought to be run. +common.h Lots of interesting stuff. +Makefile Makefile +p Script used to test ps when the library is not installed. +utf Empty file used to test "ps ut?" unmangling behavior. +ps.1 Man page. + +Compiling: + +Whatever you do, don't trust the Makefiles. They are severely broken. +You can touch top.h and top won't be recompiled, or you can change +library files and discover that the library and programs won't be +recompiled. + +Operation: + +Unless the personality forces BSD parsing, parser.c tries to parse the +command line as a mixed BSD+SysV+Gnu mess. On failure, BSD parsing is +attempted. If BSD parsing fails _after_ SysV parsing has been attempted, +the error message comes from the original SysV parse. + +Control goes to sortformat.c, which must pick apart ambiguous options +like "O". Failure can reset the whole program and set PER_FORCE_BSD, +which means a second trip through parser.c and sortformat.c. + +The choice of output format happens in sortformat.c. There is a switch() +with all the valid format_flags combinations. The SysV and default +options are NULL (unless overridden by personality), which causes a +trip through SysV output format generation hackery. Note that the +default format always goes through there, even if it is for BSD. +Formats that came from the switch() (generally BSD, plus overrides) +get mangled a bit to support various SysV output modifiers. diff --git a/ps/Makefile b/ps/Makefile new file mode 100755 index 00000000..ec04f2f3 --- /dev/null +++ b/ps/Makefile @@ -0,0 +1,35 @@ +all: ps + +ps: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o + $(CC) -o ps escape.o global.o help.o select.o sortformat.o output.o parser.o display.o -L../proc -lproc + +# This just adds the stacktrace code +debug: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o + $(CC) -o ps escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o -L../proc -lproc -lefence + +sortformat.o: sortformat.c common.h + +global.o: global.c common.h + +escape.o: escape.c + +help.o: help.c + +select.o: select.c common.h + +output.o: output.c common.h + +parser.o: parser.c common.h + +display.o: display.c common.h + +stacktrace.o: stacktrace.c + + +install: ps + install $(OWNERGROUP) --mode a=rx --strip ps $(BINDIR)/ps + install $(OWNERGROUP) --mode a=r ps.1 $(MAN1DIR)/ps.1 + -rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz + +clean: + rm -f *.o DEADJOE *~ core ps gmon.out diff --git a/ps/TRANSLATION b/ps/TRANSLATION new file mode 100644 index 00000000..788aa6fe --- /dev/null +++ b/ps/TRANSLATION @@ -0,0 +1,28 @@ +Initially I only want to translate the --help output and man page. +Common error messages would be next on the list. I want to avoid +run-time overhead and bloat. + +Translations of the --help output should not be longer than 22 lines long. +Feel free to leave out the less useful options to save space. (not even +the English help text has all the options) + +I think these are the most important options: + +*** selection *** +-C by command name list +-G by real group ID list (supports names) +-U by real user ID list (supports names) +-u by effective user ID list (supports names) +-e all processes +-p by process ID list + +*** output *** +--no-heading No header line. +-o,o user-defined output +-j,j job control format +-l,l long format +-f full format +s signal format +u user-oriented format +--forest ASCII art forest (process heirarchy) +c show true command name diff --git a/ps/common.h b/ps/common.h new file mode 100644 index 00000000..d2bf76f5 --- /dev/null +++ b/ps/common.h @@ -0,0 +1,324 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#ifndef PROCPS_PS_H +#define PROCPS_PS_H + +#include "../proc/readproc.h" +#include /* looks safe for glibc, we need PAGE_SIZE */ + +#if 0 +#define trace(args...) printf(## args) +#else +#define trace(args...) +#endif + + +/***************** GENERAL DEFINE ********************/ + + +/* selection list */ +#define SEL_RUID 1 +#define SEL_EUID 2 +#define SEL_SUID 3 +#define SEL_FUID 4 +#define SEL_RGID 5 +#define SEL_EGID 6 +#define SEL_SGID 7 +#define SEL_FGID 8 +#define SEL_PGRP 9 +#define SEL_PID 10 +#define SEL_TTY 11 +#define SEL_SESS 12 +#define SEL_COMM 13 + +/* Since an enum could be smashed by a #define, it would be bad. */ +#define U98 0 /* Unix98 standard */ /* This must be 0 */ +#define XXX 1 /* Common extension */ +#define DEC 2 /* Digital Unix */ +#define AIX 3 /* AIX */ +#define SCO 4 /* SCO */ +#define LNX 5 /* Linux original :-) */ +#define BSD 6 /* FreeBSD and OpenBSD */ +#define SUN 7 /* SunOS 5 (Solaris) */ +#define HPU 8 /* HP-UX */ +#define SGI 9 /* Irix */ +#define SOE 10 /* IBM's S/390 OpenEdition */ + +/* + * Must not overflow the output buffer: + * 32 pages for env+cmd + * 8 kB pages on the Alpha + * 5 chars for "\001 " + * plus some slack for other stuff + * That is about 1.3 MB on the Alpha + * + * This isn't good enough for setuid. If anyone cares, mmap() over the + * last page with something unwriteable. + */ + +/* maximum escape expansion is 6, for " */ +#define ESC_STRETCH 6 +/* output buffer size */ +#define OUTBUF_SIZE (32*PAGE_SIZE*ESC_STRETCH + 8*PAGE_SIZE) +/* spaces used to right-justify things */ +#define SPACE_AMOUNT (int)(PAGE_SIZE) + +/******************* PS DEFINE *******************/ + +/* personality control flags */ +#define PER_BROKEN_o 0x0001 +#define PER_BSD_h 0x0002 +#define PER_BSD_m 0x0004 +#define PER_CUMUL_MARKED 0x0008 +#define PER_FORCE_BSD 0x0010 +#define PER_GOOD_o 0x0020 +#define PER_OLD_m 0x0040 +#define PER_NO_DEFAULT_g 0x0080 +#define PER_ZAP_ADDR 0x0100 +#define PER_SANE_USER 0x0200 +#define PER_IRIX_l 0x0400 + +/* Simple selections by bit mask */ +#define SS_B_x 0x01 +#define SS_B_g 0x02 +#define SS_U_d 0x04 +#define SS_U_a 0x08 +#define SS_B_a 0x10 + +/* predefined format flags such as: -l -f l u s -j */ +#define FF_Uf 0x0001 /* -f */ +#define FF_Uj 0x0002 /* -j */ +#define FF_Ul 0x0004 /* -l */ +#define FF_Bj 0x0008 /* j */ +#define FF_Bl 0x0010 /* l */ +#define FF_Bs 0x0020 /* s */ +#define FF_Bu 0x0040 /* u */ +#define FF_Bv 0x0080 /* v */ +#define FF_LX 0x0100 /* X */ +#define FF_Lm 0x0200 /* m */ /* overloaded: threads, sort, format */ + +/* predefined format modifier flags such as: -l -f l u s -j */ +#define FM_c 0x0001 /* -c */ +#define FM_j 0x0002 /* -j */ /* only set when !sysv_j_format */ +#define FM_y 0x0004 /* -y */ +#define FM_L 0x0008 /* -L */ +#define FM_P 0x0010 /* -P */ +#define FM_M 0x0020 /* -M */ +#define FM_T 0x0040 /* -T */ +#define FM_F 0x0080 /* -F */ /* -F also sets the regular -f flags */ + +/* sorting & formatting */ +/* U,B,G is Unix,BSD,Gnu and then there is the option itself */ +#define SF_U_O 1 +#define SF_U_o 2 +#define SF_B_O 3 +#define SF_B_o 4 +#define SF_B_m 5 /* overloaded: threads, sort, format */ +#define SF_G_sort 6 +#define SF_G_format 7 + +/* headers */ +#define HEAD_SINGLE 0 /* default, must be 0 */ +#define HEAD_NONE 1 +#define HEAD_MULTI 2 + + +/********************** GENERAL TYPEDEF *******************/ + +/* Other fields that might be useful: + * + * char *name; user-defined column name (format specification) + * int reverse; sorting in reverse (sort specification) + * + * name in place of u + * reverse in place of n + */ + +typedef union sel_union { + pid_t pid; + uid_t uid; + gid_t gid; + dev_t tty; + char cmd[8]; /* this is _not_ \0 terminated */ +} sel_union; + +typedef struct selection_node { + struct selection_node *next; + sel_union *u; /* used if selection type has a list of values */ + int n; /* used if selection type has a list of values */ + int typecode; +} selection_node; + +typedef struct sort_node { + struct sort_node *next; + int (*sr)(const proc_t* P, const proc_t* Q); /* sort function */ + int reverse; /* can sort backwards */ + int typecode; +} sort_node; + +typedef struct format_node { + struct format_node *next; + char *name; /* user can override default name */ + int (*pr)(void); /* print function */ +/* int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */ + int width; + int pad; + int vendor; /* Vendor that invented this */ + int flags; + int typecode; +} format_node; + +typedef struct format_struct { + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ + int (* const pr)(void); /* print function */ + int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */ + const int width; + const int pad; /* could be second width */ + const int vendor; /* Where does this come from? */ + const int flags; +} format_struct; + +/* though ps-specific, needed by general file */ +typedef struct macro_struct { + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ +} macro_struct; + +/**************** PS TYPEDEF ***********************/ + +typedef struct aix_struct { + const int desc; /* 1-character format code */ + const char *spec; /* format specifier */ + const char *head; /* default header in the POSIX locale */ +} aix_struct; + +typedef struct shortsort_struct { + const int desc; /* 1-character format code */ + const char *spec; /* format specifier */ +} shortsort_struct; + +/* Save these options for later: -o o -O O --format --sort */ +typedef struct sf_node { + struct sf_node *next; /* next arg */ + format_node *f_cooked; /* convert each arg alone, then merge */ + sort_node *s_cooked; /* convert each arg alone, then merge */ + char *sf; + int sf_code; +} sf_node; + + +/*********************** GENERAL GLOBALS *************************/ + +/* escape.c */ +extern int escape_strlist(char *dst, const char **src, size_t n); +extern int escape_str(char *dst, const char *src, size_t n); +extern int octal_escape_str(char *dst, const char *src, size_t n); +extern int simple_escape_str(char *dst, const char *src, size_t n); + +/********************* UNDECIDED GLOBALS **************/ + +/* + * fputs(3) should (as in "good behavior") return the number of + * characters written as it does on Digital Unix, AIX, Irix, and SunOS. + * I'll assume glibc 2.1 has this extremely useful feature. + * + * Note: code ported from other systems will keep breaking until + * the library is updated. You should patch the library itself if + * at all possible. (for example, distributers who build libc from + * source with automatic patching as part of the build process) + */ + +#if defined __GLIBC__ && ((__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) || __GLIBC__ > 2) +#warning Hopefully fputs(3) has been modernized... +#else +#define EMULATE_FPUTS +#define fputs something_to_avoid_libc_troubles +static inline int fputs(const char *s, FILE *fp){ + return fwrite(s,1,strlen(s),fp); + /* return fprintf(fp, "%s", s); */ +} +#endif + +/* output.c */ +extern void show_one_proc(proc_t* p); +extern void print_format_specifiers(void); +extern const aix_struct *search_aix_array(const int findme); +extern const shortsort_struct *search_shortsort_array(const int findme); +extern const format_struct *search_format_array(const char *findme); +extern const macro_struct *search_macro_array(const char *findme); +extern void init_output(void); + +/* global.c */ +extern void reset_global(void); + +/* global.c */ +extern int all_processes; +extern char *bsd_j_format; +extern char *bsd_l_format; +extern char *bsd_s_format; +extern char *bsd_u_format; +extern char *bsd_v_format; +extern int bsd_c_option; +extern int bsd_e_option; +extern uid_t cached_euid; +extern dev_t cached_tty; +extern char forest_prefix[4 * 32*1024 + 100]; +extern int forest_type; +extern unsigned format_flags; /* -l -f l u s -j... */ +extern format_node *format_list; /* digested formatting options */ +extern unsigned format_modifiers; /* -c -j -y -P -L... */ +extern int header_gap; +extern int header_type; /* none, single, multi... */ +extern int include_dead_children; +extern int lines_to_next_header; +extern int max_line_width; +extern const char *namelist_file; +extern int negate_selection; +extern unsigned personality; +extern int prefer_bsd_defaults; +extern int running_only; +extern int screen_cols; +extern int screen_rows; +extern unsigned long seconds_since_boot; +extern selection_node *selection_list; +extern unsigned simple_select; +extern sort_node *sort_list; +extern char *sysv_f_format; +extern char *sysv_fl_format; +extern char *sysv_j_format; +extern char *sysv_l_format; +extern int unix_f_option; +extern int user_is_number; +extern int wchan_is_number; + +/************************* PS GLOBALS *********************/ + +/* sortformat.c */ +extern int defer_sf_option(const char *arg, int source); +extern const char *process_sf_options(int localbroken); +extern void reset_sortformat(void); + +/* select.c */ +extern int want_this_proc(proc_t *buf); +extern const char *select_bits_setup(void); + +/* help.c */ +extern const char *help_message; + +/* global.c */ +extern void self_info(void); + +/* parser.c */ +extern int arg_parse(int argc, char *argv[]); + +#endif diff --git a/ps/display.c b/ps/display.c new file mode 100644 index 00000000..6824b41f --- /dev/null +++ b/ps/display.c @@ -0,0 +1,398 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include +#include + +/* username lookups */ +#include +#include +#include + +/* major/minor number */ +#include + +#include /* catch signals */ + +#include "common.h" +#include "../proc/procps.h" +#include "../proc/version.h" +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" + +#ifndef SIGCHLD +#define SIGCHLD SIGCLD +#endif + +/* just reports a crash */ +static void signal_handler(int signo){ + if(signo==SIGPIPE) _exit(0); /* "ps | head" will cause this */ + /* fprintf() is not reentrant, but we _exit() anyway */ + fprintf(stderr, + "\n\n" + "Signal %d caught by ps (%s).\n" + "Please send bug reports to \n", + signo, + procps_version + ); + _exit(signo+128); +} + + +#undef DEBUG +#ifdef DEBUG +void init_stack_trace(char *prog_name); + +#include + +void hex_dump(void *vp){ + char *charlist; + int i = 0; + int line = 45; + char *cp = (char *)vp; + + while(line--){ + printf("%8lx ", (unsigned long)cp); + charlist = cp; + cp += 16; + for(i=0; i<16; i++){ + if((charlist[i]>31) && (charlist[i]<127)){ + printf("%c", charlist[i]); + }else{ + printf("."); + } + } + printf(" "); + for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i]))); + printf("\n"); + i=0; + } +} + +static void show_pid(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d,", data[n].pid); + } + printf("%d\n", data[0].pid); +} + +static void show_uid(char *s, int n, sel_union *data){ + struct passwd *pw_data; + printf("%s ", s); + while(--n){ + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s,", pw_data->pw_name); + else printf("%d,", data[n].uid); + } + pw_data = getpwuid(data[n].uid); + if(pw_data) printf("%s\n", pw_data->pw_name); + else printf("%d\n", data[n].uid); +} + +static void show_gid(char *s, int n, sel_union *data){ + struct group *gr_data; + printf("%s ", s); + while(--n){ + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s,", gr_data->gr_name); + else printf("%d,", data[n].gid); + } + gr_data = getgrgid(data[n].gid); + if(gr_data) printf("%s\n", gr_data->gr_name); + else printf("%d\n", data[n].gid); +} + +static void show_tty(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty)); + } + printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty)); +} + +static void show_cmd(char *s, int n, sel_union *data){ + printf("%s ", s); + while(--n){ + printf("%.8s,", data[n].cmd); + } + printf("%.8s\n", data[0].cmd); +} + +static void arg_show(void){ + selection_node *walk = selection_list; + while(walk){ + switch(walk->typecode){ + case SEL_RUID: show_uid("RUID", walk->n, walk->u); break; + case SEL_EUID: show_uid("EUID", walk->n, walk->u); break; + case SEL_SUID: show_uid("SUID", walk->n, walk->u); break; + case SEL_FUID: show_uid("FUID", walk->n, walk->u); break; + case SEL_RGID: show_gid("RGID", walk->n, walk->u); break; + case SEL_EGID: show_gid("EGID", walk->n, walk->u); break; + case SEL_SGID: show_gid("SGID", walk->n, walk->u); break; + case SEL_FGID: show_gid("FGID", walk->n, walk->u); break; + case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break; + case SEL_PID : show_pid("PID ", walk->n, walk->u); break; + case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break; + case SEL_SESS: show_pid("SESS", walk->n, walk->u); break; + case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break; + default: printf("Garbage typecode value!\n"); + } + walk = walk->next; + } +} + +#endif + + +/***** check the header */ +/* Unix98: must not print empty header */ +static void check_headers(void){ + format_node *walk = format_list; + int head_normal = 0; + if(header_type==HEAD_MULTI){ + header_gap = screen_rows-1; /* true BSD */ + return; + } + if(header_type==HEAD_NONE){ + lines_to_next_header = -1; /* old Linux */ + return; + } + while(walk){ + if(!*(walk->name)){ + walk = walk->next; + continue; + } + if(walk->pr){ + head_normal++; + walk = walk->next; + continue; + } + walk = walk->next; + } + if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */ +} + +/***** fill in %CPU; not in libproc because of include_dead_children */ +static void fill_pcpu(proc_t *buf){ + unsigned long total_time; + unsigned long pcpu = 0; + unsigned long seconds; + + total_time = buf->utime + buf->stime; + if(include_dead_children) total_time += (buf->cutime + buf->cstime); + seconds = (seconds_since_boot - ((unsigned long)buf->start_time) / Hertz); + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + buf->pcpu = (pcpu > 999) ? 999 : pcpu; +} + +/***** just display */ +static void simple_spew(void){ + proc_t buf; + PROCTAB* ptp; + ptp = openproc(PROC_FILLBUG); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + memset(&buf, '#', sizeof(proc_t)); + /* use "ps_" prefix to catch library mismatch */ + while(ps_readproc(ptp,&buf)){ + fill_pcpu(&buf); + if(want_this_proc(&buf)) show_one_proc(&buf); + /* if(buf.cmdline) free(buf.cmdline); */ /* these crash */ + /* if(buf.environ) free(buf.environ); */ + memset(&buf, '#', sizeof(proc_t)); + } + closeproc(ptp); +} + +/***** forest output requires sorting by ppid; add start_time by default */ +static void prep_forest_sort(void){ + sort_node *tmp_list = sort_list; + const format_struct *incoming; + + if(!sort_list) { /* assume start time order */ + incoming = search_format_array("start_time"); + if(!incoming) fprintf(stderr, "Could not find start_time!\n"); + tmp_list = malloc(sizeof(sort_node)); + tmp_list->reverse = 0; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->next = sort_list; + sort_list = tmp_list; + } + /* this is required for the forest option */ + incoming = search_format_array("ppid"); + if(!incoming) fprintf(stderr, "Could not find ppid!\n"); + tmp_list = malloc(sizeof(sort_node)); + tmp_list->reverse = 0; + tmp_list->typecode = '?'; /* what was this for? */ + tmp_list->sr = incoming->sr; + tmp_list->next = sort_list; + sort_list = tmp_list; +} + +/* we rely on the POSIX requirement for zeroed memory */ +static proc_t *processes[32*1024]; + +/***** compare function for qsort */ +static int compare_two_procs(const void *a, const void *b){ + sort_node *tmp_list = sort_list; + while(tmp_list){ + int result; + result = (*tmp_list->sr)(*(const proc_t **)a, *(const proc_t **)b); + if(result) return (tmp_list->reverse) ? -result : result; + tmp_list = tmp_list->next; + } + return 0; /* no conclusion */ +} + +/***** show pre-sorted array of process pointers */ +static void show_proc_array(int n){ + proc_t **p = processes; + while(n--){ + show_one_proc(*p); + /* if(p->cmdline) free(p->cmdline); */ /* this crashes */ + /* if(p->environ) free(p->environ); */ /* this crashes */ + /* memset(*p, '%', sizeof(proc_t)); */ /* debug */ + free(*p); + p++; + } +} + +/***** show tree */ +/* this needs some optimization work */ +#define ADOPTED(x) 1 +static void show_tree(const int self, const int n, const int level, const int have_sibling){ + int i = 0; + if(level){ + /* add prefix of "+" or "L" */ + if(have_sibling) forest_prefix[level-1] = '+'; + else forest_prefix[level-1] = 'L'; + forest_prefix[level] = '\0'; + } + show_one_proc(processes[self]); /* first show self */ + /* if(p->cmdline) free(p->cmdline); */ /* this crashes */ + /* if(p->environ) free(p->environ); */ /* this crashes */ + /* memset(*p, '%', sizeof(proc_t)); */ /* debug */ + for(;;){ /* look for children */ + if(i >= n) return; /* no children */ + if(processes[i]->ppid == processes[self]->pid) break; + i++; + } + if(level){ + /* change our prefix to "|" or " " for the children */ + if(have_sibling) forest_prefix[level-1] = '|'; + else forest_prefix[level-1] = ' '; + forest_prefix[level] = '\0'; + } + for(;;){ + int self_pid; + int more_children = 1; + if(i >= n) break; /* over the edge */ + self_pid=processes[self]->pid; + if(i+1 >= n) + more_children = 0; + else + if(processes[i+1]->ppid != self_pid) more_children = 0; + if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u') + show_tree(i++, n, level, more_children); + else + show_tree(i++, n, level+1, more_children); + if(!more_children) break; + } + /* chop prefix that children added -- do we need this? */ + forest_prefix[level] = '\0'; +} + +/***** show forest */ +static void show_forest(const int n){ + int i = n; + int j; + while(i--){ /* cover whole array looking for trees */ + j = n; + while(j--){ /* search for parent: if none, i is a tree! */ + if(processes[j]->pid == processes[i]->ppid) goto not_root; + } + show_tree(i,n,0,0); +not_root: + } + /* don't free the array because it takes time and ps will exit anyway */ +} + +/***** sorted or forest */ +static void fancy_spew(void){ + proc_t *retbuf; + PROCTAB* ptp; + int n = 0; /* number of processes & index into array */ + ptp = openproc(PROC_FILLBUG); + if(!ptp) { + fprintf(stderr, "Error: can not access /proc.\n"); + exit(1); + } + while((retbuf = ps_readproc(ptp,NULL))){ + fill_pcpu(retbuf); + if(want_this_proc(retbuf)) processes[n++] = retbuf; + else free(retbuf); + } + closeproc(ptp); + if(!n) return; /* no processes */ + if(forest_type) prep_forest_sort(); + qsort(processes, n, sizeof(proc_t*), compare_two_procs); + if(forest_type) show_forest(n); + else show_proc_array(n); +} + + +/***** no comment */ +int main(int argc, char *argv[]){ +#ifdef DEBUG + init_stack_trace(argv[0]); +#else + do { + struct sigaction sa; + int i = 32; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigfillset(&sa.sa_mask); + while(i--) switch(i){ + default: + sigaction(i,&sa,NULL); + case 0: + case SIGINT: /* ^C */ + case SIGQUIT: /* ^\ */ + case SIGPROF: /* profiling */ + case SIGKILL: /* can not catch */ + case SIGSTOP: /* can not catch */ + case SIGWINCH: /* don't care if window size changes */ + } + } while (0); +#endif + + reset_global(); /* must be before parser */ + arg_parse(argc,argv); + +/* arg_show(); */ + trace("screen is %ux%u\n",screen_cols,screen_rows); +/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */ + trace("======= ps output follows =======\n"); + + init_output(); /* must be between parser and output */ + check_headers(); + if (open_psdb(namelist_file)) wchan_is_number = 1; + if(forest_type || sort_list) fancy_spew(); /* sort or forest */ + else simple_spew(); /* no sort, no forest */ + show_one_proc((proc_t *)-1); /* no output yet? */ + return 0; +} diff --git a/ps/escape.c b/ps/escape.c new file mode 100644 index 00000000..0c613cc3 --- /dev/null +++ b/ps/escape.c @@ -0,0 +1,106 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include + +/* sanitize a string, without the nice BSD library function: */ +/* strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH) */ +int octal_escape_str(char *dst, const char *src, size_t n){ + unsigned char c; + char d; + size_t i; + const char *codes = + "Z------abtnvfr-------------e----" + " *******************************" /* better: do not print any space */ + "****************************\\***" + "*******************************-" + "--------------------------------" + "********************************" + "********************************" + "********************************"; + for(i=0; i n) goto leave; + i += 4; + *(dst++) = '\\'; + *(dst++) = "01234567"[c>>6]; + *(dst++) = "01234567"[(c>>3)&07]; + *(dst++) = "01234567"[c&07]; + break; + default: + if(i+2 > n) goto leave; + i += 2; + *(dst++) = '\\'; + *(dst++) = d; + break; + } + } +leave: + *(dst++) = '\0'; + return i; +} + +/* sanitize a string via one-way mangle */ +int simple_escape_str(char *dst, const char *src, size_t n){ + unsigned char c; + size_t i; + const char *codes = + "Z-------------------------------" + "********************************" + "********************************" + "*******************************-" + "--------------------------------" + "********************************" + "********************************" + "********************************"; + for(i=0; i 1) && (*(src+1))) dst[i++] = ' '; + src++; + } + return i; +} diff --git a/ps/global.c b/ps/global.c new file mode 100644 index 00000000..640978f0 --- /dev/null +++ b/ps/global.c @@ -0,0 +1,422 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/*#undef __GLIBC_MINOR__ +#define __GLIBC_MINOR__ 1 */ +#include "common.h" +/*#undef __GLIBC_MINOR__ +#define __GLIBC_MINOR__ 0 */ + +#include +#include "../proc/version.h" +#include "../proc/sysinfo.h" + + +#ifndef __GNU_LIBRARY__ +#define __GNU_LIBRARY__ -1 +#endif +#ifndef __GLIBC__ +#define __GLIBC__ -1 +#endif +#ifndef __GLIBC_MINOR__ +#define __GLIBC_MINOR__ -1 +#endif + + +static char *saved_personality_text = "You found a bug!"; + +int all_processes = -1; +char *bsd_j_format = (char *)0xdeadbeef; +char *bsd_l_format = (char *)0xdeadbeef; +char *bsd_s_format = (char *)0xdeadbeef; +char *bsd_u_format = (char *)0xdeadbeef; +char *bsd_v_format = (char *)0xdeadbeef; +int bsd_c_option = -1; +int bsd_e_option = -1; +uid_t cached_euid = -1; +dev_t cached_tty = -1; +char forest_prefix[4 * 32*1024 + 100]; +int forest_type = -1; +unsigned format_flags = 0xffffffff; /* -l -f l u s -j... */ +format_node *format_list = (format_node *)0xdeadbeef; /* digested formatting options */ +unsigned format_modifiers = 0xffffffff; /* -c -j -y -P -L... */ +int header_gap = -1; +int header_type = -1; +int include_dead_children = -1; +int lines_to_next_header = -1; +const char *namelist_file = (const char *)0xdeadbeef; +int negate_selection = -1; +int running_only = -1; +unsigned personality = 0xffffffff; +int prefer_bsd_defaults = -1; +int screen_cols = -1; +int screen_rows = -1; +unsigned long seconds_since_boot = -1; +selection_node *selection_list = (selection_node *)0xdeadbeef; +unsigned simple_select = 0xffffffff; +sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */ +char *sysv_f_format = (char *)0xdeadbeef; +char *sysv_fl_format = (char *)0xdeadbeef; +char *sysv_j_format = (char *)0xdeadbeef; +char *sysv_l_format = (char *)0xdeadbeef; +int unix_f_option = -1; +int user_is_number = -1; +int wchan_is_number = -1; + + +static void reset_selection_list(void){ + selection_node *old; + selection_node *walk = selection_list; + if(selection_list == (selection_node *)0xdeadbeef){ + selection_list = NULL; + return; + } + while(walk){ + old = walk; + walk = old->next; + free(old->u); + free(old); + } + selection_list = NULL; +} + +/* The rules: + * 1. Defaults are implementation-specific. (ioctl,termcap,guess) + * 2. COLUMNS and LINES override the defaults. (standards compliance) + * 3. Command line options override everything else. + * 4. Actual output may be more if the above is too narrow. + */ +static void set_screen_size(void){ + struct winsize ws; + char *columns; /* Unix98 environment variable */ + char *lines; /* Unix98 environment variable */ + if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0){ + screen_cols = ws.ws_col; + screen_rows = ws.ws_row; + }else{ /* TODO: ought to do tgetnum("co") and tgetnum("li") now */ + screen_cols = 80; + screen_rows = 24; + } + if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE; + columns = getenv("COLUMNS"); + if(columns && *columns){ + long t; + char *endptr; + t = strtol(columns, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t; + } + lines = getenv("LINES"); + if(lines && *lines){ + long t; + char *endptr; + t = strtol(lines, &endptr, 0); + if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t; + } + if((screen_cols<9) || (screen_rows<2)) + fprintf(stderr,"Your %dx%d screen size is bogus. Expect trouble.\n", + screen_cols, screen_rows + ); +} + +/**************** personality control **************/ + +typedef struct personality_table_struct { + const char *name; /* personality name */ + const void *jump; /* See gcc extension info. :-) */ +} personality_table_struct; + +static int compare_personality_table_structs(const void *a, const void *b){ + return strcasecmp(((personality_table_struct*)a)->name,((personality_table_struct*)b)->name); +} + +static const char *set_personality(void){ + char *s; + size_t sl; + char buf[16]; + personality_table_struct findme = { buf, NULL}; + personality_table_struct *found; + static const personality_table_struct personality_table[] = { + {"390", &&case_390}, + {"aix", &&case_aix}, + {"bsd", &&case_bsd}, + {"compaq", &&case_compaq}, + {"debian", &&case_debian}, + {"default", &&case_default}, + {"digital", &&case_digital}, + {"gnu", &&case_gnu}, + {"hp", &&case_hp}, + {"hpux", &&case_hpux}, + {"irix", &&case_irix}, + {"linux", &&case_linux}, + {"old", &&case_old}, + {"os390", &&case_os390}, + {"posix", &&case_posix}, + {"s390", &&case_s390}, + {"sco", &&case_sco}, + {"sgi", &&case_sgi}, + {"solaris2", &&case_solaris2}, + {"sunos4", &&case_sunos4}, + {"sysv", &&case_sysv}, + {"tru64", &&case_tru64}, + {"unix", &&case_unix}, + {"unix95", &&case_unix95}, + {"unix98", &&case_unix98}, + {"unknown", &&case_unknown} + }; + const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct); + + personality = 0; + prefer_bsd_defaults = 0; + + bsd_j_format = "OL_j"; + bsd_l_format = "OL_l"; + bsd_s_format = "OL_s"; + bsd_u_format = "OL_u"; + bsd_v_format = "OL_v"; + + /* When these are NULL, the code does SysV output modifier logic */ + sysv_f_format = NULL; + sysv_fl_format = NULL; + sysv_j_format = NULL; + sysv_l_format = NULL; + + s = getenv("PS_PERSONALITY"); + if(!s || !*s) s = getenv("CMD_ENV"); + if(!s || !*s) s="unknown"; /* "Do The Right Thing[tm]" */ + if(getenv("I_WANT_A_BROKEN_PS")) s="old"; + sl = strlen(s); + if(sl > 15) return "Environment specified an unknown personality."; + strncpy(buf, s, sl); + buf[sl] = '\0'; + saved_personality_text = strdup(buf); + + found = bsearch(&findme, personality_table, personality_table_count, + sizeof(personality_table_struct), compare_personality_table_structs + ); + + if(!found) return "Environment specified an unknown personality."; + + goto *(found->jump); /* See gcc extension info. :-) */ + + case_bsd: + personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m; + prefer_bsd_defaults = 1; + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_old: + personality = PER_FORCE_BSD | PER_OLD_m; + prefer_bsd_defaults = 1; + return NULL; + + case_debian: /* Toss this? They don't seem to care much. */ + case_gnu: + personality = PER_GOOD_o | PER_CUMUL_MARKED | PER_OLD_m; + prefer_bsd_defaults = 1; + sysv_f_format = "RD_f"; + /* sysv_fl_format = "RD_fl"; */ /* Debian can't do this! */ + sysv_j_format = "RD_j"; + sysv_l_format = "RD_l"; + return NULL; + + case_linux: + personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER; + return NULL; + + case_default: /* use defaults for ps, ignoring other environment variables */ + return NULL; + + case_unknown: /* defaults, but also check inferior environment variables */ + if( + getenv("UNIX95") /* Irix */ + || getenv("POSIXLY_CORRECT") /* most gnu stuff */ + || (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */ + ) personality = PER_BROKEN_o; + return NULL; + + case_aix: + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_tru64: + case_compaq: + case_digital: + personality = PER_GOOD_o | PER_BSD_h; + prefer_bsd_defaults = 1; + sysv_f_format = "F5FMT"; + sysv_fl_format = "FL5FMT"; + sysv_j_format = "JFMT"; + sysv_l_format = "L5FMT"; + bsd_j_format = "JFMT"; + bsd_l_format = "LFMT"; + bsd_s_format = "SFMT"; + bsd_u_format = "UFMT"; + bsd_v_format = "VFMT"; + return NULL; + + case_sunos4: + personality = PER_NO_DEFAULT_g; + prefer_bsd_defaults = 1; + bsd_j_format = "FB_j"; + bsd_l_format = "FB_l"; + /* bsd_s_format not used */ + bsd_u_format = "FB_u"; + bsd_v_format = "FB_v"; + return NULL; + + case_irix: + case_sgi: + s = getenv("_XPG"); + if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o; + else personality = PER_IRIX_l; + return NULL; + + case_os390: /* IBM's OS/390 OpenEdition on the S/390 mainframe */ + case_s390: + case_390: + sysv_j_format = "J390"; /* don't know what -jl and -jf do */ + return NULL; + + case_hp: + case_hpux: + case_posix: + case_sco: + case_solaris2: + case_sysv: + case_unix95: + case_unix98: + case_unix: + personality = PER_BROKEN_o; + return NULL; +} + + +/************ Call this to reinitialize everything ***************/ +void reset_global(void){ + static proc_t p; + reset_selection_list(); + look_up_our_self(&p); + set_screen_size(); + set_personality(); + + all_processes = 0; + bsd_c_option = 0; + bsd_e_option = 0; + cached_euid = geteuid(); + cached_tty = p.tty; +/* forest_prefix must be all zero because of POSIX */ + forest_type = 0; + format_flags = 0; /* -l -f l u s -j... */ + format_list = NULL; /* digested formatting options */ + format_modifiers = 0; /* -c -j -y -P -L... */ + header_gap = -1; /* send lines_to_next_header to -infinity */ + header_type = HEAD_SINGLE; + include_dead_children = 0; + lines_to_next_header = 1; + namelist_file = NULL; + negate_selection = 0; + running_only = 0; + seconds_since_boot = uptime(0,0); + selection_list = NULL; + simple_select = 0; + sort_list = NULL; + unix_f_option = 0; + user_is_number = 0; + wchan_is_number = 0; +} + +/*********** spew variables ***********/ +void self_info(void){ +#ifndef EMULATE_FPUTS + int count; +#endif + fprintf(stderr, + "BSD j %s\n" + "BSD l %s\n" + "BSD s %s\n" + "BSD u %s\n" + "BSD v %s\n" + "SysV -f %s\n" + "SysV -fl %s\n" + "SysV -j %s\n" + "SysV -l %s\n" + "\n", + bsd_j_format ? bsd_j_format : "(none)", + bsd_l_format ? bsd_l_format : "(none)", + bsd_s_format ? bsd_s_format : "(none)", + bsd_u_format ? bsd_u_format : "(none)", + bsd_v_format ? bsd_v_format : "(none)", + sysv_f_format ? sysv_f_format : "(none)", + sysv_fl_format ? sysv_fl_format : "(none)", + sysv_j_format ? sysv_j_format : "(none)", + sysv_l_format ? sysv_l_format : "(none)" + ); + + display_version(); + fprintf(stderr, "Linux version %d.%d.%d\n", + LINUX_VERSION_MAJOR(linux_version_code), + LINUX_VERSION_MINOR(linux_version_code), + LINUX_VERSION_PATCH(linux_version_code) + ); + /* __libc_print_version(); */ /* how can we get the run-time version? */ + fprintf(stderr, "Compiled with: libc %d, internal version %d.%d\n\n", + __GNU_LIBRARY__, __GLIBC__, __GLIBC_MINOR__ + ); + +#ifdef EMULATE_FPUTS + fprintf(stderr, "libc assumed lame, using fprintf to emulate fputs.\n\n"); +#else + fprintf(stderr, "fputs(\""); + count = fputs("123456789", stderr); + fprintf(stderr, "\", stderr) gives %d, which is %s.\n", + count, count==9?"good":"BAD!\nAdjust ps/common.h or libc, then recompile" + ); + if(count!=9){ + fprintf(stderr, "(procps includes a libc patch called glibc.patch)\n"); + } + fprintf(stderr, "\n"); +#endif + + fprintf(stderr, + "header_gap=%d lines_to_next_header=%d\n" + "screen_cols=%d screen_rows=%d\n" + "\n", + header_gap, lines_to_next_header, + screen_cols, screen_rows + ); + +/* open_psdb(namelist_file); */ + fprintf(stderr, + "personality=0x%08x (from \"%s\")\n" + "EUID=%d TTY=%d,%d Hertz=%ld\n" +/* "namelist_file=\"%s\"\n" */ + , + personality, saved_personality_text, + cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz /* , + namelist_file?namelist_file:"" */ + ); +} diff --git a/ps/help.c b/ps/help.c new file mode 100644 index 00000000..8e6bc0d5 --- /dev/null +++ b/ps/help.c @@ -0,0 +1,49 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* + * The help message must not become longer, because it must fit + * on an 80x24 screen _with_ the error message and command prompt. + */ + + +const char *help_message = +"********* simple selection ********* ********* selection by list *********\n" +"-A all processes -C by command name\n" +"-N negate selection -G by real group ID (supports names)\n" +"-a all w/ tty except session leaders -U by real user ID (supports names)\n" +"-d all except session leaders -g by session leader OR by group name\n" +"-e all processes -p by process ID\n" +"T all processes on this terminal -s processes in the sessions given\n" +"a all w/ tty, including other users -t by tty\n" +"g all, even group leaders! -u by effective user ID (supports names)\n" +"r only running processes U processes for specified users\n" +"x processes w/o controlling ttys t by tty\n" +"*********** output format ********** *********** long options ***********\n" +"-o,o user-defined -f full --Group --User --pid --cols\n" +"-j,j job control s signal --group --user --sid --rows\n" +"-O,O preloaded -o v virtual memory --cumulative --format --deselect\n" +"-l,l long u user-oriented --sort --tty --forest --version\n" +" X registers --heading --no-heading\n" +" ********* misc options *********\n" +"-V,V show version L list format codes f ASCII art forest\n" +"-m,m show threads S children in sum -y change -l format\n" +"-n,N set namelist file c true command name n numeric WCHAN,UID\n" +"-w,w wide output e show environment -H process heirarchy\n" +; + + + +/* Missing: + * + * -c -L -P -M --info + * + */ diff --git a/ps/it b/ps/it new file mode 100644 index 00000000..07fd6dce --- /dev/null +++ b/ps/it @@ -0,0 +1,35 @@ +From ddainese@dsi.unive.it Sun Apr 18 14:12:27 1999 + +here is a first translation of the text: +--------------------------------------------------------------------- +const char *help_message = +"****** seleziona i processi ******* * seleziona una lista specificando: *\n" +"-A tutti -C il nome del comando\n" +"-N nega la selezione -G il real group ID (supporta i nomi)\n" +"-a con tty, tranne i session leader -U il real user ID (supporta i nomi)\n" +"-d tutti, tranne i session leader -g il session leader OPPURE il gruppo\n" +"-e tutti -p l'ID del processo\n" +"T su questo terminale -s la sessione\n" +"a con tty, di tutti gli utenti -t il tty\n" +"g tutti, anche i leader di gruppo -u l'effective user ID (supporta i nomi)\n" +"r in stato running U una lista di utenti\n" +"x senza tty t il tty\n" +"******** formato dell'output ****** ********** opzioni lunghe **********\n" +"-o,o definito dall'utente --Group --User --pid --cols\n" +"-j,j job s segnali --group --user --sid --rows\n" +"-O,O -o preimpostato v memoria virtuale --cumulative --format --deselect\n" +"-l,l lungo u utenti --sort --tty --forest --version\n" +"-f completo X registri --heading --no-heading\n" +" ******** opzioni varie *********\n" +"-V,V versione L codici di formato f foresta di ASCII\n" +"-m,m vista ad albero S figli in sum -y cambia il formato -l\n" +"-n,N namelist file c nome reale del comando n WCHAN,UID numerici\n" +"-w,w output ampio e mostra l'environment -H gerarchia dei processi\n" +; +--------------------------------------------------------------------- + +Unfortunately it isn't really understandable for a newbie, because +there is too little space for a good translation; to make it more +meaningful, I would need about an entire line for every option, thus +if you really want the help text stays under 22 lines, it must +contains only 22 options. What do you think about it? diff --git a/ps/output.c b/ps/output.c new file mode 100644 index 00000000..152ad641 --- /dev/null +++ b/ps/output.c @@ -0,0 +1,1608 @@ +/* + * Copyright 1999 by Albert Cahalan; all rights resered. + * + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* + * This file is really gross, and I know it. I looked into several + * alternate ways to deal with the mess, and they were all ugly. + * + * FreeBSD has a fancy hack using offsets into a struct -- that + * saves code but it is _really_ gross. See the PO macro below. + * + * We could have a second column width for wide output format. + * For example, Digital prints the real-time signals. + */ + + +/* + * Data table idea: + * + * table 1 maps aix to specifier + * table 2 maps shortsort to specifier + * table 3 maps macro to specifiers + * table 4 maps specifier to title,datatype,offset,vendor,helptext + * table 5 maps datatype to justification,width,widewidth,sorting,printing + * + * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty... + * It must be enough to determine printing and sorting. + * + * After the tables, increase width as needed to fit the header. + * + * Table 5 could go in a file with the output functions. + */ + + +/* proc_t offset macro */ +#define PO(q) ((unsigned long)(&(((proc_t*)0)->q))) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../proc/readproc.h" +#include "../proc/sysinfo.h" +#include "../proc/status.h" +#include "../proc/procps.h" +#include "../proc/devname.h" +#include "common.h" + + +/* TODO: + * Stop assuming system time is local time. + */ + +#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */ + +static char whitespace_and_outbuf[OUTBUF_SIZE + SPACE_AMOUNT + PAGE_SIZE*2]; +static char *outbuf = whitespace_and_outbuf+SPACE_AMOUNT; +static char *whitespace = whitespace_and_outbuf; +static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */ +static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */ + +/* Justification control for flags field. */ +#define JUST_MASK 0x0f + /* AIXHACK 0 */ +#define USER 1 /* left if text, right if numeric */ +#define LEFT 2 +#define RIGHT 3 +#define UNLIMITED 4 +#define WCHAN 5 /* left if text, right if numeric */ +#define SIGNAL 6 /* right in 9, or 16 if screen_cols>107 */ + +#define CUMUL 16 /* mark cumulative (Summed) headers with 'C' */ + +static int wide_signals; /* true if we have room */ + +static proc_t *pp; /* the process being printed */ + +static unsigned long seconds_since_1970; +static unsigned long time_of_boot; +static unsigned long page_shift; + + +/*************************************************************************/ +/************ Lots of sort functions, starting with the NOP **************/ + +static int sr_nop(const proc_t* a, const proc_t* b){ + (void)a;(void)b; /* shut up gcc */ + return 0; +} + +#define CMP_STR(NAME) \ +static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \ + return strcmp(P->NAME, Q->NAME); \ +} + +#define CMP_INT(NAME) \ +static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \ + if (P->NAME < Q->NAME) return -1; \ + if (P->NAME > Q->NAME) return 1; \ + return 0; \ +} + +CMP_INT(cutime) +CMP_INT(cstime) +CMP_INT(priority) /* nice */ +CMP_INT(timeout) +CMP_INT(nice) /* priority */ +CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */ +CMP_INT(it_real_value) +CMP_INT(size) /* total pages */ /* vm_size, vsize */ +CMP_INT(resident) /* resident pages */ /* vm_rss, rss */ +CMP_INT(share) /* shared pages */ +CMP_INT(trs) /* executable pages */ +CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */ +CMP_INT(drs) /* other pages (assumed data?) */ +CMP_INT(dt) /* dirty pages */ + +CMP_INT(vm_size) /* kB VM */ /* size, vsize */ +CMP_INT(vm_lock) /* kB locked */ +CMP_INT(vm_rss) /* kB rss */ /* rss, resident */ +CMP_INT(vm_data) /* kB "data" == data-stack */ +CMP_INT(vm_stack) /* kB stack */ +CMP_INT(vm_exe) /* kB "exec" == exec-lib */ +CMP_INT(vm_lib) /* kB "libraries" */ +CMP_INT(vsize) /* pages VM */ /* size, vm_size */ +CMP_INT(rss_rlim) +CMP_INT(flags) +CMP_INT(min_flt) +CMP_INT(maj_flt) +CMP_INT(cmin_flt) +CMP_INT(cmaj_flt) +CMP_INT(nswap) +CMP_INT(cnswap) +CMP_INT(utime) +CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */ +CMP_INT(start_code) +CMP_INT(end_code) +CMP_INT(start_stack) +CMP_INT(kstk_esp) +CMP_INT(kstk_eip) +CMP_INT(start_time) +CMP_INT(wchan) + +/* CMP_STR(*environ) */ +/* CMP_STR(*cmdline) */ + +CMP_STR(ruser) +CMP_STR(euser) +CMP_STR(suser) +CMP_STR(fuser) +CMP_STR(rgroup) +CMP_STR(egroup) +CMP_STR(sgroup) +CMP_STR(fgroup) +CMP_STR(cmd) +/* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */ + +CMP_INT(ruid) +CMP_INT(rgid) +CMP_INT(euid) +CMP_INT(egid) +CMP_INT(suid) +CMP_INT(sgid) +CMP_INT(fuid) +CMP_INT(fgid) +CMP_INT(pid) +CMP_INT(ppid) +CMP_INT(pgrp) +CMP_INT(session) +CMP_INT(tty) +CMP_INT(tpgid) + +CMP_INT(pcpu) + +CMP_INT(state) + +/***************************************************************************/ +/************ Lots of format functions, starting with the NOP **************/ + +static int pr_nop(void){ + return snprintf(outbuf, COLWID, "%c", '-'); +} + + +/********* Unix 98 ************/ + +/*** + +Only comm and args are allowed to contain blank characters; all others are +not. Any implementation-dependent variables will be specified in the system +documentation along with the default header and indicating if the field +may contain blank characters. + +Some headers do not have a standardized specifier! + +%CPU pcpu The % of cpu time used recently, with unspecified "recently". +ADDR The address of the process. +C Processor utilisation for scheduling. +CMD The command name, or everything with -f. +COMMAND args Command + args. May chop as desired. May use either version. +COMMAND comm argv[0] +ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss +F Flags (octal and additive) +GROUP group Effective group ID, prefer text over decimal. +NI nice Decimal system scheduling priority, see nice(1). +PGID pgid The decimal value of the process group ID. +PID pid Decimal PID. +PPID ppid Decimal PID. +PRI Priority. Higher numbers mean lower priority. +RGROUP rgroup Real group ID, prefer text over decimal. +RUSER ruser Real user ID, prefer text over decimal. +S The state of the process. +STIME Starting time of the process. +SZ The size in blocks of the core image of the process. +TIME time Cumulative CPU time. [dd-]hh:mm:ss +TT tty Name of tty in format used by who(1). +TTY The controlling terminal for the process. +UID UID, or name when -f +USER user Effective user ID, prefer text over decimal. +VSZ vsz Virtual memory size in decimal kB. +WCHAN Where waiting/sleeping or blank if running. + +The nice value is used to compute the priority. + +For some undefined ones, Digital does: + +F flag Process flags -- but in hex! +PRI pri Process priority +S state Symbolic process status +TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??" +UID uid Process user ID (effective UID) +WCHAN wchan Address of event on which a + +For some undefined ones, Sun does: + +ADDR addr memory address of the process +C c Processor utilization for scheduling (obsolete). +CMD +F f +S s state: OSRZT +STIME start time, printed w/o blanks. If 24h old, months & days +SZ size (in pages) of the swappable process's image in main memory +TTY +UID uid +WCHAN wchan + +For some undefined ones, SCO does: +ADDR addr Virtual address of the process' entry in the process table. +SZ swappable size in kB of the virtual data and stack +STIME stime hms or md time format +***/ + +/* Source & destination are known. Return bytes or screen characters? */ +static int forest_helper(void){ + char *p = forest_prefix; + char *q = outbuf; + if(!*p) return 0; + /* Arrrgh! somebody defined unix as 1 */ + if(forest_type == 'u') goto unixy; + while(*p){ + switch(*p){ + case ' ': strcpy(q, " "); break; + case 'L': strcpy(q, " \\_ "); break; + case '+': strcpy(q, " \\_ "); break; + case '|': strcpy(q, " | "); break; + case '\0': return q-outbuf; /* redundant & not used */ + } + q += 4; + p++; + } + return q-outbuf; /* gcc likes this here */ +unixy: + while(*p){ + switch(*p){ + case ' ': strcpy(q, " "); break; + case 'L': strcpy(q, " "); break; + case '+': strcpy(q, " "); break; + case '|': strcpy(q, " "); break; + case '\0': return q-outbuf; /* redundant & not used */ + } + q += 2; + p++; + } + return q-outbuf; /* gcc likes this here */ +} + + +/* XPG4-UNIX, according to Digital: +The "args" and "command" specifiers show what was passed to the command. +Modifications to the arguments are not shown. +*/ + +/* + * pp->cmd short accounting name (comm & ucomm) + * pp->cmdline long name with args (args & command) + * pp->environ environment + */ + +/* "command" is the same thing: long unless c */ +static int pr_args(void){ + char *endp; + endp = outbuf + forest_helper(); + if(bsd_c_option){ + endp += escape_str(endp, pp->cmd, PAGE_SIZE); /* short version */ + }else{ + const char **lc = (const char**)pp->cmdline; /* long version */ + if(lc && *lc) { + endp += escape_strlist(endp, lc, OUTBUF_SIZE); + } else { + char buf[ESC_STRETCH*PAGE_SIZE]; /* TODO: avoid copy */ + escape_str(buf, pp->cmd, ESC_STRETCH*PAGE_SIZE); + endp += snprintf(endp, COLWID, "[%s]", buf); + } + } + if(bsd_e_option){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + endp += escape_strlist(endp, env, OUTBUF_SIZE); + } + } + return endp - outbuf; +} + +/* "ucomm" is the same thing: short unless -f */ +static int pr_comm(void){ + char *endp; + endp = outbuf + forest_helper(); + if(!unix_f_option){ /* does -f matter? */ + endp += escape_str(endp, pp->cmd, PAGE_SIZE); /* short version */ + }else{ + const char **lc = (const char**)pp->cmdline; /* long version */ + if(lc && *lc) { + endp += escape_strlist(endp, lc, OUTBUF_SIZE); + } else { + char buf[ESC_STRETCH*PAGE_SIZE]; /* TODO: avoid copy */ + escape_str(buf, pp->cmd, ESC_STRETCH*PAGE_SIZE); + endp += snprintf(endp, COLWID, "[%s]", buf); + } + } + if(bsd_e_option){ + const char **env = (const char**)pp->environ; + if(env && *env){ + *endp++ = ' '; + endp += escape_strlist(endp, env, OUTBUF_SIZE); + } + } + return endp - outbuf; +} +/* Non-standard, from SunOS 5 */ +static int pr_fname(void){ + char *endp; + endp = outbuf + forest_helper(); + endp += escape_str(endp, pp->cmd, 8); + return endp - outbuf; +} + +/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */ +static int pr_etime(void){ + unsigned t, dd,hh,mm,ss; + char *cp = outbuf; + t = ( + ((unsigned long)seconds_since_boot) + - ((unsigned long)pp->start_time) + / Hertz + ); + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + dd = t; + cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 ); + cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 ); + cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ; + return (int)(cp-outbuf); +} +static int pr_nice(void){ + return snprintf(outbuf, COLWID, "%ld", pp->nice); +} + +/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */ +static int pr_c(void){ + unsigned long total_time; /* jiffies used by this process */ + unsigned long pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = + seconds_since_boot - ((unsigned long)pp->start_time) / Hertz + ; + /* Use 100ULL (not 100) to avoid 32-bit overflow. */ + if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds; + if (pcpu > 99) pcpu = 99; + return snprintf(outbuf, COLWID, "%2u", (unsigned)pcpu); +} +/* normal %CPU in ##.# format. */ +static int pr_pcpu(void){ + unsigned long total_time; /* jiffies used by this process */ + unsigned long pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = + seconds_since_boot - ((unsigned long)pp->start_time) / Hertz + ; + /* Use 1000ULL (not 1000) to avoid 32-bit overflow. */ + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999) pcpu = 999; + return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pcpu/10), (unsigned)(pcpu%10)); +} +/* this is a "per-mill" format, like %cpu with no decimal point */ +static int pr_cp(void){ + unsigned long total_time; /* jiffies used by this process */ + unsigned long pcpu = 0; /* scaled %cpu, 999 means 99.9% */ + unsigned long seconds; /* seconds of process life */ + total_time = pp->utime + pp->stime; + if(include_dead_children) total_time += (pp->cutime + pp->cstime); + seconds = + seconds_since_boot - ((unsigned long)pp->start_time) / Hertz + ; + /* Use 1000ULL (not 1000) to avoid 32-bit overflow. */ + if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds; + if (pcpu > 999) pcpu = 999; + return snprintf(outbuf, COLWID, "%3u", (unsigned)pcpu); +} + +static int pr_pgid(void){ + return snprintf(outbuf, COLWID, "%u", pp->pgrp); +} +static int pr_pid(void){ + return snprintf(outbuf, COLWID, "%u", pp->pid); +} +static int pr_ppid(void){ + return snprintf(outbuf, COLWID, "%u", pp->ppid); +} + + +/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */ +static int pr_time(void){ + unsigned t, dd,hh,mm,ss; + int c; + t = (unsigned)( + ( + (unsigned long)(pp->utime) + + + (unsigned long)(pp->stime) + ) + / + (unsigned long)Hertz + ); + ss = t%60; + t /= 60; + mm = t%60; + t /= 60; + hh = t%24; + t /= 24; + dd = t; + c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 ); + c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) ); + return c; +} + +/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages. + * Unix98 requires "vsz" to be kB. + * Tru64 does both vsize and vsz like "1.23M" + * + * Our pp->vm_size is kB and our pp->vsize is pages. + * + * TODO: add flag for "1.23M" behavior, on this and other columns. + */ +static int pr_vsz(void){ + return snprintf(outbuf, COLWID, "%lu", pp->vm_size); +} + +/* + * internal terms: ruid euid suid fuid + * kernel vars: uid euid suid fsuid + * command args: ruid uid svuid n/a + */ + +static int pr_ruser(void){ + if(user_is_number || (strlen(pp->ruser)>max_rightward)) + return snprintf(outbuf, COLWID, "%d", pp->ruid); + return snprintf(outbuf, COLWID, "%s", pp->ruser); +} +static int pr_egroup(void){ + if(strlen(pp->egroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->egid); + return snprintf(outbuf, COLWID, "%s", pp->egroup); +} +static int pr_rgroup(void){ + if(strlen(pp->rgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->rgid); + return snprintf(outbuf, COLWID, "%s", pp->rgroup); +} +static int pr_euser(void){ + if(user_is_number || (strlen(pp->euser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->euid); + return snprintf(outbuf, COLWID, "%s", pp->euser); +} + +/********* maybe standard (Unix98 only defines the header) **********/ + + +/* + * "PRI" is created by "opri", or by "pri" when -c is used. + * + * Unix98 only specifies that a high "PRI" is low priority. + * Sun and SCO add the -c behavior. Sun defines "pri" and "opri". + * Linux may use "priority" for historical purposes. + */ +static int pr_priority(void){ /* -20..20 */ + return snprintf(outbuf, COLWID, "%ld", pp->priority); +} +static int pr_pri(void){ /* 20..60 */ + return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority); +} +static int pr_opri(void){ /* 39..79 */ + return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority); +} + +static int pr_wchan(void){ +/* + * Unix98 says "blank if running" and also "no blanks"! :-( + * Unix98 also says to use '-' if something is meaningless. + * Digital uses both '*' and '-', with undocumented differences. + * (the '*' for -1 (rare) and the '-' for 0) + * Sun claims to use a blank AND use '-', in the same man page. + * Perhaps "blank" should mean '-'. + * + * AIX uses '-' for running processes, the location when there is + * only one thread waiting in the kernel, and '*' when there is + * more than one thread waiting in the kernel. + */ + if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "%s", "-"); + if(wchan_is_number) return snprintf(outbuf, COLWID, "%lx", pp->wchan & 0xffffff); + return snprintf(outbuf, COLWID, "%s", wchan(pp->wchan)); +} + +/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */ +/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */ +static int pr_tty4(void){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, 4, pp->tty, pp->pid, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS); +} + +/* Unix98: format is unspecified, but must match that used by who(1). */ +static int pr_tty8(void){ +/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */ + return dev_to_tty(outbuf, PAGE_SIZE-1, pp->tty, pp->pid, ABBREV_DEV); +} + +#if 0 +/* This BSD state display may contain spaces, which is illegal. */ +static int pr_oldstate(void){ + return snprintf(outbuf, COLWID, "%s", status(pp)); +} +#endif + +/* This state display is Unix98 compliant and has lots of info like BSD. */ +static int pr_stat(void){ + int end = 0; + outbuf[end++] = pp->state; + if(pp->rss == 0 && pp->state != 'Z') outbuf[end++] = 'W'; + if(pp->nice < 0) outbuf[end++] = '<'; + if(pp->nice > 0) outbuf[end++] = 'N'; + if(pp->vm_lock) outbuf[end++] = 'L'; + outbuf[end] = '\0'; + return end; +} + +/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */ +static int pr_s(void){ + outbuf[0] = pp->state; + outbuf[1] = '\0'; + return 1; +} + +static int pr_flag(void){ + /* Unix98 requires octal -- good thing Linux hex looks octal! */ + return snprintf(outbuf, COLWID, "%03lx", (pp->flags)&0x777); +} + +static int pr_euid(void){ + return snprintf(outbuf, COLWID, "%d", pp->euid); +} + +/*********** non-standard ***********/ + +/*** BSD +sess session pointer +(SCO has:Process session leader ID as a decimal value. (SESSION)) +jobc job control count +cpu short-term cpu usage factor (for scheduling) +sl sleep time (in seconds; 127 = infinity) +re core residency time (in seconds; 127 = infinity) +pagein pageins (same as majflt) +lim soft memory limit +tsiz text size (in Kbytes) +***/ + +static int pr_stackp(void){ + return snprintf(outbuf, COLWID, "%08lx", pp->start_stack); +} + +static int pr_esp(void){ + return snprintf(outbuf, COLWID, "%08lx", pp->kstk_esp); +} + +static int pr_eip(void){ + return snprintf(outbuf, COLWID, "%08lx", pp->kstk_eip); +} + +/* This function helps print old-style time formats */ +static int old_time_helper(char *dst, unsigned long t, unsigned long rel) { + if(!t) return snprintf(dst, COLWID, " -"); + if((long)t == -1) return snprintf(dst, COLWID, " xx"); + if((long)(t-=rel) < 0) t=0; + if(t>9999) return snprintf(dst, COLWID, "%5lu", t/100); + else return snprintf(dst, COLWID, "%2lu.%02lu", t/100, t%100); +} + +static int pr_bsdtime(void){ + unsigned long t; + t = pp->utime + pp->stime; + if(include_dead_children) t += (pp->cutime + pp->cstime); + t /= Hertz; + return snprintf(outbuf, COLWID, "%3ld:%02d", t/60, (int)(t%60)); +} + +static int pr_bsdstart(void){ + time_t start; + time_t seconds_ago; + start = time_of_boot + pp->start_time/Hertz; + seconds_ago = seconds_since_1970 - start; + if(seconds_ago < 0) seconds_ago=0; + if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4); + else strcpy(outbuf, ctime(&start)+10); + outbuf[6] = '\0'; + return 6; +} + +static int pr_timeout(void){ + return old_time_helper(outbuf, pp->timeout, seconds_since_boot*Hertz); +} + +static int pr_alarm(void){ + return old_time_helper(outbuf, pp->it_real_value, 0); +} + +/* HP-UX puts this in pages and uses "vsz" for kB */ +static int pr_sz(void){ + return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(PAGE_SIZE/1024)); +} + + +/* + * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss + * I suspect some/all of those are broken. They seem to have been + * inherited by Linux and AIX from early BSD systems. FreeBSD only + * retains tsiz. The prefixed versions come from Debian. + * Sun and Digital have none of this crap. The code here comes + * from an old Linux ps, and might not be correct for ELF executables. + * + * AIX TRS size of resident-set (real memory) of text + * AIX TSIZ size of text (shared-program) image + * FreeBSD tsiz text size (in Kbytes) + * 4.3BSD NET/2 trss text resident set size (in Kbytes) + * 4.3BSD NET/2 tsiz text size (in Kbytes) + */ + +/* kB data size. See drs, tsiz & trs. */ +static int pr_dsiz(void){ + long dsiz = 0; + if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", dsiz); +} + +/* kB text (code) size. See trs, dsiz & drs. */ +static int pr_tsiz(void){ + long tsiz = 0; + if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", tsiz); +} + +/* kB _resident_ data size. See dsiz, tsiz & trs. */ +static int pr_drs(void){ + long drs = 0; + if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", drs); +} + +/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */ +static int pr_trs(void){ + long trs = 0; + if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10; + return snprintf(outbuf, COLWID, "%ld", trs); +} + + + +static int pr_minflt(void){ + long flt = pp->min_flt; + if(include_dead_children) flt += pp->cmin_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_majflt(void){ + long flt = pp->maj_flt; + if(include_dead_children) flt += pp->cmaj_flt; + return snprintf(outbuf, COLWID, "%ld", flt); +} + +static int pr_lim(void){ + if(pp->rss_rlim == RLIM_INFINITY) return snprintf(outbuf, COLWID, "%s", "xx"); + return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10); +} + +/* should print leading tilde ('~') if process is bound to the CPU */ +static int pr_psr(void){ + return snprintf(outbuf, COLWID, "%d", pp->processor); +} + +static int pr_wname(void){ +/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too. + * + * We use '-' for running processes, the location when there is + * only one thread waiting in the kernel, and '*' when there is + * more than one thread waiting in the kernel. + */ + if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "%s", "-"); + return snprintf(outbuf, COLWID, "%s", wchan(pp->wchan)); +} + +static int pr_nwchan(void){ + if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "-"); + return snprintf(outbuf, COLWID, "%lx", pp->wchan & 0xffffff); +} + +static int pr_rss(void){ + return snprintf(outbuf, COLWID, "%lu", pp->vm_rss); +} + +/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */ +static int pr_pmem(void){ + unsigned long pmem = 0; + pmem = pp->vm_rss * 1000ULL / kb_main_total; + if (pmem > 999) pmem = 999; + return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10)); +} + +static int pr_lstart(void){ + time_t t; + t = ( + ((unsigned long)time_of_boot) + + ((unsigned long)pp->start_time) + / Hertz + ); + return snprintf(outbuf, COLWID, "%24.24s", ctime(&t)); +} + +/* Unix98 specifies a STIME header for a column that shows the start + * time of the process, but does not specify a format or format specifier. + * From the general Unix98 rules, we know there must not be any spaces. + * Most systems violate that rule, though the Solaris documentation + * claims to print the column without spaces. (NOT!) + * + * So this isn't broken, but could be renamed to u98_std_stime, + * as long as it still shows as STIME when using the -f option. + */ +static int pr_stime(void){ + struct tm *proc_time; + struct tm *our_time; + time_t t; + char *fmt; + int tm_year; + int tm_yday; + our_time = localtime(&seconds_since_1970); /* not reentrant */ + tm_year = our_time->tm_year; + tm_yday = our_time->tm_yday; + t = (time_t)( + ((unsigned long)time_of_boot) + + ((unsigned long)pp->start_time) + / Hertz + ); + proc_time = localtime(&t); /* not reentrant, this corrupts our_time */ + fmt = "%H:%M"; /* 03:02 23:59 */ + if(tm_yday != proc_time->tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */ + if(tm_year != proc_time->tm_year) fmt = "%Y"; /* 1991 2001 */ + return strftime(outbuf, 42, fmt, proc_time); +} + +static int pr_start(void){ + time_t t; + char *str; + t = ( + ((unsigned long)time_of_boot) + + ((unsigned long)pp->start_time) + / Hertz + ); + str = ctime(&t); + if(str[8]==' ') str[8]='0'; + if(str[11]==' ') str[11]='0'; + if((unsigned long)t+60*60*24 > seconds_since_1970) + return snprintf(outbuf, COLWID, "%8.8s", str+11); + return snprintf(outbuf, COLWID, " %6.6s", str+4); +} + + +#ifdef SIGNAL_STRING +static int help_pr_sig(const char *sig){ + long len = 0; + len = strlen(sig); + if(wide_signals){ + if(len>8) return snprintf(outbuf, COLWID, "%s", sig); + return snprintf(outbuf, COLWID, "00000000%s", sig); + } + if(len-strspn(sig,"0") > 8) + return snprintf(outbuf, COLWID, "<%s", sig+len-8); + return snprintf(outbuf, COLWID, "%s", sig+len-8); +} +#else +static int help_pr_sig(unsigned long long sig){ + if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig); + if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL); + return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL); +} +#endif + +static int pr_sig(void){ + return help_pr_sig(pp->signal); +} +static int pr_sigmask(void){ + return help_pr_sig(pp->blocked); +} +static int pr_sigignore(void){ + return help_pr_sig(pp->sigignore); +} +static int pr_sigcatch(void){ + return help_pr_sig(pp->sigcatch); +} + + +static int pr_egid(void){ + return snprintf(outbuf, COLWID, "%d", pp->egid); +} +static int pr_rgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->rgid); +} +static int pr_sgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->sgid); +} +static int pr_fgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->fgid); +} +static int pr_ruid(void){ + return snprintf(outbuf, COLWID, "%d", pp->ruid); +} +static int pr_suid(void){ + return snprintf(outbuf, COLWID, "%d", pp->suid); +} +static int pr_fuid(void){ + return snprintf(outbuf, COLWID, "%d", pp->fuid); +} + + +static int pr_fgroup(void){ + if(strlen(pp->fgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->fgid); + return snprintf(outbuf, COLWID, "%s", pp->fgroup); +} +static int pr_sgroup(void){ + if(strlen(pp->sgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->sgid); + return snprintf(outbuf, COLWID, "%s", pp->sgroup); +} +static int pr_fuser(void){ + if(user_is_number || (strlen(pp->fuser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->fuid); + return snprintf(outbuf, COLWID, "%s", pp->fuser); +} +static int pr_suser(void){ + if(user_is_number || (strlen(pp->suser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->suid); + return snprintf(outbuf, COLWID, "%s", pp->suser); +} + + +static int pr_thread(void){ /* TID tid LWP lwp SPID spid */ + return snprintf(outbuf, COLWID, "%u", pp->pid); /* for now... FIXME */ +} +static int pr_nlwp(void){ /* THCNT thcount NLWP nlwp */ + return snprintf(outbuf, COLWID, "-"); /* for now... FIXME */ +} + +static int pr_sess(void){ + return snprintf(outbuf, COLWID, "%u", pp->session); +} +static int pr_tpgid(void){ + return snprintf(outbuf, COLWID, "%d", pp->tpgid); +} + + +/* SGI uses "cpu" to print the processor ID with header "P" */ +static int pr_sgi_p(void){ /* FIXME */ + if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor); + return snprintf(outbuf, COLWID, "*"); +} + + + +/***************************************************************************/ +/*************************** other stuff ***********************************/ + +/* + * Old header specifications. + * + * short Up " PID TTY STAT TIME COMMAND" + * long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND + * user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND + * jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND + * sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND + * vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND + * m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND + * regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND + */ + +/* + * Unix98 requires that the heading for tty is TT, though XPG4, Digital, + * and BSD use TTY. The Unix98 headers are: + * args,comm,etime,group,nice,pcpu,pgid + * pid,ppid,rgroup,ruser,time,tty,user,vsz + * + * BSD c: "command" becomes accounting name ("comm" or "ucomm") + * BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number) + */ + +/* short names to save space */ +#define MEM PROC_FILLMEM /* read statm */ +#define CMD PROC_FILLCMD /* read cmdline */ +#define ENV PROC_FILLENV /* read environ */ +#define USR PROC_FILLUSR /* uid_t and gid_t -> user and group names */ +#define BUG PROC_FILLBUG /* what does this need? */ + +/* TODO + * pull out annoying BSD aliases into another table (to macro table?) + * add sorting functions here (to unify names) + */ + +/* temporary hack -- mark new stuff grabbed from Debian ps */ +#define LNx LNX + +/* there are about 195 listed */ + +/* Many of these are placeholders for unsupported options. */ +static const format_struct format_array[] = { +/* code header print() sort() width ? vendor flags */ +{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, RIGHT}, /*pcpu*/ +{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, RIGHT}, /*pmem*/ +{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, /*acflg*/ +{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*acflag*/ +{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, RIGHT}, +{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, LEFT}, +{"alarm", "ALARM", pr_alarm, sr_it_real_value, 5, 0, LNX, RIGHT}, +{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, RIGHT}, +{"args", "COMMAND", pr_args, sr_nop, 16, 0, U98, UNLIMITED}, /*command*/ +{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, CUMUL|RIGHT}, /*cputime*/ /* was 6 wide */ +{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, SIGNAL}, /*sigmask*/ +{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, RIGHT}, +{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, RIGHT}, +{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, RIGHT}, +{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, RIGHT}, +{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, SIGNAL}, /*sigcatch*/ +{"class", "CLS", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, +{"cls", "-", pr_nop, sr_nop, 1, 0, HPU, RIGHT}, +{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, RIGHT}, +{"cmd", "CMD", pr_args, sr_cmd, 16, 0, DEC, UNLIMITED}, /*ucomm*/ +{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, RIGHT}, +{"cnswap", "-", pr_nop, sr_cnswap, 1, 0, LNX, RIGHT}, +{"comm", "COMMAND", pr_comm, sr_nop, 16, 0, U98, UNLIMITED}, /*ucomm*/ +{"command", "COMMAND", pr_args, sr_nop, 16, 0, XXX, UNLIMITED}, /*args*/ +{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, RIGHT}, /*cpu*/ +{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */ +{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, RIGHT}, /*time*/ +{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, RIGHT}, +{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, RIGHT}, +{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, LEFT}, +{"drs", "DRS", pr_drs, sr_drs, 4, 0, LNX, RIGHT}, +{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, RIGHT}, +{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, RIGHT}, +{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, 0, LNX, USER}, +{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, RIGHT}, +{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, RIGHT}, +{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, 0, LNx, UNLIMITED}, +{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, RIGHT}, +{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, RIGHT}, /* was 7 wide */ +{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, RIGHT}, +{"euser", "EUSER", pr_euser, sr_euser, 8, 0, LNX, USER}, +{"f", "F", pr_flag, sr_nop, 3, 0, XXX, RIGHT}, /*flags*/ +{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, RIGHT}, +{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, 0, LNX, USER}, +{"flag", "F", pr_flag, sr_flags, 3, 0, DEC, RIGHT}, +{"flags", "F", pr_flag, sr_flags, 3, 0, BSD, RIGHT}, /*f*/ /* was FLAGS, 8 wide */ +{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, LEFT}, +{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, RIGHT}, +{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, 0, LNX, USER}, +{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, RIGHT}, +{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, 0, LNX, USER}, +{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, RIGHT}, +{"fuser", "FUSER", pr_fuser, sr_fuser, 8, 0, LNX, USER}, +{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, RIGHT}, +{"group", "GROUP", pr_egroup, sr_egroup, 5, 0, U98, USER}, /* was 8 wide */ +{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, SIGNAL}, /*sigignore*/ +{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*inblock*/ +{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, /*inblk*/ +{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, RIGHT}, +{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, RIGHT}, +{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, RIGHT}, +{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, RIGHT}, +{"label", "LABEL", pr_nop, sr_nop, 25, 0, SGI, LEFT}, +{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, RIGHT}, +{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, LEFT}, /*logname*/ /* double check */ +{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, LEFT}, /*login*/ +{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, LEFT}, +{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, RIGHT}, +{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, RIGHT}, /* login ID */ +{"luser", "LUSER", pr_nop, sr_nop, 8, 0, LNX, USER}, /* login USER */ +{"lwp", "LWP", pr_thread, sr_nop, 5, 0, SUN, RIGHT}, +{"m_drs", "DRS", pr_drs, sr_drs, 5, 0, LNx, RIGHT}, +{"m_dt", "DT", pr_nop, sr_dt, 4, 0, LNx, RIGHT}, +{"m_lrs", "LRS", pr_nop, sr_lrs, 5, 0, LNx, RIGHT}, +{"m_resident", "RES", pr_nop, sr_resident, 5, 0, LNx, RIGHT}, +{"m_share", "SHRD", pr_nop, sr_share, 5, 0, LNx, RIGHT}, +{"m_size", "SIZE", pr_nop, sr_size, 5, 0, LNx, RIGHT}, +{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, RIGHT}, +{"m_trs", "TRS", pr_trs, sr_trs, 5, 0, LNx, RIGHT}, +{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, CUMUL|RIGHT}, +{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, RIGHT}, +{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, CUMUL|RIGHT}, +{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, RIGHT}, +{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, RIGHT}, +{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, RIGHT}, +{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, RIGHT}, /*nice*/ +{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, RIGHT}, /*ni*/ +{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, +{"nlwp", "NLWP", pr_nlwp, sr_nop, 4, 0, SUN, RIGHT}, +{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, /*nsigs*/ +{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*nsignals*/ +{"nswap", "NSWAP", pr_nop, sr_nswap, 5, 0, XXX, RIGHT}, +{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, RIGHT}, +{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, RIGHT}, +{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, RIGHT}, +{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, RIGHT}, +{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, /*oublock*/ +{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, /*oublk*/ +{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, RIGHT}, +{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, RIGHT}, +{"pagein", "PAGEIN", pr_majflt, sr_nop, 6, 0, XXX, RIGHT}, +{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, RIGHT}, /*%cpu*/ +{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, SIGNAL}, /*sig*/ +{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, RIGHT}, +{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, RIGHT}, +{"pid", "PID", pr_pid, sr_pid, 5, 0, U98, RIGHT}, +{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, RIGHT}, /*%mem*/ +{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, RIGHT}, +{"policy", "POL", pr_nop, sr_nop, 3, 0, DEC, RIGHT}, +{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, RIGHT}, +{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, RIGHT}, +{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, RIGHT}, /*ni,nice*/ /* from Linux sorting names */ +{"prmgrp", "-", pr_nop, sr_nop, 1, 0, HPU, RIGHT}, +{"prmid", "-", pr_nop, sr_nop, 1, 0, HPU, RIGHT}, +{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, RIGHT}, +{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, RIGHT}, +{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, RIGHT}, +{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, RIGHT}, +{"resident", "RES", pr_nop, sr_resident, 5, 0, LNX, RIGHT}, +{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, RIGHT}, +{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 6, 0, U98, USER}, /* was 8 wide */ +{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, RIGHT}, +{"rss", "RSS", pr_rss, sr_rss, 4, 0, XXX, RIGHT}, /* was 5 wide */ +{"rssize", "RSS", pr_rss, sr_vm_rss, 4, 0, DEC, RIGHT}, /*rsz*/ +{"rsz", "RSZ", pr_rss, sr_vm_rss, 4, 0, BSD, RIGHT}, /*rssize*/ +{"rtprio", "RTPRIO", pr_nop, sr_nop, 7, 0, BSD, RIGHT}, +{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, RIGHT}, +{"ruser", "RUSER", pr_ruser, sr_ruser, 8, 0, U98, USER}, +{"s", "S", pr_s, sr_state, 1, 0, SUN, LEFT}, /*stat,state*/ +{"sched", "SCH", pr_nop, sr_nop, 1, 0, AIX, RIGHT}, +{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, RIGHT}, /* man page misspelling of scount? */ +{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, RIGHT}, /* scnt==scount, DEC claims both */ +{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, RIGHT}, +{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, RIGHT}, +{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, RIGHT}, /* "cpu" number */ +{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, LEFT}, /* SZ:RSS */ +{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, RIGHT}, +{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, 0, LNX, USER}, +{"share", "-", pr_nop, sr_share, 1, 0, LNX, RIGHT}, +{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, RIGHT}, /* Sun & HP */ +{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, SIGNAL}, /*pending*/ +{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, SIGNAL}, +{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, SIGNAL}, +{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, SIGNAL}, +{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, SIGNAL}, +{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, SIGNAL}, /*caught*/ +{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, SIGNAL}, /*ignored*/ +{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, SIGNAL}, /*blocked*/ +{"size", "-", pr_nop, sr_size, 1, 0, SCO, RIGHT}, +{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, RIGHT}, +{"spid", "SPID", pr_thread, sr_nop, 5, 0, SGI, RIGHT}, +{"stackp", "STACKP", pr_stackp, sr_nop, 8, 0, LNX, RIGHT}, /*start_stack*/ +{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, RIGHT}, +{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, RIGHT}, +{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, RIGHT}, /*stackp*/ +{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, RIGHT}, +{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, LEFT}, /*state,s*/ +{"state", "S", pr_s, sr_state, 1, 0, XXX, LEFT}, /*stat,s*/ /* was STAT */ +{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, /* CUMUL| */RIGHT}, /* was 6 wide */ +{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, RIGHT}, +{"suser", "SUSER", pr_suser, sr_suser, 8, 0, LNx, USER}, +{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, RIGHT}, +{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, 0, LNX, USER}, +{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, RIGHT}, +{"svuser", "SVUSER", pr_suser, sr_suser, 8, 0, LNX, USER}, +{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, RIGHT}, +{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, RIGHT}, +{"thcount", "THCNT", pr_nlwp, sr_nop, 5, 0, AIX, RIGHT}, +{"tid", "TID", pr_thread, sr_nop, 5, 0, AIX, RIGHT}, +{"time", "TIME", pr_time, sr_nop, 8, 0, U98, CUMUL|RIGHT}, /*cputime*/ /* was 6 wide */ +{"timeout", "TMOUT", pr_timeout, sr_timeout, 5, 0, LNX, RIGHT}, +{"tmout", "TMOUT", pr_timeout, sr_timeout, 5, 0, LNX, RIGHT}, +{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, LEFT}, +{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, RIGHT}, +{"trs", "TRS", pr_trs, sr_trs, 4, 0, AIX, RIGHT}, +{"trss", "TRSS", pr_trs, sr_trs, 4, 0, BSD, RIGHT}, /* 4.3BSD NET/2 */ +{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, +{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, +{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, RIGHT}, +{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, LEFT}, +{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */ +{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, LEFT}, +{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, LEFT}, +{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, RIGHT}, +{"ucmd", "CMD", pr_comm, sr_cmd, 16, 0, DEC, UNLIMITED}, /*ucomm*/ +{"ucomm", "COMMAND", pr_comm, sr_nop, 16, 0, XXX, UNLIMITED}, /*comm*/ +{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, RIGHT}, +{"uid_hack", "UID", pr_euser, sr_nop, 8, 0, XXX, USER}, +{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, RIGHT}, +{"uname", "USER", pr_euser, sr_euser, 8, 0, DEC, USER}, /* man page misspelling of user? */ +{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, RIGHT}, /*usrpri*/ +{"uprocp", "-", pr_nop, sr_nop, 1, 0, BSD, RIGHT}, +{"user", "USER", pr_euser, sr_euser, 8, 0, U98, USER}, /* BSD n forces this to UID */ +{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, RIGHT}, +{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, RIGHT}, /*upr*/ +{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, CUMUL|RIGHT}, +{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, RIGHT}, +{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, RIGHT}, +{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, RIGHT}, +{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, RIGHT}, +{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, RIGHT}, +{"vsize", "VSZ", pr_vsz, sr_vsize, 5, 0, DEC, RIGHT}, /*vsz*/ +{"vsz", "VSZ", pr_vsz, sr_vm_size, 5, 0, U98, RIGHT}, /*vsize*/ +{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, 0, XXX, WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */ +{"wname", "WCHAN", pr_wname, sr_nop, 6, 0, SGI, WCHAN}, /* opposite of nwchan */ +{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, RIGHT}, +{"~", "-", pr_nop, sr_nop, 1, 0, LNX, RIGHT} /* NULL would ruin alphabetical order */ +}; + +static const int format_array_count = sizeof(format_array)/sizeof(format_struct); + + +/****************************** Macro formats *******************************/ +/* First X field may be NR, which is p->start_code>>26 printed with %2ld */ +/* That seems useless though, and Debian already killed it. */ +/* The ones marked "Digital" have the name defined, not just the data. */ +static const macro_struct macro_array[] = { +{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */ +{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */ +{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */ +{"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */ +{"END_SYS5", "state,tname,time,command"}, /* trailer for -O */ +{"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */ + +{"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */ +{"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */ +{"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */ +{"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */ +{"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */ + +{"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */ +{"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */ +{"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */ +{"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */ +{"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */ +{"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */ + +{"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */ + +{"HP_", "pid,tty,time,comm"}, /* HP default */ +{"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */ +{"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */ +{"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */ + +{"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */ +{"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */ +{"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */ +{"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */ + +{"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */ +{"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */ +{"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */ +{"OL_m", "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */ +{"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */ +{"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */ +{"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */ + +{"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */ +{"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */ +{"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */ +{"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */ +{"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */ +{"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */ + +{"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */ +{"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */ +{"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */ + +{"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */ +{"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */ +{"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */ + +{"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */ +{"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */ +{"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */ +{"~", "~"} /* NULL would ruin alphabetical order */ +}; + +static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct); + + +/*************************** AIX formats ********************/ +/* Convert AIX format codes to normal format specifiers. */ +static const aix_struct aix_array[] = { +{'C', "pcpu", "%CPU"}, +{'G', "group", "GROUP"}, +{'P', "ppid", "PPID"}, +{'U', "user", "USER"}, +{'a', "args", "COMMAND"}, +{'c', "comm", "COMMAND"}, +{'g', "rgroup", "RGROUP"}, +{'n', "nice", "NI"}, +{'p', "pid", "PID"}, +{'r', "pgid", "PGID"}, +{'t', "etime", "ELAPSED"}, +{'u', "ruser", "RUSER"}, +{'x', "time", "TIME"}, +{'y', "tty", "TTY"}, +{'z', "vsz", "VSZ"}, +{'~', "~", "~"} /* NULL would ruin alphabetical order */ +}; +static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct); + + +/********************* sorting ***************************/ +/* Convert short sorting codes to normal format specifiers. */ +static const shortsort_struct shortsort_array[] = { +{'C', "pcpu" }, +{'G', "tpgid" }, +{'J', "cstime" }, +/* {'K', "stime" }, */ /* conflict, system vs. start time */ +{'M', "maj_flt" }, +{'N', "cmaj_flt" }, +{'P', "ppid" }, +{'R', "resident" }, +{'S', "share" }, +{'T', "start_time" }, +{'U', "uid" }, /* euid */ +{'c', "cmd" }, +{'f', "flags" }, +{'g', "pgrp" }, +{'j', "cutime" }, +{'k', "utime" }, +{'m', "min_flt" }, +{'n', "cmin_flt" }, +{'o', "session" }, +{'p', "pid" }, +{'r', "rss" }, +{'s', "size" }, +{'t', "tty" }, +{'u', "user" }, +{'v', "vsize" }, +{'y', "priority" }, /* nice */ +{'~', "~" } /* NULL would ruin alphabetical order */ +}; +static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct); + + +/*********** print format_array **********/ +/* called by the parser in another file */ +void print_format_specifiers(void){ + const format_struct *walk = format_array; + while(*(walk->spec) != '~'){ + if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head); + walk++; + } +} + +/************ comparison functions for bsearch *************/ + +static int compare_format_structs(const void *a, const void *b){ + return strcmp(((format_struct*)a)->spec,((format_struct*)b)->spec); +} + +static int compare_macro_structs(const void *a, const void *b){ + return strcmp(((macro_struct*)a)->spec,((macro_struct*)b)->spec); +} + +/******** look up structs as needed by the sort & format parsers ******/ + +const shortsort_struct *search_shortsort_array(const int findme){ + const shortsort_struct *walk = shortsort_array; + while(walk->desc != '~'){ + if(walk->desc == findme) return walk; + walk++; + } + return NULL; +} + +const aix_struct *search_aix_array(const int findme){ + const aix_struct *walk = aix_array; + while(walk->desc != '~'){ + if(walk->desc == findme) return walk; + walk++; + } + return NULL; +} + +const format_struct *search_format_array(const char *findme){ + format_struct key; + key.spec = findme; + return bsearch(&key, format_array, format_array_count, + sizeof(format_struct), compare_format_structs + ); +} + +const macro_struct *search_macro_array(const char *findme){ + macro_struct key; + key.spec = findme; + return bsearch(&key, macro_array, macro_array_count, + sizeof(macro_struct), compare_macro_structs + ); +} + +static unsigned int active_cols; /* some multiple of screen_cols */ + +/***** Last chance, avoid needless trunctuation. */ +static void check_header_width(void){ + format_node *walk = format_list; + unsigned int total = 0; + int was_normal = 0; + unsigned int i = 0; + unsigned int sigs = 0; + while(walk){ + switch((walk->flags) & JUST_MASK){ + default: + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case SIGNAL: + sigs++; + total += walk->width; + total += was_normal; + was_normal = 1; + break; + case UNLIMITED: /* could chop this a bit */ + if(walk->next) total += walk->width; + else total += 3; /* not strlen(walk->name) */ + total += was_normal; + was_normal = 1; + break; + case 0: /* AIX */ + total += walk->width; + was_normal = 0; + break; + } + walk = walk->next; + } + for(;;){ + i++; + active_cols = screen_cols * i; + if(active_cols>=total) break; + if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */ + } + wide_signals = (total+sigs*7 <= active_cols); + +#if 0 + printf("123456789-123456789-123456789-123456789-" + "123456789-123456789-123456789-123456789\n"); + printf("need %d, using %d\n", total, active_cols); +#endif +} + + +/********** show one process (NULL proc prints header) **********/ +void show_one_proc(proc_t* p){ + /* unknown: maybe set correct & actual to 1, remove +/- 1 below */ + int correct = 0; /* screen position we should be at */ + int actual = 0; /* screen position we are at */ + int amount = 0; /* amount of text that this data is */ + int leftpad = 0; /* amount of space this column _could_ need */ + int space = 0; /* amount of space we actually need to print */ + int dospace = 0; /* previous column determined that we need a space */ + int legit = 0; /* legitimately stolen extra space */ + format_node *fmt = format_list; + static int did_stuff = 0; /* have we ever printed anything? */ + + if(-1==(long)p){ /* true only once, at the end */ + check_header_width(); /* temporary test code */ + if(did_stuff) return; + /* have _never_ printed anything, but might need a header */ + if(!--lines_to_next_header){ + lines_to_next_header = header_gap; + show_one_proc(NULL); + } + /* fprintf(stderr, "No processes available.\n"); */ /* legal? */ + exit(1); + } + if(p){ /* not header, maybe we should call ourselves for it */ + if(!--lines_to_next_header){ + lines_to_next_header = header_gap; + show_one_proc(NULL); + } + } + did_stuff = 1; + pp = p; /* global, the proc_t struct */ + if(active_cols>(int)OUTBUF_SIZE) fprintf(stderr,"Fix bigness error.\n"); + + /* print row start sequence */ + for(;;){ + legit = 0; + /* set width suggestion which might be ignored */ + if(fmt->next) max_rightward = fmt->width; + else max_rightward = active_cols-((correct>actual) ? correct : actual); + max_leftward = fmt->width + actual - correct; /* TODO check this */ + /* prepare data and calculate leftpad */ + if(p && fmt->pr) amount = (*fmt->pr)(); + else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */ + switch((fmt->flags) & JUST_MASK){ + case 0: /* for AIX, assigned outside this file */ + leftpad = 0; + break; + case LEFT: /* bad */ + leftpad = 0; + break; + case RIGHT: /* OK */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + case SIGNAL: + /* if the screen is wide enough, use full 16-character output */ + if(wide_signals){ + leftpad = 16 - amount; + legit = 7; + }else{ + leftpad = 9 - amount; + } + if(leftpad < 0) leftpad = 0; + break; + case USER: /* bad */ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + if(!user_is_number) leftpad = 0; + break; + case WCHAN: /* bad */ + if(wchan_is_number){ + leftpad = fmt->width - amount; + if(leftpad < 0) leftpad = 0; + break; + }else{ + if(fmt->next){ + outbuf[fmt->width] = '\0'; /* Must chop, more columns! */ + }else{ + int chopspot; /* place to chop */ + int tmpspace; /* need "space" before it is calculated below */ + tmpspace = correct - actual; + if(tmpspace<1) tmpspace = dospace; + chopspot = active_cols-actual-tmpspace; + if(chopspot<1) chopspot=1; /* oops, we (mostly) lose this column... */ + outbuf[chopspot] = '\0'; /* chop at screen/buffer limit */ + } + leftpad = 0; + break; + } + case UNLIMITED: + if(fmt->next){ + outbuf[fmt->width] = '\0'; /* Must chop, more columns! */ + }else{ + int chopspot; /* place to chop */ + int tmpspace; /* need "space" before it is calculated below */ + tmpspace = correct - actual; + if(tmpspace<1) tmpspace = dospace; + chopspot = active_cols-actual-tmpspace; + if(chopspot<1) chopspot=1; /* oops, we (mostly) lose this column... */ + outbuf[chopspot] = '\0'; /* chop at screen/buffer limit */ + } + leftpad = 0; + break; + default: + fputs("bad alignment code\n", stderr); + break; + } + /* At this point: + * + * correct from previous column + * actual from previous column + * amount not needed (garbage due to chopping) + * leftpad left padding for this column alone (not make-up or gap) + * space not needed (will recalculate now) + * dospace if we require space between this and the prior column + * legit space we were allowed to steal, and thus did steal + */ + space = correct - actual + leftpad; + if(space<1) space=dospace; + if(space>SPACE_AMOUNT) space=SPACE_AMOUNT; + + /* print data, set x position stuff */ + amount = strlen(outbuf); /* post-chop data width */ + if(!fmt->next){ + /* Last column. Write padding + data + newline all together. */ + outbuf[amount] = '\n'; + fwrite(outbuf-space, space+amount+1, 1, stdout); + break; + } + /* Not the last column. Write padding + data together. */ + fwrite(outbuf-space, space+amount, 1, stdout); + actual += space+amount; + correct += fmt->width; + correct += legit; /* adjust for SIGNAL expansion */ + if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */ + correct++; + dospace = 1; + }else{ + dospace = 0; + } + fmt = fmt->next; + /* At this point: + * + * correct screen position we should be at + * actual screen position we are at + * amount not needed + * leftpad not needed + * space not needed + * dospace if have determined that we need a space next time + * legit not needed + */ + } +} + +#ifdef TESTING +static void sanity_check(void){ + format_struct *fs = format_array; + while((fs->spec)[0] != '~'){ + if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec); + fs++; + } +} +#endif + +void init_output(void){ + memset(whitespace, ' ', PAGE_SIZE); +#if 0 + mprotect(whitespace, PAGE_SIZE, PROT_READ); /* FIXME may fail if unaligned */ + mprotect( + (void *)((unsigned long)(whitespace_and_outbuf-PAGE_SIZE) &~ (PAGE_SIZE-1)), + PAGE_SIZE, PROT_NONE + ); +#endif + seconds_since_1970 = time(NULL); + time_of_boot = seconds_since_1970 - seconds_since_boot; + meminfo(); + switch(getpagesize()){ + case 65536: page_shift = 16; break; + case 32768: page_shift = 15; break; + case 16384: page_shift = 14; break; + case 8192: page_shift = 13; break; + default: fprintf(stderr, "Unknown page size! (assume 4096)\n"); + case 4096: page_shift = 12; break; + case 2048: page_shift = 11; break; + case 1024: page_shift = 10; break; + } + check_header_width(); +} diff --git a/ps/p b/ps/p new file mode 100755 index 00000000..880df4a6 --- /dev/null +++ b/ps/p @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with: ps "pid,user pcpu,pmem" +# The "$@" won't break that into 2 arguments. +# +LD_PRELOAD=../proc/libproc.so exec ./ps "$@" diff --git a/ps/parser.c b/ps/parser.c new file mode 100644 index 00000000..4ecf0e8b --- /dev/null +++ b/ps/parser.c @@ -0,0 +1,1123 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights reserved. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +/* Ought to have debug print stuff like this: + * #define Print(fmt, args...) printf("Debug: " fmt, ## args) + */ + + +#include +#include +#include + +/* username lookups */ +#include +#include +#include + +#include +#include + +#include "common.h" +#include "../proc/version.h" + +#define ARG_GNU 0 +#define ARG_END 1 +#define ARG_PGRP 2 +#define ARG_SYSV 3 +#define ARG_PID 4 +#define ARG_BSD 5 +#define ARG_FAIL 6 +#define ARG_SESS 7 + +static int w_count = 0; + +static int ps_argc; /* global argc */ +static char **ps_argv; /* global argv */ +static int thisarg; /* index into ps_argv */ +static char *flagptr; /* current location in ps_argv[thisarg] */ +static int not_pure_unix = 0; /* set by BSD and GNU options */ +static int force_bsd = 0; /* set when normal parsing fails */ + +#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x))\ + return "The " x " option is exclusive." + + +/********** utility functions **********/ + +/* + * Both "-Oppid" and "-O ppid" should be legal, though Unix98 + * does not require it. BSD and Digital Unix allow both. + * Return the argument or NULL; + */ +static const char *get_opt_arg(void){ + if(*(flagptr+1)){ /* argument is part of ps_argv[thisarg] */ + not_pure_unix = 1; + return flagptr+1; + } + if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */ + /* argument follows ps_argv[thisarg] */ + if(*(ps_argv[thisarg+1]) == '\0') return NULL; + return ps_argv[++thisarg]; +} + +/********** parse lists (of UID, tty, GID, PID...) **********/ + +static const char *parse_pid(char *str, sel_union *ret){ + char *endp; + int num; + static const char *pidrange = "Process ID out of range."; + static const char *pidsyntax = "Process ID list syntax error."; + num = strtol(str, &endp, 0); + if(*endp != '\0') return pidsyntax; + if(num>0x7fff) return pidrange; /* Linux PID limit */ + if(num<1) return pidrange; + ret->pid = num; + return 0; +} + +static const char *parse_uid(char *str, sel_union *ret){ + struct passwd *passwd_data; + char *endp; + unsigned long num; + static const char *uidrange = "User ID out of range."; + static const char *uidexist = "User name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + passwd_data = getpwnam(str); + if(!passwd_data) return uidexist; + num = passwd_data->pw_uid; + } + if(num > 0xfffffffeUL) return uidrange; + ret->uid = num; + return 0; +} + +static const char *parse_gid(char *str, sel_union *ret){ + struct group *group_data; + char *endp; + unsigned long num; + static const char *gidrange = "Group ID out of range."; + static const char *gidexist = "Group name does not exist."; + num = strtoul(str, &endp, 0); + if(*endp != '\0'){ /* hmmm, try as login name */ + group_data = getgrnam(str); + if(!group_data) return gidexist; + num = group_data->gr_gid; + } + if(num > 0xfffffffeUL) return gidrange; + ret->gid = num; + return 0; +} + +static const char *parse_cmd(char *str, sel_union *ret){ + strncpy(ret->cmd, str, 8); /* strncpy pads to end */ + return 0; +} + +static const char *parse_tty(char *str, sel_union *ret){ + struct stat sbuf; + static const char *missing = "TTY could not be found."; + static const char *not_tty = "List member was not a TTY."; + char path[4096]; + if(str[0]=='/'){ + if(stat(str, &sbuf) >= 0) goto found_it; + return missing; + } +#define lookup(p) \ + snprintf(path,4096,p,str); \ + if(stat(path, &sbuf) >= 0) goto found_it + + lookup("/dev/pts/%s"); /* New Unix98 ptys go first */ + lookup("/dev/%s"); + lookup("/dev/tty%s"); + lookup("/dev/pty%s"); + lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */ + if(!strcmp(str,"-")){ /* "-" means no tty (from AIX) */ + ret->tty = -1; /* processes w/o tty */ + return 0; + } + if(!strcmp(str,"?")){ /* "?" means no tty, which bash eats (Reno BSD?) */ + ret->tty = -1; /* processes w/o tty */ + return 0; + } + if(!*(str+1) && (stat(str,&sbuf)>=0)){ /* Kludge! Assume bash ate '?'. */ + ret->tty = -1; /* processes w/o tty */ + return 0; + } +#undef lookup + return missing; +found_it: + if(!S_ISCHR(sbuf.st_mode)) return not_tty; + ret->tty = sbuf.st_rdev; + return 0; +} + +/* + * Used to parse lists in a generic way. (function pointers) + */ +static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){ + selection_node *node; + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t," */ + char *walk; + int items; + int need_item; + const char *err; /* error code that could or did happen */ + /*** prepare to operate ***/ + node = malloc(sizeof(selection_node)); + node->u = malloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */ + node->n = 0; + buf = malloc(strlen(arg)+1); + strcpy(buf, arg); + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + err = "Improper list."; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\0': + if(need_item) goto parse_error; + need_item=1; + break; + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); + if(need_item) goto parse_error; + node->n = items; + /*** actually parse the list ***/ + walk = buf; + while(items--){ + sep_loc = strpbrk(walk," ,\t"); + if(sep_loc) *sep_loc = '\0'; + if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + node->next = selection_list; + selection_list = node; + return NULL; +parse_error: + free(buf); + free(node->u); + free(node); + return err; +} + +/***************** parse SysV options, including Unix98 *****************/ +static const char *parse_sysv_option(void){ + const char *arg; + const char *err; + flagptr = ps_argv[thisarg]; + while(*++flagptr){ + /* Find any excuse to ignore stupid Unix98 misfeatures. */ + if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; + switch(*flagptr){ + case 'A': + trace("-A selects all processes.\n"); + all_processes = 1; + break; + case 'C': /* end */ + trace("-C select by process name.\n"); /* Why only HP/UX and us? */ + arg=get_opt_arg(); + if(!arg) return "List of command names must follow -C."; + err=parse_list(arg, parse_cmd); + if(err) return err; + selection_list->typecode = SEL_COMM; + return NULL; /* can't have any more options */ + case 'F': /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */ + trace("-F does fuller listing\n"); + format_modifiers |= FM_F; + format_flags |= FF_Uf; + unix_f_option = 1; /* does this matter? */ + break; + case 'G': /* end */ + trace("-G select by RGID (supports names)\n"); + arg=get_opt_arg(); + if(!arg) return "List of real groups must follow -G."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_RGID; + return NULL; /* can't have any more options */ + case 'H': /* another nice HP/UX feature */ + trace("-H Process heirarchy (like ASCII art forest option)\n"); + forest_type = 'u'; + break; + case 'L': /* */ + /* In spite of the insane 2-level thread system, Sun appears to + * have made this option Linux-compatible. If a process has N + * threads, ps will produce N lines of output. (not N+1 lines) + * Zombies are the only exception, with NLWP==0 and 1 output line. + * SCO UnixWare uses -L too. + */ + trace("-L Print LWP (thread) info.\n"); + format_modifiers |= FM_L; + break; + case 'M': /* someday, maybe, we will have MAC like SGI's Irix */ + trace("-M Print security label for Mandatory Access Control.\n"); + format_modifiers |= FM_M; + return "Sorry, no Mandatory Access Control support."; + break; + case 'N': + trace("-N negates.\n"); + negate_selection = 1; + break; + case 'O': /* end */ + trace("-O is preloaded -o.\n"); + arg=get_opt_arg(); + if(!arg) return "Format or sort specification must follow -O."; + defer_sf_option(arg, SF_U_O); + return NULL; /* can't have any more options */ + case 'P': /* SunOS 5 "psr" or unknown HP/UX feature */ + trace("-P adds columns of PRM info (HP) or PSR column (Sun)\n"); + format_modifiers |= FM_P; + break; +#ifdef WE_UNDERSTAND_THIS + case 'R': /* unknown HP/UX feature */ + trace("-R selects PRM groups\n"); + return "Don't understand PRM on Linux."; + break; +#endif + case 'T': + /* IRIX 6.5 docs suggest POSIX threads get shown individually. + * This would make -T be like -L, -m, and m. (but an extra column) + * Testing (w/ normal processes) shows 1 line/process, not 2. + * Also, testing shows PID==SPID for all normal processes. + */ + trace("-T adds strange SPID column (old sproc() threads?)\n"); + format_modifiers |= FM_T; + break; + case 'U': /* end */ + trace("-U select by RUID (supports names).\n"); + arg=get_opt_arg(); + if(!arg) return "List of real groups must follow -U."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_RUID; + return NULL; /* can't have any more options */ + case 'V': /* single */ + trace("-V prints version.\n"); + exclusive("-V"); + display_version(); + exit(0); + case 'Z': /* full Mandatory Access Control level info */ + trace("-Z shows full MAC info\n"); + return "Don't understand MAC on Linux."; + break; + case 'a': + trace("-a select all with a tty, but omit session leaders.\n"); + simple_select |= SS_U_a; + break; + case 'c': + /* HP-UX and SunOS 5 scheduling info modifier */ + trace("-c changes scheduling info.\n"); + format_modifiers |= FM_c; + break; + case 'd': + trace("-d select all, but omit session leaders.\n"); + simple_select |= SS_U_d; + break; + case 'e': + trace("-e selects all processes.\n"); + all_processes = 1; + break; + case 'f': + trace("-f does full listing\n"); + format_flags |= FF_Uf; + unix_f_option = 1; /* does this matter? */ + break; + case 'g': /* end */ + trace("-g selects by session leader OR by group name\n"); + arg=get_opt_arg(); + if(!arg) return "List of session leaders OR effective group names must follow -g."; + err=parse_list(arg, parse_pid); + if(!err){ + selection_list->typecode = SEL_SESS; + return NULL; /* can't have any more options */ + } + err=parse_list(arg, parse_gid); + if(!err){ + selection_list->typecode = SEL_EGID; + return NULL; /* can't have any more options */ + } + return "List of session leaders OR effective group IDs was invalid."; + case 'j': + trace("-j jobs format.\n"); + /* Debian uses RD_j and Digital uses JFMT */ + if(sysv_j_format) format_flags |= FF_Uj; + else format_modifiers |= FM_j; + break; + case 'l': + trace("-l long format.\n"); + format_flags |= FF_Ul; + break; + case 'm': + trace("-m shows threads.\n"); + /* note that AIX shows 2 lines for a normal process */ + /* not implemented -- silently ignore the option */ + break; + case 'n': /* end */ + trace("-n sets namelist file.\n"); + arg=get_opt_arg(); + if(!arg) return "Alternate System.map file must follow -n."; + namelist_file = arg; + return NULL; /* can't have any more options */ + case 'o': /* end */ + /* Unix98 has gross behavior regarding this. From the following: */ + /* ps -o pid,nice=NICE,tty=TERMINAL,comm */ + /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm" */ + /* Yes, the second column has the name "NICE,tty=TERMINAL,comm" */ + /* This parser looks for any excuse to ignore that braindamage. */ + trace("-o user-defined format.\n"); + arg=get_opt_arg(); + if(!arg) return "Format specification must follow -o."; + not_pure_unix |= defer_sf_option(arg, SF_U_o); + return NULL; /* can't have any more options */ + case 'p': /* end */ + trace("-p select by PID.\n"); + arg=get_opt_arg(); + if(!arg) return "List of process IDs must follow -p."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ +#ifdef KNOW_WHAT_TO_DO_WITH_THIS + case 'r': + trace("-r some Digital Unix thing about warnings...\n"); + trace(" or SCO's option to chroot() for new /proc and /dev.\n"); + return "The -r option is reserved."; + break; +#endif + case 's': /* end */ + trace("-s Select processes belonging to the sessions given.\n"); + arg=get_opt_arg(); + if(!arg) return "List of session IDs must follow -s."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_SESS; + return NULL; /* can't have any more options */ + case 't': /* end */ + trace("-t select by tty.\n"); + arg=get_opt_arg(); + if(!arg) return "List of terminals (pty, tty...) must follow -t."; + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; /* can't have any more options */ + case 'u': /* end */ + trace("-u select by user ID (the EUID?) (supports names).\n"); + arg=get_opt_arg(); + if(!arg) return "List of users must follow -u."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; /* can't have any more options */ + case 'w': + trace("-w wide output.\n"); + w_count++; + break; +#ifdef NOBODY_HAS_BSD_HABITS_ANYMORE + case 'x': /* Same as -y, but for System V Release 4 MP */ + trace("-x works like Sun Solaris & SCO Unixware -y option\n"); + format_modifiers |= FM_x; + break; +#endif + case 'y': /* Sun's -l hack (also: Irix "lnode" resource control info) */ + trace("-y Print lnone info in UID/USER column or do Sun -l hack.\n"); + format_modifiers |= FM_y; + break; + case 'z': /* alias of Mandatory Access Control level info */ + trace("-z shows aliased MAC info\n"); + return "Don't understand MAC on Linux."; + break; + case '-': + return "Embedded '-' among SysV options makes no sense."; + break; + case '\0': + return "Please report the \"SysV \\0 can't happen\" bug."; + break; + default: + return "Unsupported SysV option."; + } /* switch */ + } /* while */ + return NULL; +} + +/************************* parse BSD options **********************/ +static const char *parse_bsd_option(void){ + const char *arg; + const char *err; + + flagptr = ps_argv[thisarg]; /* assume we _have_ a '-' */ + if(flagptr[0]=='-'){ + if(!force_bsd) return "Can't happen! Problem #1."; + }else{ + flagptr--; /* off beginning, will increment before use */ + if(personality & PER_FORCE_BSD){ + if(!force_bsd) return "Can't happen! Problem #2."; + }else{ + if(force_bsd) return "2nd chance parse failed, not BSD or SysV."; + } + } + + while(*++flagptr){ + switch(*flagptr){ + case '0' ... '9': /* end */ + trace("0..9 Old BSD-style select by process ID\n"); + arg=flagptr; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ +#if 0 + case 'A': + /* maybe this just does a larger malloc() ? */ + trace("A Increases the argument space (Digital Unix)\n"); + return "Option A is reserved."; + break; + case 'C': + /* should divide result by 1-(e**(foo*log(bar))) */ + trace("C Use raw CPU time for %%CPU instead of decaying ave\n"); + return "Option C is reserved."; + break; +#endif + case 'L': /* single */ + trace("L List all format specifiers\n"); + exclusive("L"); + print_format_specifiers(); + exit(0); +#if 0 + case 'M': + trace("M junk (use alternate core)\n"); + return "Option M is unsupported, try N or -n instead."; + break; +#endif + case 'N': /* end */ + trace("N Specify namelist file\n"); + arg=get_opt_arg(); + if(!arg) return "Alternate System.map file must follow N."; + namelist_file = arg; + return NULL; /* can't have any more options */ + case 'O': /* end */ + trace("O Like o + defaults, add new columns after PID. Also sort.\n"); + arg=get_opt_arg(); + if(!arg) return "Format or sort specification must follow O."; + defer_sf_option(arg, SF_B_O); + return NULL; /* can't have any more options */ + break; + case 'S': + trace("S include dead kids in sum\n"); + include_dead_children = 1; + break; + case 'T': + trace("T Select all processes on this terminal\n"); + /* put our tty on a tiny list */ + { + selection_node *node; + node = malloc(sizeof(selection_node)); + node->u = malloc(sizeof(sel_union)); + node->u[0].tty = cached_tty; + node->typecode = SEL_TTY; + node->n = 1; + node->next = selection_list; + selection_list = node; + } + break; + case 'U': /* end */ + trace("U Select processes for specified users.\n"); + arg=get_opt_arg(); + if(!arg) return "List of users must follow U."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; /* can't have any more options */ + case 'V': /* single */ + trace("V show version info\n"); + exclusive("V"); + display_version(); + exit(0); + case 'W': + trace("W N/A get swap info from ... not /dev/drum.\n"); + return "Obsolete W option not supported. (You have a /dev/drum?)"; + break; + case 'X': + trace("X Old Linux i386 register format\n"); + format_flags |= FF_LX; + break; + case 'a': + trace("a Select all w/tty, including other users\n"); + simple_select |= SS_B_a; + break; + case 'c': + trace("c true command name\n"); + bsd_c_option = 1; + break; + case 'e': + trace("e environment\n"); + bsd_e_option = 1; + break; + case 'f': + trace("f ASCII art forest\n"); + forest_type = 'b'; + break; + case 'g': + trace("g _all_, even group leaders!.\n"); + simple_select |= SS_B_g; + break; + case 'h': + trace("h Repeat header... yow.\n"); + if(header_type) return "Only one heading option may be specified."; + if(personality & PER_BSD_h) header_type = HEAD_MULTI; + else header_type = HEAD_NONE; + break; + case 'j': + trace("j job control format\n"); + format_flags |= FF_Bj; + break; +#if 0 + case 'k': + trace("k N/A Use /vmcore as c-dumpfile\n"); + return "Obsolete k option not supported."; + break; +#endif + case 'l': + trace("l Display long format\n"); + format_flags |= FF_Bl; + break; + case 'm': + trace("m all threads, sort on mem use, show mem info\n"); + if(personality & PER_OLD_m){ + format_flags |= FF_Lm; + break; + } + if(personality & PER_BSD_m){ + defer_sf_option("pmem", SF_B_m); + break; + } + /* not implemented -- silently ignore the option */ + break; + case 'n': + trace("n Numeric output for WCHAN, and USER replaced by UID\n"); + wchan_is_number = 1; + user_is_number = 1; + /* TODO add tty_is_number too? */ + break; + case 'o': /* end */ + trace("o Specify user-defined format\n"); + arg=get_opt_arg(); + if(!arg) return "Format specification must follow o."; + defer_sf_option(arg, SF_B_o); + return NULL; /* can't have any more options */ + case 'p': /* end */ + trace("p Select by process ID\n"); + arg=get_opt_arg(); + if(!arg) return "List of process IDs must follow p."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; /* can't have any more options */ + case 'r': + trace("r Select running processes\n"); + running_only = 1; + break; + case 's': + trace("s Display signal format\n"); + format_flags |= FF_Bs; + break; + case 't': /* end */ + trace("t Select by tty.\n"); + /* List of terminals (tty, pty...) _should_ follow t. */ + arg=get_opt_arg(); + if(!arg){ + /* Wow, obsolete BSD syntax. Put our tty on a tiny list. */ + selection_node *node; + node = malloc(sizeof(selection_node)); + node->u = malloc(sizeof(sel_union)); + node->u[0].tty = cached_tty; + node->typecode = SEL_TTY; + node->n = 1; + node->next = selection_list; + selection_list = node; + return NULL; + } + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; /* can't have any more options */ + case 'u': + trace("u Display user-oriented\n"); + format_flags |= FF_Bu; + break; + case 'v': + trace("v Display virtual memory\n"); + format_flags |= FF_Bv; + break; + case 'w': + trace("w wide output\n"); + w_count++; + break; + case 'x': + trace("x Select processes without controlling ttys\n"); + simple_select |= SS_B_x; + break; + case '-': + return "Embedded '-' among BSD options makes no sense."; + break; + case '\0': + return "Please report the \"BSD \\0 can't happen\" bug."; + break; + default: + return "Unsupported option (BSD syntax)"; + } /* switch */ + } /* while */ + return NULL; +} + +/*************** gnu long options **********************/ + +/* + * Return the argument or NULL + */ +static const char *grab_gnu_arg(void){ + switch(*flagptr){ /* argument is part of ps_argv[thisarg] */ + default: + return NULL; /* something bad */ + case '=': case ':': + if(*++flagptr) return flagptr; /* found it */ + return NULL; /* empty '=' or ':' */ + case '\0': /* try next argv[] */ + } + if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */ + /* argument follows ps_argv[thisarg] */ + if(*(ps_argv[thisarg+1]) == '\0') return NULL; + return ps_argv[++thisarg]; +} + +typedef struct gnu_table_struct { + const char *name; /* long option name */ + const void *jump; /* See gcc extension info. :-) */ +} gnu_table_struct; + +static int compare_gnu_table_structs(const void *a, const void *b){ + return strcmp(((gnu_table_struct*)a)->name,((gnu_table_struct*)b)->name); +} + +/* Option arguments are after ':', after '=', or in argv[n+1] */ +static const char *parse_gnu_option(void){ + const char *arg; + const char *err; + char *s; + size_t sl; + char buf[16]; + gnu_table_struct findme = { buf, NULL}; + gnu_table_struct *found; + static const gnu_table_struct gnu_table[] = { + {"Group", &&case_Group}, /* rgid */ + {"User", &&case_User}, /* ruid */ + {"cols", &&case_cols}, + {"columns", &&case_columns}, + {"cumulative", &&case_cumulative}, + {"deselect", &&case_deselect}, /* -N */ + {"forest", &&case_forest}, /* f -H */ + {"format", &&case_format}, + {"group", &&case_group}, /* egid */ + {"header", &&case_header}, + {"headers", &&case_headers}, + {"heading", &&case_heading}, + {"headings", &&case_headings}, + {"help", &&case_help}, + {"info", &&case_info}, + {"lines", &&case_lines}, + {"no-header", &&case_no_header}, + {"no-headers", &&case_no_headers}, + {"no-heading", &&case_no_heading}, + {"no-headings", &&case_no_headings}, + {"noheader", &&case_noheader}, + {"noheaders", &&case_noheaders}, + {"noheading", &&case_noheading}, + {"noheadings", &&case_noheadings}, + {"pid", &&case_pid}, + {"rows", &&case_rows}, + {"sid", &&case_sid}, + {"sort", &&case_sort}, + {"tty", &&case_tty}, + {"user", &&case_user}, /* euid */ + {"version", &&case_version}, + {"width", &&case_width}, + }; + const int gnu_table_count = sizeof(gnu_table)/sizeof(gnu_table_struct); + + s = ps_argv[thisarg]+2; + sl = strcspn(s,":="); + if(sl > 15) return "Unknown gnu long option."; + strncpy(buf, s, sl); + buf[sl] = '\0'; + flagptr = s+sl; + + found = bsearch(&findme, gnu_table, gnu_table_count, + sizeof(gnu_table_struct), compare_gnu_table_structs + ); + + if(!found) return "Unknown gnu long option."; + + goto *(found->jump); /* See gcc extension info. :-) */ + + case_Group: + trace("--Group\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of real groups must follow --Group."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_RGID; + return NULL; + case_User: + trace("--User\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of real users must follow --User."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_RUID; + return NULL; + case_cols: + case_width: + case_columns: + trace("--cols\n"); + arg = grab_gnu_arg(); + if(arg && *arg){ + long t; + char *endptr; + t = strtol(arg, &endptr, 0); + if(!*endptr && (t>0) && (t<2000000000)){ + screen_cols = (int)t; + return NULL; + } + } + return "Number of columns must follow --cols, --width, or --columns."; + case_cumulative: + trace("--cumulative\n"); + if(s[sl]) return "Option --cumulative does not take an argument."; + include_dead_children = 1; + return NULL; + case_deselect: + trace("--deselect\n"); + if(s[sl]) return "Option --deselect does not take an argument."; + negate_selection = 1; + return NULL; + case_no_header: + case_no_headers: + case_no_heading: + case_no_headings: + case_noheader: + case_noheaders: + case_noheading: + case_noheadings: + trace("--noheaders\n"); + if(s[sl]) return "Option --no-heading does not take an argument."; + if(header_type) return "Only one heading option may be specified."; + header_type = HEAD_NONE; + return NULL; + case_header: + case_headers: + case_heading: + case_headings: + trace("--headers\n"); + if(s[sl]) return "Option --heading does not take an argument."; + if(header_type) return "Only one heading option may be specified."; + header_type = HEAD_MULTI; + return NULL; + case_forest: + trace("--forest\n"); + if(s[sl]) return "Option --forest does not take an argument."; + forest_type = 'g'; + return NULL; + case_format: + trace("--format\n"); + arg=grab_gnu_arg(); + if(!arg) return "Format specification must follow --format."; + defer_sf_option(arg, SF_G_format); + return NULL; + case_group: + trace("--group\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of effective groups must follow --group."; + err=parse_list(arg, parse_gid); + if(err) return err; + selection_list->typecode = SEL_EGID; + return NULL; + case_help: + trace("--help\n"); + exclusive("--help"); + fputs(help_message, stderr); + exit(0); + return NULL; + case_info: + trace("--info\n"); + exclusive("--info"); + self_info(); + exit(0); + return NULL; + case_pid: + trace("--pid\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of process IDs must follow --pid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_PID; + return NULL; + case_rows: + case_lines: + trace("--rows\n"); + arg = grab_gnu_arg(); + if(arg && *arg){ + long t; + char *endptr; + t = strtol(arg, &endptr, 0); + if(!*endptr && (t>0) && (t<2000000000)){ + screen_rows = (int)t; + return NULL; + } + } + return "Number of rows must follow --rows or --lines."; + case_sid: + trace("--sid\n"); + arg = grab_gnu_arg(); + if(!arg) return "Some sid thing(s) must follow --sid."; + err=parse_list(arg, parse_pid); + if(err) return err; + selection_list->typecode = SEL_SESS; + return NULL; + case_sort: + trace("--sort\n"); + arg=grab_gnu_arg(); + if(!arg) return "Long sort specification must follow --sort."; + defer_sf_option(arg, SF_G_sort); + return NULL; + case_tty: + trace("--tty\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of ttys must follow --tty."; + err=parse_list(arg, parse_tty); + if(err) return err; + selection_list->typecode = SEL_TTY; + return NULL; + case_user: + trace("--user\n"); + arg = grab_gnu_arg(); + if(!arg) return "List of effective users must follow --user."; + err=parse_list(arg, parse_uid); + if(err) return err; + selection_list->typecode = SEL_EUID; + return NULL; + case_version: + trace("--version\n"); + exclusive("--version"); + display_version(); + exit(0); + return NULL; +} + +/*************** process trailing PIDs **********************/ +static const char *parse_trailing_pids(void){ + selection_node *pidnode; /* pid */ + selection_node *grpnode; /* process group */ + selection_node *sidnode; /* session */ + char **argp; /* pointer to pointer to text of PID */ + const char *err; /* error code that could or did happen */ + int i; + + i = ps_argc - thisarg; /* how many trailing PIDs, SIDs, PGRPs?? */ + argp = ps_argv + thisarg; + thisarg = ps_argc - 1; /* we must be at the end now */ + + pidnode = malloc(sizeof(selection_node)); + pidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + pidnode->n = 0; + + grpnode = malloc(sizeof(selection_node)); + grpnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + grpnode->n = 0; + + sidnode = malloc(sizeof(selection_node)); + sidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */ + sidnode->n = 0; + + while(i--){ + char *data; + data = *(argp++); + switch(*data){ + default: err = parse_pid( data, pidnode->u + pidnode->n++); break; + case '-': err = parse_pid(++data, grpnode->u + grpnode->n++); break; + case '+': err = parse_pid(++data, sidnode->u + sidnode->n++); break; + } + if(err) return err; /* the node gets freed with the list */ + } + + if(pidnode->n){ + pidnode->next = selection_list; + selection_list = pidnode; + selection_list->typecode = SEL_PID; + } /* else free both parts */ + + if(grpnode->n){ + grpnode->next = selection_list; + selection_list = grpnode; + selection_list->typecode = SEL_PGRP; + } /* else free both parts */ + + if(sidnode->n){ + sidnode->next = selection_list; + selection_list = sidnode; + selection_list->typecode = SEL_SESS; + } /* else free both parts */ + + return NULL; +} + +/************** misc stuff ***********/ + +static void reset_parser(void){ + w_count = 0; +} + +static int arg_type(const char *str){ + int tmp = str[0]; + if((tmp>='a') && (tmp<='z')) return ARG_BSD; + if((tmp>='A') && (tmp<='Z')) return ARG_BSD; + if((tmp>='0') && (tmp<='9')) return ARG_PID; + if(tmp=='+') return ARG_SESS; + if(tmp!='-') return ARG_FAIL; + tmp = str[1]; + if((tmp>='a') && (tmp<='z')) return ARG_SYSV; + if((tmp>='A') && (tmp<='Z')) return ARG_SYSV; + if((tmp>='0') && (tmp<='9')) return ARG_PGRP; + if(tmp!='-') return ARG_FAIL; + tmp = str[2]; + if((tmp>='a') && (tmp<='z')) return ARG_GNU; + if((tmp>='A') && (tmp<='Z')) return ARG_GNU; + if(tmp=='\0') return ARG_END; + return ARG_FAIL; +} + +/* First assume sysv, because that is the POSIX and Unix98 standard. */ +static const char *parse_all_options(void){ + const char *err = NULL; + int at; + while(++thisarg < ps_argc){ + trace("parse_all_options calling arg_type for \"%s\"\n", ps_argv[thisarg]); + at = arg_type(ps_argv[thisarg]); + trace("ps_argv[thisarg] is %s\n", ps_argv[thisarg]); + if(at != ARG_SYSV) not_pure_unix = 1; + switch(at){ + case ARG_GNU: + err = parse_gnu_option(); + break; + case ARG_SYSV: + if(!force_bsd){ /* else go past case ARG_BSD */ + err = parse_sysv_option(); + break; + case ARG_BSD: + if(force_bsd && !(personality & PER_FORCE_BSD)) return "way bad"; + } + prefer_bsd_defaults = 1; + err = parse_bsd_option(); + break; + case ARG_PGRP: + case ARG_SESS: + case ARG_PID: + prefer_bsd_defaults = 1; + err = parse_trailing_pids(); + break; + case ARG_END: + case ARG_FAIL: + trace(" FAIL/END on [%s]\n",ps_argv[thisarg]); + return "Garbage option."; + break; + default: + printf(" ? %s\n",ps_argv[thisarg]); + return "Something broke."; + } /* switch */ + if(err) return err; + } /* while */ + return NULL; +} + +static void choose_dimensions(void){ + if(w_count && (screen_cols<132)) screen_cols=132; + if(w_count>1) screen_cols=OUTBUF_SIZE; + /* perhaps --html and --null should set unlimited width */ +} + +int arg_parse(int argc, char *argv[]){ + const char *err = NULL; + const char *err2 = NULL; + ps_argc = argc; + ps_argv = argv; + thisarg = 0; + +#if 0 + {int debugloop = 0; while(debugloop are dead processes (so-called "zombies") that +remain because their parent has not destroyed them properly. These processes +will be destroyed by init(8) if the parent process exits. + + +PROCESS FLAGS + +ALIGNWARN 001 print alignment warning msgs +STARTING 002 being created +EXITING 004 getting shut down +PTRACED 010 set if ptrace (0) has been called +TRACESYS 020 tracing system calls +FORKNOEXEC 040 forked but didn't exec +SUPERPRIV 100 used super-user privileges +DUMPCORE 200 dumped core +SIGNALED 400 killed by a signal + + +PROCESS STATE CODES + +D uninterruptible sleep (usually IO) +R runnable (on run queue) +S sleeping +T traced or stopped +Z a defunct ("zombie") process + +For BSD formats and when the "stat" keyword is used, additional +letters may be displayed: + +W has no resident pages +< high-priority process +N low-priority task +L has pages locked into memory (for real-time and custom IO) + + +SORT KEYS + +Note that the values used in sorting are the internal values ps uses and not +the `cooked' values used in some of the output format fields. Pipe ps +output into the sort(1) command if you want to sort the cooked values. + +KEY LONG DESCRIPTION +c cmd simple name of executable +C cmdline full command line +f flags flags as in long format F field +g pgrp process group ID +G tpgid controlling tty process group ID +j cutime cumulative user time +J cstime cumulative system time +k utime user time +K stime system time +m min_flt number of minor page faults +M maj_flt number of major page faults +n cmin_flt cumulative minor page faults +N cmaj_flt cumulative major page faults +o session session ID +p pid process ID +P ppid parent process ID +r rss resident set size +R resident resident pages +s size memory size in kilobytes +S share amount of shared pages +t tty the minor device number of tty +T start_time time process was started +U uid user ID number +u user user name +v vsize total VM size in kB +y priority kernel scheduling priority + + +AIX FORMAT DESCRIPTORS + +This ps supports AIX format descriptors, which work somewhat like the +formatting codes of printf(1) and printf(3). For example, the normal +default output can be produced with this: ps -eo "%p %y %x %c" + +CODE NORMAL HEADER +%C pcpu %CPU +%G group GROUP +%P ppid PPID +%U user USER +%a args COMMAND +%c comm COMMAND +%g rgroup RGROUP +%n nice NI +%p pid PID +%r pgid PGID +%t etime ELAPSED +%u ruser RUSER +%x time TIME +%y tty TTY +%z vsz VSZ + + +STANDARD FORMAT SPECIFIERS + +These may be used to control both output format and sorting. +For example: ps -eo pid,user,args --sort user + +CODE HEADER +%cpu %CPU +%mem %MEM +alarm ALARM +args COMMAND +blocked BLOCKED +bsdstart START +bsdtime TIME +c C +caught CAUGHT +cmd CMD +comm COMMAND +command COMMAND +cputime TIME +drs DRS +dsiz DSIZ +egid EGID +egroup EGROUP +eip EIP +esp ESP +etime ELAPSED +euid EUID +euser EUSER +f F +fgid FGID +fgroup FGROUP +flag F +flags F +fname COMMAND +fsgid FSGID +fsgroup FSGROUP +fsuid FSUID +fsuser FSUSER +fuid FUID +fuser FUSER +gid GID +group GROUP +ignored IGNORED +intpri PRI +lim LIM +longtname TTY +lstart STARTED +m_drs DRS +m_trs TRS +maj_flt MAJFL +majflt MAJFLT +min_flt MINFL +minflt MINFLT +ni NI +nice NI +nwchan WCHAN +opri PRI +pagein PAGEIN +pcpu %CPU +pending PENDING +pgid PGID +pgrp PGRP +pid PID +pmem %MEM +ppid PPID +pri PRI +priority PRI +rgid RGID +rgroup RGROUP +rss RSS +rssize RSS +rsz RSZ +ruid RUID +ruser RUSER +s S +sess SESS +session SESS +sgi_p P +sgi_rss RSS +sgid SGID +sgroup SGROUP +sid SID +sig PENDING +sig_block BLOCKED +sig_catch CATCHED +sig_ignore IGNORED +sig_pend SIGNAL +sigcatch CAUGHT +sigignore IGNORED +sigmask BLOCKED +stackp STACKP +start STARTED +start_stack STACKP +start_time START +stat STAT +state S +stime STIME +suid SUID +suser SUSER +svgid SVGID +svgroup SVGROUP +svuid SVUID +svuser SVUSER +sz SZ +time TIME +timeout TMOUT +tmout TMOUT +tname TTY +tpgid TPGID +trs TRS +trss TRSS +tsiz TSIZ +tt TT +tty TT +tty4 TTY +tty8 TTY +ucmd CMD +ucomm COMMAND +uid UID +uid_hack UID +uname USER +user USER +vsize VSZ +vsz VSZ +wchan WCHAN + + + + +ENVIRONMENT VARIABLES +The following environment variables could affect ps: + COLUMNS Override default display width. + LINES Override default display height. + PS_PERSONALITY Set to one of posix,old,linux,bsd,sun,digital... + CMD_ENV Set to one of posix,old,linux,bsd,sun,digital... + I_WANT_A_BROKEN_PS Force obsolete command line interpretation. + LC_TIME Date format. + PS_COLORS Not currently supported. + PS_FORMAT Default output format override. + PS_SYSMAP Default namelist (System.map) location. + PS_SYSTEM_MAP Default namelist (System.map) location. + POSIXLY_CORRECT Don't find excuses to ignore bad "features". + UNIX95 Don't find excuses to ignore bad "features". + _XPG Cancel CMD_ENV=irix non-standard behavior. + +In general, it is a bad idea to set these variables. The one exception +is CMD_ENV or PS_PERSONALITY, which could be set to Linux for normal +systems. Without that setting, ps follows the useless and bad parts +of the Unix98 standard. + + +PERSONALITY + 390 like the S/390 OpenEdition ps + aix like AIX ps + bsd like FreeBSD ps (totally non-standard) + compaq like Digital Unix ps + debian like the old Debian ps + digital like Digital Unix ps + gnu like the old Debian ps + hp like HP-UX ps + hpux like HP-UX ps + irix like Irix ps + linux ***** RECOMMENDED ***** + old like the original Linux ps (totally non-standard) + posix standard + sco like SCO ps + sgi like Irix ps + sun like SunOS 4 ps (totally non-standard) + sunos like SunOS 4 ps (totally non-standard) + sysv standard + unix standard + unix95 standard + unix98 standard + + +EXAMPLES +To see every process on the system using standard syntax: + ps -e +To see every process on the system using BSD syntax: + ps ax +To see every process except those running as root (real & effective ID) + ps -U root -u root -N +To see every process with a user-defined format: + ps -eo pid,tt,user,fname,tmout,f,wchan +Odd display with AIX field descriptors: + ps -o "%u : %U : %p : %a" +Print only the process IDs of syslogd: + ps -C syslogd -o pid= + +SEE ALSO +top(1) pstree(1) proc(5) + +STANDARDS +This ps conforms to version 2 of the Single Unix Specification. + +AUTHOR +ps was originally written by Branko Lankester . Michael +K. Johnson re-wrote it significantly to use the proc +filesystem, changing a few things in the process. Michael Shields + added the pid-list feature. Charles Blake + added multi-level sorting, the dirent-style library, the +device name-to-number mmaped database, the approximate binary search +directly on System.map, and many code and documentation cleanups. David +Mossberger-Tang wrote the generic BFD support for psupdate. Albert Cahalan + rewrote ps for full Unix98 and BSD support, along with +some ugly hacks for obsolete and foreign syntax. + +Please send bug reports to diff --git a/ps/regression b/ps/regression new file mode 100644 index 00000000..c71c826a --- /dev/null +++ b/ps/regression @@ -0,0 +1,26 @@ +-u 500 -o pid,ppid,fname,comm,args # right margin trouble +-u 500 -o pid,ppid,fname,comm,args,wchan,wchan,wchan,wchan,wchan,nice,wchan +-u 500 -o pid,pid,pid,pid,user,user,user,args # had trouble +-u 500 -o user,user,user,pid,pid,pid,pid,args # no trouble! + +Test with each type of field (RIGHT,LEFT,UNLIMITED...) hanging off the +edge of the screen and each type of field to the left of the one that +hangs off the edge. + +Test "ps ef" as _both_ normal user and root. Especially after su! + +On a 108-col screen, try "ps alx" and "ps alx | cat" + +These ought to be the same: +CMD_ENV=old ps -m +CMD_ENV=old ps m + +These ought to be the same: +CMD_ENV=old ps -X +CMD_ENV=old ps X +ps X +ps -X # needs to be a non-SysV option + +This should fail: +ps x -x + diff --git a/ps/select.c b/ps/select.c new file mode 100644 index 00000000..e6bf8c7f --- /dev/null +++ b/ps/select.c @@ -0,0 +1,146 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include + +#include "common.h" +#include "../proc/readproc.h" +#include "../proc/procps.h" + +#define session_leader(p) ((p)->session == (p)->pid) +#define process_group_leader(p) ((p)->pgid == (p)->pid) +#define without_a_tty(p) ((unsigned short)((p)->tty) == (unsigned short)-1) +#define some_other_user(p) ((p)->euid != cached_euid) +#define running(p) (((p)->state=='R')||((p)->state=='D')) +#define has_our_euid(p) ((unsigned short)((p)->euid) == (unsigned short)cached_euid) +#define on_our_tty(p) ((unsigned short)((p)->tty) == (unsigned short)cached_tty) + +static unsigned long select_bits = 0; + +/***** prepare select_bits for use */ +const char *select_bits_setup(void){ + int switch_val = 0; + /* don't want a 'g' screwing up simple_select */ + if(!simple_select && !prefer_bsd_defaults){ + select_bits = 0xaa00; /* the STANDARD selection */ + return NULL; + } + /* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */ + if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) ) + switch_val = simple_select|SS_B_g; + else + switch_val = simple_select; + switch(switch_val){ + /* UNIX options */ + case SS_U_a | SS_U_d: select_bits = 0x3f3f; break; /* 3333 or 3f3f */ + case SS_U_a: select_bits = 0x0303; break; /* 0303 or 0f0f */ + case SS_U_d: select_bits = 0x3333; break; + /* SunOS 4 only (others have 'g' enabled all the time) */ + case 0: select_bits = 0x0202; break; + case SS_B_a: select_bits = 0x0303; break; + case SS_B_x : select_bits = 0x2222; break; + case SS_B_x | SS_B_a: select_bits = 0x3333; break; + /* General BSD options */ + case SS_B_g : select_bits = 0x0a0a; break; + case SS_B_g | SS_B_a: select_bits = 0x0f0f; break; + case SS_B_g | SS_B_x : select_bits = 0xaaaa; break; + case SS_B_g | SS_B_x | SS_B_a: /* convert to -e instead of using 0xffff */ + all_processes = 1; + simple_select = 0; + break; + default: + return "Process selection options conflict."; + break; + } + return NULL; +} + +/***** selected by simple option? */ +static int table_accept(proc_t *buf){ + unsigned proc_index; + proc_index = (has_our_euid(buf) <<0) + | (session_leader(buf) <<1) + | (without_a_tty(buf) <<2) + | (on_our_tty(buf) <<3); + return (select_bits & (1<typecode){ + default: + printf("Internal error in ps! Please report this bug.\n"); + +#define return_if_match(foo,bar) \ + i=sn->n; while(i--) \ + if((unsigned short )(buf->foo) == (unsigned short)(*(sn->u+i)).bar) \ + return 1 + + break; case SEL_RUID: return_if_match(ruid,uid); + break; case SEL_EUID: return_if_match(euid,uid); + break; case SEL_SUID: return_if_match(suid,uid); + break; case SEL_FUID: return_if_match(fuid,uid); + + break; case SEL_RGID: return_if_match(rgid,gid); + break; case SEL_EGID: return_if_match(egid,gid); + break; case SEL_SGID: return_if_match(sgid,gid); + break; case SEL_FGID: return_if_match(fgid,gid); + + break; case SEL_PGRP: return_if_match(pgrp,pid); + break; case SEL_PID : return_if_match(pid,pid); + break; case SEL_TTY : return_if_match(tty,tty); + break; case SEL_SESS: return_if_match(session,pid); + + /* TODO Should use a long long cast for performance */ + break; case SEL_COMM: i=sn->n; while(i--) + if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 8 )) return 1; + + + +#undef return_if_match + + } + sn = sn->next; + } + return 0; +} + + +/***** This must satisfy Unix98 and as much BSD as possible */ +int want_this_proc(proc_t *buf){ + int accepted_proc = 1; /* assume success */ + /* elsewhere, convert T to list, U sets x implicitly */ + + /* handle -e -A */ + if(all_processes) goto finish; + + /* use table for -a a d g x */ + if((simple_select || !selection_list)) + if(table_accept(buf)) goto finish; + + /* search lists */ + if(proc_was_listed(buf)) goto finish; + + /* fail, fall through to loose ends */ + accepted_proc = 0; + + /* do r N */ +finish: + if(running_only && !running(buf)) accepted_proc = 0; + if(negate_selection) return !accepted_proc; + return accepted_proc; +} diff --git a/ps/sortformat.c b/ps/sortformat.c new file mode 100644 index 00000000..9c08d9a1 --- /dev/null +++ b/ps/sortformat.c @@ -0,0 +1,875 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ + +#include +#include +#include + +/* username lookups */ +#include +#include +#include + +#include "../proc/readproc.h" +#include "common.h" + +static sf_node *sf_list = NULL; /* deferred sorting and formatting */ +static int broken; /* use gross Unix98 parsing? */ +static int have_gnu_sort = 0; /* if true, "O" must be format */ +static int already_parsed_sort = 0; /* redundantly set in & out of fn */ +static int already_parsed_format = 0; + + +#define parse_sort_opt <-- arrgh! do not use this --> +#define gnusort_parse <-- arrgh! do not use this --> + + +/**************** Parse single format specifier *******************/ +static format_node *do_one_spec(const char *spec, const char *override){ + const format_struct *fs; + const macro_struct *ms; + + fs = search_format_array(spec); + if(fs){ + int w1, w2; + format_node *thisnode; + thisnode = malloc(sizeof(format_node)); + w1 = fs->width; + if(override){ + w2 = strlen(override); + thisnode->width = (w1>w2)?w1:w2; + thisnode->name = malloc(strlen(override)+1); + strcpy(thisnode->name, override); + }else{ + thisnode->width = w1; + thisnode->name = malloc(strlen(fs->head)+1); + strcpy(thisnode->name, fs->head); + } + thisnode->pr = fs->pr; + thisnode->pad = fs->pad; + thisnode->vendor = fs->vendor; + thisnode->flags = fs->flags; + thisnode->next = NULL; + return thisnode; + } + + /* That failed, so try it as a macro. */ + ms = search_macro_array(spec); + if(ms){ + format_node *list = NULL; + format_node *newnode; + const char *walk; + int dist; + char buf[16]; /* trust strings will be short (from above, not user) */ + walk = ms->head; + while(*walk){ + dist = strcspn(walk, ", "); + strncpy(buf,walk,dist); + buf[dist] = '\0'; + newnode = do_one_spec(buf,override); /* call self, assume success */ + newnode->next = list; + list = newnode; + walk += dist; + if(*walk) walk++; + } + return list; + } + return NULL; /* bad, spec not found */ +} + + +/************ must wrap user format in default *************/ +static void O_wrap(sf_node *sfn, int otype){ + format_node *fnode; + format_node *endp; + char *trailer; + + trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ; + + fnode = do_one_spec("pid",NULL); + if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n"); + endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */ + endp->next = fnode; + + fnode = do_one_spec(trailer,NULL); + if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n"); + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; +} + +/****************************************************************** + * Used to parse option AIX field descriptors. + * Put each completed format_node onto the list starting at ->f_cooked + */ +static const char *aix_format_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *walk; + int items; + + /*** sanity check and count items ***/ + items = 0; + walk = sfn->sf; + /* state machine */ { + int c; + initial: + c = *walk++; + if(c=='%') goto get_desc; + if(!c) goto looks_ok; + /* get_text: */ + items++; + get_more_text: + c = *walk++; + if(c=='%') goto get_desc; + if(c) goto get_more_text; + goto looks_ok; + get_desc: + items++; + c = *walk++; + if(c) goto initial; + return "Improper AIX field descriptor."; + looks_ok: + } + + /*** sanity check passed ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + walk = sfn->sf; + + while(items--){ + format_node *fnode; /* newly allocated */ + format_node *endp; /* for list manipulation */ + + if(*walk == '%'){ + const aix_struct *aix; + walk++; + if(*walk == '%') goto double_percent; + aix = search_aix_array(*walk); + walk++; + if(!aix){ + free(buf); + return "Unknown AIX field descriptor."; + } + fnode = do_one_spec(aix->spec, aix->head); + if(!fnode){ + free(buf); + return "AIX field descriptor processing bug."; + } + } else { + int len; + len = strcspn(walk, "%"); + memcpy(buf,walk,len); + if(0){ +double_percent: + len = 1; + buf[0] = '%'; + } + buf[len] = '\0'; + walk += len; + fnode = malloc(sizeof(format_node)); + fnode->width = len; + fnode->name = malloc(len+1); + strcpy(fnode->name, buf); + fnode->pr = NULL; /* checked for */ + fnode->pad = 0; + fnode->vendor = AIX; + fnode->flags = 0; + fnode->next = NULL; + } + + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; + } + free(buf); + already_parsed_format = 1; + return NULL; +} + +/*************************************************************** + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + * Put each completed format_node onto the list starting at ->f_cooked + */ +static const char *format_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t,\n" */ + char *walk; + const char *err; /* error code that could or did happen */ + format_node *fnode; + int items; + int need_item; + static char errbuf[80]; /* for variable-text error message */ + + /*** prepare to operate ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\n': case '\0': + /* Linux extension: allow \t and \n as delimiters */ + if(need_item){ + free(buf); + goto improper; + } + need_item=1; + break; + case '=': + if(broken) goto out; + /* fall through */ + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); +out: + if(!items){ + free(buf); + goto empty; + } +#ifdef STRICT_LIST + if(need_item){ /* can't have trailing deliminator */ + free(buf); + goto improper; + } +#else + if(need_item){ /* allow 1 trailing deliminator */ + *--walk='\0'; /* remove the trailing deliminator */ + } +#endif + /*** actually parse the list ***/ + walk = buf; + while(items--){ + format_node *endp; + char *equal_loc; + sep_loc = strpbrk(walk," ,\t\n"); + /* if items left, then sep_loc is not in header override */ + if(items && sep_loc) *sep_loc = '\0'; + equal_loc = strpbrk(walk,"="); + if(equal_loc){ /* if header override */ + *equal_loc = '\0'; + equal_loc++; + } + fnode = do_one_spec(walk,equal_loc); + if(!fnode){ + if(!*errbuf){ /* if didn't already create an error string */ + snprintf( + errbuf, + sizeof(errbuf), + "Unknown user-defined format specifier \"%s\".", + walk + ); + } + free(buf); + goto unknown; + } + endp = fnode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->f_cooked; + sfn->f_cooked = fnode; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + already_parsed_format = 1; + return NULL; + + /* errors may cause a retry looking for AIX format codes */ + if(0) unknown: err=errbuf; + if(0) empty: err="Empty format list."; + if(0) improper: err="Improper format list."; + if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn); + return err; +} + +/**************** Parse single sort specifier *******************/ +static sort_node *do_one_sort_spec(const char *spec){ + const format_struct *fs; + int reverse = 0; + if(*spec == '-'){ + reverse = 1; + spec++; + } + fs = search_format_array(spec); + if(fs){ + sort_node *thisnode; + thisnode = malloc(sizeof(format_node)); + thisnode->sr = fs->sr; + thisnode->reverse = reverse; + thisnode->next = NULL; + return thisnode; + } + return NULL; /* bad, spec not found */ +} + + +/************************************************************** + * Used to parse long sorting options. + * Put each completed sort_node onto the list starting at ->s_cooked + */ +static const char *long_sort_parse(sf_node *sfn){ + char *buf; /* temp copy of arg to hack on */ + char *sep_loc; /* separator location: " \t,\n" */ + char *walk; + const char *err; /* error code that could or did happen */ + sort_node *snode; + int items; + int need_item; + + /*** prepare to operate ***/ + buf = malloc(strlen(sfn->sf)+1); + strcpy(buf, sfn->sf); + + /*** sanity check and count items ***/ + need_item = 1; /* true */ + items = 0; + walk = buf; + err = "Improper sort specifier list."; + do{ + switch(*walk){ + case ' ': case ',': case '\t': case '\n': case '\0': + if(need_item){ + free(buf); + return "Improper sort list"; + } + need_item=1; + break; + default: + if(need_item) items++; + need_item=0; + } + } while (*++walk); + if(!items){ + free(buf); + return "Empty sort list."; + } +#ifdef STRICT_LIST + if(need_item){ /* can't have trailing deliminator */ + free(buf); + return "Improper sort list."; + } +#else + if(need_item){ /* allow 1 trailing deliminator */ + *--walk='\0'; /* remove the trailing deliminator */ + } +#endif + /*** actually parse the list ***/ + walk = buf; + while(items--){ + sort_node *endp; + sep_loc = strpbrk(walk," ,\t\n"); + if(sep_loc) *sep_loc = '\0'; + snode = do_one_sort_spec(walk); + if(!snode){ + free(buf); + return "Unknown sort specifier."; + } + endp = snode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->s_cooked; + sfn->s_cooked = snode; + walk = sep_loc + 1; /* point to next item, if any */ + } + free(buf); + already_parsed_sort = 1; + return NULL; +} + + + + + + +/************ pre-parse short sorting option *************/ +/* Errors _must_ be detected so that the "O" option can try to + * reparse as formatting codes. + */ +static const char *verify_short_sort(const char *arg){ + const char *all = "CGJKMNPRSTUcfgjkmnoprstuvy+-"; + char checkoff[256]; + int i; + const char *walk; + int tmp; + if(strspn(arg,all) != strlen(arg)) return "Bad sorting code."; + for(i=256; i--;) checkoff[i] = 0; + walk = arg; + for(;;){ + tmp = *walk; + switch(tmp){ + case '\0': + return NULL; /* looks good */ + case '+': + case '-': + tmp = *(walk+1); + if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code."; + break; + case 'P': + if(forest_type) return "PPID sort and forest output conflict."; + /* fall through */ + default: + if(checkoff[tmp]) return "Bad sorting code."; /* repeated */ + /* ought to check against already accepted sort options */ + checkoff[tmp] = 1; + break; + } + walk++; + } +} + + + +/************ parse short sorting option *************/ +static const char *short_sort_parse(sf_node *sfn){ + int direction = 0; + const char *walk; + int tmp; + sort_node *snode; + sort_node *endp; + const struct shortsort_struct *ss; + walk = sfn->sf; + for(;;){ + tmp = *walk; + switch(tmp){ + case '\0': + already_parsed_sort = 1; + return NULL; + case '+': + direction = 0; + break; + case '-': + direction = 1; + break; + default: + ss = search_shortsort_array(tmp); + if(!ss) return "Unknown sort specifier."; + snode = do_one_sort_spec(ss->spec); + if(!snode) return "Unknown sort specifier."; + snode->reverse = direction; + endp = snode; while(endp->next) endp = endp->next; /* find end */ + endp->next = sfn->s_cooked; + sfn->s_cooked = snode; + direction = 0; + break; + } + walk++; + } +} + +/******************* high-level below here *********************/ + + +/* + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + * Recursion is to preserve original order. + */ +static const char *parse_O_option(sf_node *sfn){ + const char *err; /* error code that could or did happen */ + + if(sfn->next){ + err = parse_O_option(sfn->next); + if(err) return err; + } + + switch(sfn->sf_code){ + case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/ + err = format_parse(sfn); + if(!err) already_parsed_format = 1; + break; + case SF_U_O: /*** format ***/ + /* Can have -l -f f u... set already_parsed_format like DEC does */ + if(already_parsed_format) return "option -O can not follow other format options."; + err = format_parse(sfn); + if(err) return err; + already_parsed_format = 1; + O_wrap(sfn,'u'); /* must wrap user format in default */ + break; + case SF_B_O: /*** both ***/ + if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options."; + else err = verify_short_sort(sfn->sf); + if(!err){ /* success as sorting code */ + short_sort_parse(sfn); + already_parsed_sort = 1; + return NULL; + } + if(already_parsed_format){ + err = "option O is neither first format nor sort order."; + break; + } + if(!format_parse(sfn)){ /* if success as format code */ + already_parsed_format = 1; + O_wrap(sfn,'b'); /* must wrap user format in default */ + return NULL; + } + break; + case SF_G_sort: case SF_B_m: /*** sort ***/ + if(already_parsed_sort) err = "Multiple sort options."; + else err = long_sort_parse(sfn); + already_parsed_sort = 1; + break; + default: /*** junk ***/ + return "Bug: parse_O_option got weirdness!"; + } + return err; /* could be NULL */ +} + + +/************ Main parser calls this to save lists for later **********/ +/* store data for later and return 1 if arg looks non-standard */ +int defer_sf_option(const char *arg, int source){ + sf_node *sfn; + char buf[16]; + int dist; + const format_struct *fs; + int need_item = 1; + + sfn = malloc(sizeof(sf_node)); + sfn->sf = malloc(strlen(arg)+1); + strcpy(sfn->sf, arg); + sfn->sf_code = source; + sfn->s_cooked = NULL; + sfn->f_cooked = NULL; + sfn->next = sf_list; + sf_list = sfn; + + if(source == SF_G_sort) have_gnu_sort = 1; + + /* Now try to find an excuse to ignore broken Unix98 parsing. */ + if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */ + do{ + switch(*arg){ + case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */ + if(need_item) return 1; /* something wrong */ + need_item=1; + break; + case '=': + if(need_item) return 1; /* something wrong */ + return 0; /* broken Unix98 parsing is required */ + default: + if(!need_item) break; + need_item=0; + dist = strcspn(arg,", ="); + if(dist>15) return 1; /* something wrong, sort maybe? */ + strncpy(buf,arg,dist); /* no '\0' on end */ + buf[dist] = '\0'; /* fix that problem */ + fs = search_format_array(buf); + if(!fs) return 1; /* invalid spec, macro or sort maybe? */ + if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */ + } + } while (*++arg); + + return 0; /* boring, Unix98 is no change */ +} + +/***** Since ps is not long-lived, the memory leak can be ignored. ******/ +void reset_sortformat(void){ + sf_list = NULL; /* deferred sorting and formatting */ + format_list = NULL; /* digested formatting options */ + sort_list = NULL; /* digested sorting options (redundant?) */ + have_gnu_sort = 0; + already_parsed_sort = 0; + already_parsed_format = 0; +} + + +/***** Search format_list for findme, then insert putme after findme. ****/ +static int fmt_add_after(const char *findme, format_node *putme){ + format_node *walk; + if(!strcmp(format_list->name, findme)){ + putme->next = format_list->next; + format_list->next = putme; + return 1; /* success */ + } + walk = format_list; + while(walk->next){ + if(!strcmp(walk->next->name, findme)){ + putme->next = walk->next->next; + walk->next->next = putme; + return 1; /* success */ + } + walk = walk->next; + } + return 0; /* fail */ +} + +/******* Search format_list for findme, then delete it. ********/ +static int fmt_delete(const char *findme){ + format_node *walk; + format_node *old; + if(!strcmp(format_list->name, findme)){ + old = format_list; + format_list = format_list->next; + free(old); + return 1; /* success */ + } + walk = format_list; + while(walk->next){ + if(!strcmp(walk->next->name, findme)){ + old = walk->next; + walk->next = walk->next->next; + free(old); + return 1; /* success */ + } + walk = walk->next; + } + return 0; /* fail */ +} + + +/************ Build a SysV format backwards. ***********/ +#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn) +static const char *generate_sysv_list(void){ + format_node *fn; + if((format_modifiers & FM_y) && !(format_flags & FF_Ul)) + return "Modifier -y without format -l makes no sense."; + if(prefer_bsd_defaults){ + if(format_flags) PUSH("cmd"); + else PUSH("args"); + PUSH("bsdtime"); + if(!(format_flags & FF_Ul)) PUSH("stat"); + }else{ + if(format_flags & FF_Uf) PUSH("cmd"); + else PUSH("ucmd"); + PUSH("time"); + } + PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */ + if(format_flags & FF_Uf) PUSH("stime"); + /* avoid duplicate columns from -FP and -Fly */ + if(format_modifiers & FM_F){ + /* if -FP take the Sun-style column instead (sorry about "sgi_p") */ + if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */ + /* if -Fly take the ADDR-replacement RSS instead */ + if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss"); + } + if(format_flags & FF_Ul){ + PUSH("wchan"); + } + /* since FM_y adds RSS anyway, don't do this hack when that is true */ + if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){ + if(personality & PER_IRIX_l){ /* add "rss" then ':' here */ + PUSH("sgi_rss"); + fn = malloc(sizeof(format_node)); + fn->width = 1; + fn->name = malloc(2); + strcpy(fn->name, ":"); + fn->pr = NULL; /* checked for */ + fn->pad = 0; + fn->vendor = AIX; /* yes, for SGI weirdness */ + fn->flags = 0; + fn->next = format_list; + format_list=fn; + } + } + if((format_modifiers & FM_F) || (format_flags & FF_Ul)){ + PUSH("sz"); + } + if(format_flags & FF_Ul){ + if(format_modifiers & FM_y) PUSH("rss"); + else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p"); + else PUSH("addr_1"); + } + if(format_modifiers & FM_c){ + PUSH("pri"); PUSH("class"); + }else if(format_flags & FF_Ul){ + PUSH("ni"); PUSH("opri"); + } + if((format_modifiers & FM_L) && (format_flags & FF_Uf)) PUSH("nlwp"); + if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c"); + if(format_modifiers & FM_P) PUSH("psr"); + if(format_modifiers & FM_L) PUSH("lwp"); + if(format_modifiers & FM_j){ + PUSH("sid"); + PUSH("pgid"); + } + if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid"); + if(format_modifiers & FM_T) PUSH("spid"); + PUSH("pid"); + if(format_flags & FF_Uf){ + if(personality & PER_SANE_USER) PUSH("user"); + else PUSH("uid_hack"); + }else if(format_flags & FF_Ul){ + PUSH("uid"); + } + if(format_flags & FF_Ul){ + PUSH("s"); + if(!(format_modifiers & FM_y)) PUSH("f"); + } + if(format_modifiers & FM_M){ + PUSH("label"); /* Mandatory Access Control */ + } + format_modifiers = 0; + return NULL; +} + + +/************************************************************************** + * Used to parse option O lists. Option O is shared between + * sorting and formatting. Users may expect one or the other. + * The "broken" flag enables a really bad Unix98 misfeature. + */ +const char *process_sf_options(int localbroken){ + const char *err; + sf_node *sf_walk; + int option_source; /* true if user-defined */ + if(personality & PER_BROKEN_o) localbroken = 1; + if(personality & PER_GOOD_o) localbroken = 0; + broken = localbroken; + if(sf_list){ + err = parse_O_option(sf_list); + if(err) return err; + } + + if(format_list) printf("Bug: must reset the list first!\n"); + + /* merge formatting info of sf_list into format_list here */ + sf_walk = sf_list; + while(sf_walk){ + format_node *fmt_walk; + fmt_walk = sf_walk->f_cooked; + sf_walk->f_cooked = NULL; + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + sf_walk = sf_walk->next; + } + + /* merge sorting info of sf_list into sort_list here */ + sf_walk = sf_list; + while(sf_walk){ + sort_node *srt_walk; + srt_walk = sf_walk->s_cooked; + sf_walk->s_cooked = NULL; + while(srt_walk){ /* put any nodes onto sort_list in opposite way */ + sort_node *travler; + travler = srt_walk; + srt_walk = srt_walk->next; + travler->next = sort_list; + sort_list = travler; + } + sf_walk = sf_walk->next; + } + + if(format_list){ + if(format_flags) return "Conflicting format options."; + option_source = 1; + }else{ + format_node *fmt_walk; + format_node *fn; + const char *spec; + switch(format_flags){ + + default: return "Conflicting format options."; + + /* These can be NULL, which enables SysV list generation code. */ + case 0: spec=NULL; break; + case FF_Uf | FF_Ul: spec=sysv_fl_format; break; + case FF_Uf: spec=sysv_f_format; break; + case FF_Ul: spec=sysv_l_format; break; + + /* These are NOT REACHED for normal -j processing. */ + case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */ + case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */ + case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */ + + /* These are true BSD options. */ + case FF_Bj: spec=bsd_j_format; break; + case FF_Bl: spec=bsd_l_format; break; + case FF_Bs: spec=bsd_s_format; break; + case FF_Bu: spec=bsd_u_format; break; + case FF_Bv: spec=bsd_v_format; break; + + /* These are old Linux options. Option m is overloaded. */ + case FF_LX: spec="OL_X"; break; + case FF_Lm: spec="OL_m"; break; + + } /* end switch(format_flags) */ + + option_source = 0; + if(!format_flags && !format_modifiers){ /* was default */ + char *tmp; + tmp = getenv("PS_FORMAT"); /* user override kills default */ + if(tmp && *tmp){ + spec = tmp; + option_source = 2; + } + } + + if(spec){ + fn = do_one_spec(spec, NULL); /* use override "" for no headers */ + fmt_walk = fn; + while(fmt_walk){ /* put any nodes onto format_list in opposite way */ + format_node *travler; + travler = fmt_walk; + fmt_walk = fmt_walk->next; + travler->next = format_list; + format_list = travler; + } + }else{ + err = generate_sysv_list(); + if(err) return err; + option_source = 3; + } + } + if(format_modifiers){ /* generate_sysv_list() may have cleared some bits */ + format_node *fn; + if(option_source) return "Can't use output modifiers with user-defined output"; + if(format_modifiers & FM_j){ + fn = do_one_spec("pgid", NULL); + if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn)) + return "Internal error, no PID or PPID for -j option."; + fn = do_one_spec("sid", NULL); + if(!fmt_add_after("PGID", fn)) return "Lost my PGID!"; + } + if(format_modifiers & FM_y){ + /* TODO: check for failure to do something, and complain if so */ + fmt_delete("F"); + fn = do_one_spec("rss", NULL); + if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR"); + } + if(format_modifiers & FM_c){ + fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C"); + fmt_delete("NI"); + fn = do_one_spec("class", NULL); + if(!fmt_add_after("PRI", fn)) + return "Internal error, no PRI for -c option."; + fmt_delete("PRI"); /* we want a different one */ + fn = do_one_spec("pri", NULL); + if(!fmt_add_after("CLS", fn)) return "Lost my CLS!"; + } + } + if(!option_source){ /* OK to really muck with stuff */ + format_node *fn; + /* Do personality-specific translations not covered by format_flags. + * Generally, these only get hit when personality overrides unix output. + * That (mostly?) means the Digital and Debian personalities. + */ + if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){ + fn = do_one_spec("sgi_p", NULL); + if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR"); + } + if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){ + fn = do_one_spec("user", NULL); + if(fmt_add_after("UID", fn)) fmt_delete("UID"); + } + } + + /* Could scan for duplicates (format and sort) here. Digital does. */ + return NULL; +} + diff --git a/ps/stacktrace.c b/ps/stacktrace.c new file mode 100644 index 00000000..79814bb3 --- /dev/null +++ b/ps/stacktrace.c @@ -0,0 +1,165 @@ +/* + * Gnu debugger stack trace code provided by Peter Mattis + * on Thu, 2 Nov 1995 + * + * Modified for easy use by Albert Cahalan. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define INTERACTIVE 0 +#define STACK_TRACE 1 + +char *stored_prog_name = "You forgot to set \"program\""; +static int stack_trace_done; + +/***********/ +static void debug_stop(char **args){ + execvp (args[0], args); + perror ("exec failed"); + _exit (0); +} + +/***********/ +static void stack_trace_sigchld(int signum){ + stack_trace_done = 1; +} + +/************/ +static void stack_trace(char **args){ + pid_t pid; + int in_fd[2]; + int out_fd[2]; + fd_set fdset; + fd_set readset; + struct timeval tv; + int sel, index, state; + char buffer[256]; + char c; + + stack_trace_done = 0; + signal(SIGCHLD, stack_trace_sigchld); + + if((pipe (in_fd) == -1) || (pipe (out_fd) == -1)){ + perror ("could open pipe"); + _exit (0); + } + + pid = fork (); + if (pid == 0){ + close (0); dup (in_fd[0]); /* set the stdin to the in pipe */ + close (1); dup (out_fd[1]); /* set the stdout to the out pipe */ + close (2); dup (out_fd[1]); /* set the stderr to the out pipe */ + execvp (args[0], args); /* exec gdb */ + perror ("exec failed"); + _exit (0); + } else { + if(pid == (pid_t) -1){ + perror ("could not fork"); + _exit (0); + } + } + + FD_ZERO (&fdset); + FD_SET (out_fd[0], &fdset); + + write (in_fd[1], "backtrace\n", 10); + write (in_fd[1], "p x = 0\n", 8); + write (in_fd[1], "quit\n", 5); + + index = 0; + state = 0; + + for(;;){ + readset = fdset; + tv.tv_sec = 1; + tv.tv_usec = 0; + + sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv); + if (sel == -1) break; + + if((sel > 0) && (FD_ISSET (out_fd[0], &readset))){ + if(read (out_fd[0], &c, 1)){ + switch(state){ + case 0: + if(c == '#'){ + state = 1; + index = 0; + buffer[index++] = c; + } + break; + case 1: + buffer[index++] = c; + if((c == '\n') || (c == '\r')){ + buffer[index] = 0; + fprintf (stderr, "%s", buffer); + state = 0; + index = 0; + } + break; + default: + break; + } + } + } + else if(stack_trace_done) break; + } + + close (in_fd[0]); + close (in_fd[1]); + close (out_fd[0]); + close (out_fd[1]); + _exit (0); +} + +/************/ +void debug(int method, char *prog_name){ + pid_t pid; + char buf[16]; + char *args[4] = { "gdb", NULL, NULL, NULL }; + int x; + + snprintf (buf, 99, "%d", getpid ()); + + args[1] = prog_name; + args[2] = buf; + + pid = fork (); + if(pid == 0){ + switch (method){ + case INTERACTIVE: + fprintf (stderr, "debug_stop\n"); + debug_stop(args); + break; + case STACK_TRACE: + fprintf (stderr, "stack_trace\n"); + stack_trace(args); + break; + } + _exit(0); + } else if(pid == (pid_t) -1){ + perror ("could not fork"); + return; + } + + x = 1; + while(x); /* wait for debugger? */ +} + +/************/ +static void stack_trace_sigsegv(int signum){ + debug(STACK_TRACE, stored_prog_name); +} + +/************/ +void init_stack_trace(char *prog_name){ + stored_prog_name = prog_name; + signal(SIGSEGV, stack_trace_sigsegv); +} diff --git a/skill.1 b/skill.1 new file mode 100644 index 00000000..643cffeb --- /dev/null +++ b/skill.1 @@ -0,0 +1,118 @@ +,\" t +.\" (The preceding line is a note to broken versions of man to tell +.\" them to pre-process this man page with tbl) +.\" Man page for skill and snice. +.\" Licensed under version 2 of the GNU General Public License. +.\" Written by Albert Cahalan, converted to a man page by +.\" Michael K. Johnson +.\" +.TH SKILL 1 "March 12, 1999" "Linux" "Linux User's Manual" +.SH NAME +skill, snice \- report process status + +.SH SYNOPSIS +.nf +skill [signal to send] [options] process selection criteria +snice [new priority] [options] process selection criteria +.fi + +.SH DESCRIPTION +The default signal for skill is TERM. Use -l or -L to list available signals. +Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0. +Alternate signals may be specified in three ways: -9 -SIGKILL -KILL. + +The default priority for snice is +4. (snice +4 ...) +Priority numbers range from +20 (slowest) to -20 (fastest). +Negative priority numbers are restricted to administrative users. + +.SH "GENERAL OPTIONS" +.TS +l l l. +-f fast mode This is not currently useful. +-i interactive use T{ +You will be asked to approve each action. +T} +-v verbose output T{ +Display information about selected processes. +T} +-w warnings enabled This is not currently useful. +-n no action This only displays the process ID. +.TE + +.SH "PROCESS SELECTION OPTIONS" +Selection criteria can be: terminal, user, pid, command. +The options below may be used to ensure correct interpretation. +.TS +l l. +-t The next argument is a terminal (tty or pty). +-u The next argument is a username. +-p The next argument is a process ID number. +-c The next argument is a command name. +.TE + +.SH SIGNALS +The signals listed below may be available for use with skill. +When known, numbers and default behavior are shown. +.TS +lB rB lB lB +lfCW r l l. +Name Num Action Description +.TH +ALRM 14 exit +HUP 1 exit +INT 2 exit +KILL 9 exit this signal may not be blocked +PIPE 13 exit +POLL exit +PROF exit +TERM 15 exit +USR1 exit +USR2 exit +VTALRM exit +STKFLT exit may not be implemented +PWR ignore may exit on some systems +WINCH ignore +CHLD ignore +URG ignore +TSTP stop may interact with the shell +TTIN stop may interact with the shell +TTOU stop may interact with the shell +STOP stop this signal may not be blocked +CONT restart continue if stopped, otherwise ignore +ABRT 6 core +FPE 8 core +ILL 4 core +QUIT 3 core +SEGV 11 core +TRAP 5 core +SYS core may not be implemented +EMT core may not be implemented +BUS core core dump may fail +XCPU core core dump may fail +XFSZ core core dump may fail +.TE + +.SH EXAMPLES +.TS +lB lB +lfCW l. +Command Description +.TC +snice netscape crack +7 Slow down netscape and crack +skill -KILL -v /dev/pts/* Kill users on new-style PTY devices +skill -STOP torvalds davem tytso Stop 3 users +snice -17 root bash Give priority to root's shell +.TE + +.SH "SEE ALSO" +top(1) kill(1) renice(1) nice(1) + +.SH STANDARDS +No standards apply. + +.SH AUTHOR +Albert Cahalan wrote skill and snice in 1999 as a +replacement for a non-free version. Michael K. Johnson +is the current maintainer of the procps collection. + +Please send bug reports to diff --git a/skill.c b/skill.c new file mode 100644 index 00000000..7d784692 --- /dev/null +++ b/skill.c @@ -0,0 +1,557 @@ +/* + * Copyright 1998 by Albert Cahalan; all rights resered. + * This file may be used subject to the terms and conditions of the + * GNU Library General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* char *user_from_uid(uid_t uid) */ + +static int f_flag, i_flag, v_flag, w_flag, n_flag; + +static int tty_count, uid_count, cmd_count, pid_count; +static int *ttys; +static uid_t *uids; +static char **cmds; +static int *pids; + +#define ENLIST(thing,addme) do{ \ +if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \ +if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \ +thing##s[thing##_count++] = addme; \ +}while(0) + +static int my_pid; +static int saved_argc; +static char **saved_argv; + +static int sig_or_pri; + +static int program; +#define PROG_GARBAGE 0 /* keep this 0 */ +#define PROG_KILL 1 +#define PROG_SKILL 2 +/* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */ +#define PROG_SNICE 4 + + +/********************************************************************/ + + +/***** kill or nice a process */ +static void hurt_proc(int tty, int uid, int pid, char *cmd){ + int failed; + int saved_errno; + char dn_buf[1000]; + dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV); + if(i_flag){ + char buf[8]; + fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s ? ", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + if(!fgets(buf,7,stdin)){ + printf("\n"); + exit(0); + } + if(*buf!='y' && *buf!='Y') return; + } + /* do the actual work */ + if(program==PROG_SKILL) failed=kill(pid,sig_or_pri); + else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri); + saved_errno = errno; + if(w_flag && failed){ + fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s ", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + errno = saved_errno; + perror(""); + return; + } + if(i_flag) return; + if(v_flag){ + printf("%-8.8s %-8.8s %5d %-16.16s\n", + (char*)dn_buf,user_from_uid(uid),pid,cmd + ); + return; + } + if(n_flag){ + printf("%d\n",pid); + return; + } +} + + +/***** check one process */ +static void check_proc(int pid){ + char buf[128]; + struct stat statbuf; + char *tmp; + int tty; + int fd; + int i; + if(pid==my_pid) return; + sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */ + fd = open(buf,O_RDONLY); + if(fd==-1){ /* process exited maybe */ + if(pids && w_flag) printf("WARNING: process %d could not be found.",pid); + return; + } + fstat(fd, &statbuf); + if(uids){ /* check the EUID */ + i=uid_count; + while(i--) if(uids[i]==statbuf.st_uid) break; + if(i==-1) goto closure; + } + read(fd,buf,128); + buf[127] = '\0'; + tmp = strrchr(buf, ')'); + *tmp++ = '\0'; + i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */ + tty = atoi(tmp); + if(ttys){ + i=tty_count; + while(i--) if(ttys[i]==tty) break; + if(i==-1) goto closure; + } + tmp = strchr(buf, '(') + 1; + if(cmds){ + i=cmd_count; + /* fast comparison trick -- useful? */ + while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break; + if(i==-1) goto closure; + } + /* This is where we kill/nice something. */ +/* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n", + pid, statbuf.st_uid, tty>>8, tty&0xf, tmp + ); +*/ + hurt_proc(tty, statbuf.st_uid, pid, tmp); +closure: + close(fd); /* kill/nice _first_ to avoid PID reuse */ +} + + +/***** debug function */ +#if 0 +static void show_lists(void){ + int i; + + fprintf(stderr, "%d TTY: ", tty_count); + if(ttys){ + i=tty_count; + while(i--){ + fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n'); + } + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d UID: ", uid_count); + if(uids){ + i=uid_count; + while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d PID: ", pid_count); + if(pids){ + i=pid_count; + while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); + + fprintf(stderr, "%d CMD: ", cmd_count); + if(cmds){ + i=cmd_count; + while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n'); + }else fprintf(stderr, "\n"); +} +#endif + + +/***** iterate over all PIDs */ +static void iterate(void){ + int pid; + DIR *d; + struct dirent *de; + if(pids){ + pid = pid_count; + while(pid--) check_proc(pids[pid]); + return; + } +#if 0 + /* could setuid() and kill -1 to have the kernel wipe out a user */ + if(!ttys && !cmds && !pids && !i_flag){ + } +#endif + d = opendir("/proc"); + if(!d){ + perror("/proc"); + exit(1); + } + while(( de = readdir(d) )){ + if(de->d_name[0] > '9') continue; + if(de->d_name[0] < '1') continue; + pid = atoi(de->d_name); + if(pid) check_proc(pid); + } + closedir (d); +} + +/***** kill help */ +static void kill_usage(void){ + fprintf(stderr, + "Usage:\n" + " kill pid ... Send SIGTERM to every process listed.\n" + " kill signal pid ... Send a signal to every process listed.\n" + " kill -s signal pid ... Send a signal to every process listed.\n" + " kill -l List all signal names.\n" + " kill -L List all signal names in a nice table.\n" + " kill -l signal Convert between signal numbers and names.\n" + ); + exit(1); +} + +/***** kill */ +static void kill_main(int argc, char *argv[]){ + char *sigptr; + int signo = SIGTERM; + int exitvalue = 0; + if(argc<2) kill_usage(); + if(argv[1][0]!='-'){ + argv++; + argc--; + goto no_more_args; + } + + /* The -l option prints out signal names. */ + if(argv[1][1]=='l' && argv[1][2]=='\0'){ + if(argc==2){ + unix_print_signals(); + exit(0); + } + /* at this point, argc must be 3 or more */ + if(argc>128 || argv[2][0] == '-') kill_usage(); + exit(print_given_signals(argc-2, argv+2, 80)); + } + + /* The -L option prints out signal names in a nice table. */ + if(argv[1][1]=='L' && argv[1][2]=='\0'){ + if(argc==2){ + pretty_print_signals(); + exit(0); + } + kill_usage(); + } + if(argv[1][1]=='-' && argv[1][2]=='\0'){ + argv+=2; + argc-=2; + goto no_more_args; + } + if(argv[1][1]=='-') kill_usage(); /* likely --help */ + if(argv[1][1]=='s' && argv[1][2]=='\0'){ + sigptr = argv[2]; + argv+=3; + argc-=3; + }else{ + sigptr = argv[1]+1; + argv+=2; + argc-=2; + } + signo = signal_name_to_number(sigptr); + if(signo<0){ + fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr); + kill_usage(); + } +no_more_args: + if(!argc) kill_usage(); /* nothing to kill? */ + while(argc--){ + long pid; + char *endp; + pid = strtol(argv[argc],&endp,10); + if(!*endp){ + if(!kill((pid_t)pid,signo)) continue; + exitvalue = 1; + continue; + } + fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]); + kill_usage(); + } + exit(exitvalue); +} + +/***** skill/snice help */ +static void skillsnice_usage(void){ + if(program==PROG_SKILL){ + fprintf(stderr, + "Usage: skill [signal to send] [options] process selection criteria\n" + "Example: skill -KILL -v pts/*\n" + "\n" + "The default signal is TERM. Use -l or -L to list available signals.\n" + "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n" + "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n" + ); + }else{ + fprintf(stderr, + "Usage: snice [new priority] [options] process selection criteria\n" + "Example: snice netscape crack +7\n" + "\n" + "The default priority is +4. (snice +4 ...)\n" + "Priority numbers range from +20 (slowest) to -20 (fastest).\n" + "Negative priority numbers are restricted to administrative users.\n" + ); + } + fprintf(stderr, + "\n" + "General options:\n" + "-f fast mode This is not currently useful.\n" + "-i interactive use You will be asked to approve each action.\n" + "-v verbose output Display information about selected processes.\n" + "-w warnings enabled This is not currently useful.\n" + "-n no action This only displays the process ID.\n" + "\n" + "Selection criteria can be: terminal, user, pid, command.\n" + "The options below may be used to ensure correct interpretation.\n" + "-t The next argument is a terminal (tty or pty).\n" + "-u The next argument is a username.\n" + "-p The next argument is a process ID number.\n" + "-c The next argument is a command name.\n" + ); + exit(1); +} + +#if 0 +static void _skillsnice_usage(int line){ + fprintf(stderr,"Something at line %d.\n", line); + skillsnice_usage(); +} +#define skillsnice_usage() _skillsnice_usage(__LINE__) +#endif + +#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL) + +/***** common skill/snice argument parsing code */ +#define NO_PRI_VAL ((int)0xdeafbeef) +static void skillsnice_parse(int argc, char *argv[]){ + int signo = -1; + int prino = NO_PRI_VAL; + int force = 0; + int num_found = 0; + char *argptr; + if(argc<2) skillsnice_usage(); + if(argc==2 && argv[1][0]=='-'){ + if(!strcmp(argv[1],"-L")){ + pretty_print_signals(); + exit(0); + } + if(!strcmp(argv[1],"-l")){ + unix_print_signals(); + exit(0); + } + skillsnice_usage(); + } + NEXTARG; + /* Time for serious parsing. What does "skill -int 123 456" mean? */ + while(argc){ + if(force && !num_found){ /* if forced, _must_ find something */ + fprintf(stderr,"ERROR: -%c used with bad data.\n", force); + skillsnice_usage(); + } + force = 0; + if(program==PROG_SKILL && signo<0 && *argptr=='-'){ + signo = signal_name_to_number(argptr+1); + if(signo>=0){ /* found a signal */ + if(!NEXTARG) break; + continue; + } + } + if(program==PROG_SNICE && prino==NO_PRI_VAL + && (*argptr=='+' || *argptr=='-') && argptr[1]){ + long val; + char *endp; + val = strtol(argptr,&endp,10); + if(!*endp && val<=999 && val>=-999){ + prino=val; + if(!NEXTARG) break; + continue; + } + } + /* If '-' found, collect any flags. (but lone "-" is a tty) */ + if(*argptr=='-' && argptr[1]){ + argptr++; + do{ + switch(( force = *argptr++ )){ + default: skillsnice_usage(); + case 't': + case 'u': + case 'p': + case 'c': + if(!*argptr){ /* nothing left here, *argptr is '\0' */ + if(!NEXTARG){ + fprintf(stderr,"ERROR: -%c with nothing after it.\n", force); + skillsnice_usage(); + } + } + goto selection_collection; + case 'f': f_flag++; break; + case 'i': i_flag++; break; + case 'v': v_flag++; break; + case 'w': w_flag++; break; + case 'n': n_flag++; break; + case 0: + NEXTARG; + /* + * If no more arguments, all the "if(argc)..." tests will fail + * and the big loop will exit. + */ + } /* END OF SWITCH */ + }while(force); + } /* END OF IF */ +selection_collection: + num_found = 0; /* we should find at least one thing */ + switch(force){ /* fall through each data type */ + default: skillsnice_usage(); + case 0: /* not forced */ + case 't': + if(argc){ + struct stat sbuf; + char path[32]; + if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */ + snprintf(path,32,"/dev/%s",argptr); + if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){ + num_found++; + ENLIST(tty,sbuf.st_rdev); + if(!NEXTARG) break; + }else if(!(argptr[1])){ /* if only 1 character */ + switch(*argptr){ + default: + if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */ + case '-': + case '?': + num_found++; + ENLIST(tty,-1); + if(!NEXTARG) break; + } + } + } + if(force) continue; + case 'u': + if(argc){ + struct passwd *passwd_data; + passwd_data = getpwnam(argptr); + if(passwd_data){ + num_found++; + ENLIST(uid,passwd_data->pw_uid); + if(!NEXTARG) break; + } + } + if(force) continue; + case 'p': + if(argc && *argptr>='0' && *argptr<='9'){ + char *endp; + int num; + num = strtol(argptr, &endp, 0); + if(*endp == '\0'){ + num_found++; + ENLIST(pid,num); + if(!NEXTARG) break; + } + } + if(force) continue; + if(num_found) continue; /* could still be an option */ + case 'c': + if(argc){ + num_found++; + ENLIST(cmd,argptr); + if(!NEXTARG) break; + } + } /* END OF SWITCH */ + } /* END OF WHILE */ + /* No more arguments to process. Must sanity check. */ + if(!tty_count && !uid_count && !cmd_count && !pid_count){ + fprintf(stderr,"ERROR: no process selection criteria.\n"); + skillsnice_usage(); + } + if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){ + fprintf(stderr,"ERROR: general flags may not be repeated.\n"); + skillsnice_usage(); + } + if(i_flag && (v_flag|f_flag|n_flag)){ + fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n"); + skillsnice_usage(); + } + if(v_flag && (i_flag|f_flag)){ + fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n"); + skillsnice_usage(); + } + if(n_flag && signo>=0){ + fprintf(stderr,"ERROR: -n makes no sense with a signal.\n"); + skillsnice_usage(); + } + if(n_flag && prino!=NO_PRI_VAL){ + fprintf(stderr,"ERROR: -n makes no sense with a priority value.\n"); + skillsnice_usage(); + } + /* OK, set up defaults */ + if(prino==NO_PRI_VAL) prino=4; + if(signo<0) signo=SIGTERM; + if(n_flag){ + program=PROG_SKILL; + signo=0; /* harmless */ + } + if(program==PROG_SKILL) sig_or_pri = signo; + else sig_or_pri = prino; +} + +/***** main body */ +int main(int argc, char *argv[]){ + char *tmpstr; + my_pid = getpid(); + saved_argv = argv; + saved_argc = argc; + if(!argc){ + fprintf(stderr,"ERROR: could not determine own name.\n"); + exit(1); + } + tmpstr=strrchr(*argv,'/'); + if(tmpstr) tmpstr++; + if(!tmpstr) tmpstr=*argv; + program = PROG_GARBAGE; + if(*tmpstr=='s'){ + setpriority(PRIO_PROCESS,my_pid,-20); + if(!strcmp(tmpstr,"snice")) program = PROG_SNICE; + if(!strcmp(tmpstr,"skill")) program = PROG_SKILL; + }else{ + if(!strcmp(tmpstr,"kill")) program = PROG_KILL; + } + switch(program){ + case PROG_SNICE: + case PROG_SKILL: + skillsnice_parse(argc, argv); +/* show_lists(); */ + iterate(); /* this is it, go get them */ + break; + case PROG_KILL: + kill_main(argc, argv); + break; + default: + fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr); + } + return 0; +} + + diff --git a/snice.1 b/snice.1 new file mode 100644 index 00000000..1595a808 --- /dev/null +++ b/snice.1 @@ -0,0 +1 @@ +.so man1/skill.1 diff --git a/sysctl.8 b/sysctl.8 new file mode 100644 index 00000000..1491252a --- /dev/null +++ b/sysctl.8 @@ -0,0 +1,74 @@ +.\" Copyright 1999, George Staikos (staikos@0wned.org) +.\" This file may be used subject to the terms and conditions of the +.\" GNU General Public License Version 2, or any later version +.\" at your option, as published by the Free Software Foundation. +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details." +.TH SYSCTL 8 "21 Sep 1999" "" "" +.SH NAME +sysctl \- configure kernel parameters at runtime +.SH SYNOPSIS +.B "sysctl [-n] variable ..." +.br +.B "sysctl [-n] -w variable=value ..." +.br +.B "sysctl [-n] -p (default /etc/sysctl.conf)" +.br +.B "sysctl [-n] -a" +.br +.B "sysctl [-n] -A" +.SH DESCRIPTION +.B sysctl +is used to modify kernel parameters at runtime. The parameters available +are those listed under /proc/sys/. Procfs is required for +.B sysctl(8) +support in Linux. You can use +.B sysctl(8) +to both read and write sysctl data. +.SH PARAMETERS +.TP +.B "variable" +The name of a key to read from. An example is kernel.ostype. The '/' +separator is also accepted in place of a '.'. +.TP +.B "variable=value" +To set a key, use the form variable=value, where variable is the key and +value is the value to set it to. If the value contains quotes or characters +which are parsed by the shell, you may need to enclose the value in double +quotes. This requires the -w parameter to use. +.TP +.B "-n" +Use this option to disable printing of the key name when printing values. +.TP +.B "-w" +Use this option when you want to change a sysctl setting. +.TP +.B "-p" +Load in sysctl settings from the file specified or /etc/sysctl.conf if none given. +.TP +.B "-a" +Display all values currently available. +.TP +.B "-A" +Display all values currently available in table form. +.SH EXAMPLES +.TP +/sbin/sysctl -a +.TP +/sbin/sysctl -n kernel.hostname +.TP +/sbin/sysctl -w kernel.domainname="example.com" +.TP +/sbin/sysctl -p /etc/sysctl.conf +.SH FILES +.I /proc/sys +.I /etc/sysctl.conf +.SH SEE ALSO +.BR sysctl.conf (5) +.SH BUGS +The -A parameter behaves just as -a does. +.SH AUTHOR +George Staikos, + diff --git a/sysctl.c b/sysctl.c new file mode 100644 index 00000000..e1af4bd7 --- /dev/null +++ b/sysctl.c @@ -0,0 +1,437 @@ + +/* + * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters + * + * + * "Copyright 1999 George Staikos + * This file may be used subject to the terms and conditions of the + * GNU General Public License Version 2, or any later version + * at your option, as published by the Free Software Foundation. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details." + * + * Changelog: + * v1.01: + * - added -p to preload values from a file + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Additional types we might need. + */ +typedef int bool; + +static bool true = 1; +static bool false = 0; + + +/* + * Function Prototypes + */ +int Usage(const char *name); +void Preload(const char *filename); +int WriteSetting(const char *setting); +int ReadSetting(const char *setting); +int DisplayAll(const char *path, bool ShowTableUtil); + + +/* + * Globals... + */ + +const char *PROC_PATH = "/proc/sys/"; +const char *DEFAULT_PRELOAD = "/etc/sysctl.conf"; +static bool PrintName; +static bool PrintNewline; + +/* error messages */ +const char *ERR_UNKNOWN_PARAMETER = "error: Unknown parameter '%s'\n"; +const char *ERR_MALFORMED_SETTING = "error: Malformed setting '%s'\n"; +const char *ERR_NO_EQUALS = "error: '%s' must be of the form name=value\n"; +const char *ERR_INVALID_KEY = "error: '%s' is an unknown key\n"; +const char *ERR_UNKNOWN_WRITING = "error: unknown error %d setting key '%s'\n"; +const char *ERR_UNKNOWN_READING = "error: unknown error %d reading key '%s'\n"; +const char *ERR_PERMISSION_DENIED = "error: permission denied on key '%s'\n"; +const char *ERR_OPENING_DIR = "error: unable to open directory '%s'\n"; +const char *ERR_PRELOAD_FILE = "error: unable to open preload file '%s'\n"; +const char *WARN_BAD_LINE = "warning: %s(%d): invalid syntax, continuing...\n"; + + +#define DOTSLASH(x) do{ \ + char *p_ = (x); \ + for(;;){ \ + p_ = strchr(p_, '.'); \ + if(!p_) break; \ + *p_ = '/'; \ + } \ +}while(0) + +#define SLASHDOT(x) do{ \ + char *p_ = (x); \ + for(;;){ \ + p_ = strchr(p_, '.'); \ + if(!p_) break; \ + *p_ = '/'; \ + } \ +}while(0) + + +/* + * Main... + * + */ +int main(int argc, char **argv) { +const char *me = (const char *)basename(argv[0]); +bool SwitchesAllowed = true; +bool WriteMode = false; +int ReturnCode = 0; +const char *preloadfile = DEFAULT_PRELOAD; + + PrintName = true; + PrintNewline = true; + + if (argc < 2) { + return Usage(me); + } /* endif */ + + argv++; + + for (; argv && *argv && **argv; argv++) { + if (SwitchesAllowed && **argv == '-') { /* we have a switch */ + switch((*argv)[1]) { + case 'b': + /* This is "binary" format, which means more for BSD. */ + PrintNewline = false; + /* FALL THROUGH */ + case 'n': + PrintName = false; + break; + case 'w': + SwitchesAllowed = false; + WriteMode = true; + break; + case 'p': + argv++; + if (argv && *argv && **argv) { + preloadfile = *argv; + } /* endif */ + + Preload(preloadfile); + return(0); + break; + case 'a': /* string and integer values (for Linux, all of them) */ + case 'A': /* the above, including "opaques" (would be unprintable) */ + case 'X': /* the above, with opaques completly printed in hex */ + SwitchesAllowed = false; + return DisplayAll(PROC_PATH, ((*argv)[1] == 'a') ? false : true); + case 'h': + case '?': + return Usage(me); + default: + fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv); + return Usage(me); + } /* end switch */ + } else { + SwitchesAllowed = false; + if (WriteMode) + ReturnCode = WriteSetting(*argv); + else ReadSetting(*argv); + } /* end if */ + } /* end for */ + +return ReturnCode; +} /* end main */ + + + + + +/* + * Display the usage format + * + */ +int Usage(const char *name) { + printf("usage: %s [-n] variable ... \n" + " %s [-n] -w variable=value ... \n" + " %s [-n] -a \n" + " %s [-n] -p (default /etc/sysctl.conf) \n" + " %s [-n] -A\n", name, name, name, name, name); +return -1; +} /* end Usage() */ + + +/* + * Strip the leading and trailing spaces from a string + * + */ +char *StripLeadingAndTrailingSpaces(char *oneline) { +char *t; + +if (!oneline || !*oneline) + return oneline; + +t = oneline; +t += strlen(oneline)-1; + +while ((*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r') && t != oneline) + *t-- = 0; + +t = oneline; + +while ((*t == ' ' || *t == '\t') && *t != 0) + t++; + +return t; +} /* end StripLeadingAndTrailingSpaces() */ + + + +/* + * Preload the sysctl's from the conf file + * - we parse the file and then reform it (strip out whitespace) + * + */ +void Preload(const char *filename) { +FILE *fp; +char oneline[257]; +char buffer[257]; +char *t; +int n = 0; +char *name, *value; + + if (!filename || ((fp = fopen(filename, "r")) == NULL)) { + fprintf(stderr, ERR_PRELOAD_FILE, filename); + return; + } /* endif */ + + while (fgets(oneline, 256, fp)) { + oneline[256] = 0; + n++; + t = StripLeadingAndTrailingSpaces(oneline); + + if (strlen(t) < 2) + continue; + + if (*t == '#' || *t == ';') + continue; + + name = strtok(t, "="); + if (!name || !*name) { + fprintf(stderr, WARN_BAD_LINE, filename, n); + continue; + } /* endif */ + + StripLeadingAndTrailingSpaces(name); + + value = strtok(NULL, "\n\r"); + if (!value || !*value) { + fprintf(stderr, WARN_BAD_LINE, filename, n); + continue; + } /* endif */ + + while ((*value == ' ' || *value == '\t') && *value != 0) + value++; + + sprintf(buffer, "%s=%s", name, value); + WriteSetting(buffer); + } /* endwhile */ + + fclose(fp); +} /* end Preload() */ + + + +/* + * Write a sysctl setting + * + */ +int WriteSetting(const char *setting) { +int rc = 0; +const char *name = setting; +const char *value; +const char *equals; +char *tmpname; +FILE *fp; +char *outname; + + if (!name) { /* probably dont' want to display this err */ + return 0; + } /* end if */ + + equals = index(setting, '='); + + if (!equals) { + fprintf(stderr, ERR_NO_EQUALS, setting); + return -1; + } /* end if */ + + value = equals + sizeof(char); /* point to the value in name=value */ + + if (!*name || !*value || name == equals) { + fprintf(stderr, ERR_MALFORMED_SETTING, setting); + return -2; + } /* end if */ + + tmpname = (char *)malloc((equals-name+1+strlen(PROC_PATH))*sizeof(char)); + outname = (char *)malloc((equals-name+1)*sizeof(char)); + + strcpy(tmpname, PROC_PATH); + strncat(tmpname, name, (int)(equals-name)); + tmpname[equals-name+strlen(PROC_PATH)] = 0; + strncpy(outname, name, (int)(equals-name)); + outname[equals-name] = 0; + + DOTSLASH(tmpname); /* change . to / */ + SLASHDOT(outname); /* change / to . */ + + fp = fopen(tmpname, "w"); + + if (!fp) { + switch(errno) { + case ENOENT: + fprintf(stderr, ERR_INVALID_KEY, outname); + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + break; + default: + fprintf(stderr, ERR_UNKNOWN_WRITING, errno, outname); + break; + } /* end switch */ + rc = -1; + } else { + fprintf(fp, "%s\n", value); + fclose(fp); + if (PrintName) { + fprintf(stdout, "%s = %s\n", outname, value); + } else { + if (PrintNewline) + fprintf(stdout, "%s\n", value); + else + fprintf(stdout, "%s", value); + } + } /* endif */ + + free(tmpname); + free(outname); +return rc; +} /* end WriteSetting() */ + + + +/* + * Read a sysctl setting + * + */ +int ReadSetting(const char *setting) { +int rc = 0; +char *tmpname, *outname; +char inbuf[1025]; +const char *name = setting; +FILE *fp; + + if (!setting || !*setting) { + fprintf(stderr, ERR_INVALID_KEY, setting); + } /* endif */ + + tmpname = (char *)malloc((strlen(name)+strlen(PROC_PATH)+1)*sizeof(char)); + outname = (char *)malloc((strlen(name)+1)*sizeof(char)); + + strcpy(tmpname, PROC_PATH); + strcat(tmpname, name); + strcpy(outname, name); + + + DOTSLASH(tmpname); /* change . to / */ + SLASHDOT(outname); /* change / to . */ + + fp = fopen(tmpname, "r"); + + if (!fp) { + switch(errno) { + case ENOENT: + fprintf(stderr, ERR_INVALID_KEY, outname); + break; + case EACCES: + fprintf(stderr, ERR_PERMISSION_DENIED, outname); + break; + default: + fprintf(stderr, ERR_UNKNOWN_READING, errno, outname); + break; + } /* end switch */ + rc = -1; + } else { + while(fgets(inbuf, 1024, fp)) { + /* already has the \n in it */ + if (PrintName) { + fprintf(stdout, "%s = %s", outname, inbuf); + } else { + if (!PrintNewline) { + char *nlptr = strchr(inbuf,'\n'); + if(nlptr) *nlptr='\0'; + } + fprintf(stdout, "%s", inbuf); + } + } /* endwhile */ + fclose(fp); + } /* endif */ + + free(tmpname); + free(outname); +return rc; +} /* end ReadSetting() */ + + + +/* + * Display all the sysctl settings + * + */ +int DisplayAll(const char *path, bool ShowTableUtil) { +int rc = 0; +int rc2; +DIR *dp; +struct dirent *de; +char *tmpdir; +struct stat ts; + + dp = opendir(path); + + if (!dp) { + fprintf(stderr, ERR_OPENING_DIR, path); + rc = -1; + } else { + readdir(dp); readdir(dp); /* skip . and .. */ + while (( de = readdir(dp) )) { + tmpdir = (char *)malloc(strlen(path)+strlen(de->d_name)+2); + sprintf(tmpdir, "%s%s", path, de->d_name); + rc2 = stat(tmpdir, &ts); /* should check this return code */ + if (rc2 != 0) { + perror(tmpdir); + } else { + if (S_ISDIR(ts.st_mode)) { + strcat(tmpdir, "/"); + DisplayAll(tmpdir, ShowTableUtil); + } else { + rc |= ReadSetting(tmpdir+strlen(PROC_PATH)); + } /* endif */ + } /* endif */ + free(tmpdir); + } /* end while */ + closedir(dp); + } /* endif */ + +return rc; +} /* end DisplayAll() */ + diff --git a/sysctl.conf.5 b/sysctl.conf.5 new file mode 100644 index 00000000..0d8b0731 --- /dev/null +++ b/sysctl.conf.5 @@ -0,0 +1,51 @@ +.\" Copyright 1999, George Staikos (staikos@0wned.org) +.\" This file may be used subject to the terms and conditions of the +.\" GNU General Public License Version 2, or any later version +.\" at your option, as published by the Free Software Foundation. +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details." +.TH SYSCTL.CONF 5 "21 Sep 1999" "" "" +.SH NAME +sysctl.conf \- sysctl(8) preload/configuration file +.SH DESCRIPTION +.I sysctl.conf +is a simple file containing sysctl values to be read in and set by sysctl(8). +The syntax is simply as follows: +.RS +.sp +.nf +.ne 7 +# comment +; comment + + token = value +.fi +.sp +.RE +.PP +Note that blank lines are ignored, and whitespace before and after a token or +value is ignored, although a value can contain whitespace within. Lines which +begin with a # or ; are considered comments and ignored. +.SH EXAMPLE +.RS +.sp +.nf +.ne 7 +# sysctl.conf sample +# + + kernel.domainname = example.com +; this one has a space which will be written to the sysctl! + kernel.modprobe = /sbin/mod probe + +.fi +.sp +.RE +.PP +.SH SEE ALSO +.BR sysctl(8) +.SH AUTHOR +George Staikos, + diff --git a/t b/t new file mode 100755 index 00000000..c7b3c06f --- /dev/null +++ b/t @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Wow, using $* causes great pain with embedded spaces in arguments. +# The "$@" won't break that into 2 arguments. +# +LD_PRELOAD=proc/libproc.so exec ./top "$@" diff --git a/tload.1 b/tload.1 new file mode 100644 index 00000000..aec34dd4 --- /dev/null +++ b/tload.1 @@ -0,0 +1,50 @@ +.\" -*-Nroff-*- +.\" This page Copyright (C) 1993 Matt Welsh, mdw@tc.cornell.edu. +.\" Freely distributable under the terms of the GPL +.TH TLOAD 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual" +.SH NAME +tload \- graphic representation of system load average +.SH SYNOPSIS +.B tload +.RB [ "\-V" "] [" "\-s" +.IR scale "] [" +.BI "\-d" " delay" +.RI "] [" tty ] +.SH DESCRIPTION +\fBtload\fP prints a graph of the current system load average to the +specified \fItty\fP (or the tty of the tload process if none is specified). +.SS Options +The +.BI "\-s" " scale" +option allows a vertical scale to be specified for the +display (in characters between graph ticks); thus, a smaller value +represents a larger scale, and vice versa. + +The +.BI "\-d" " delay" +sets the delay between graph updates in seconds. +.PP +.SH FILES +.I /proc/loadavg +load average information + +.SH "SEE ALSO" +.BR ps (1), +.BR top (1), +.BR uptime (1), +.BR w (1) + +.SH BUGS +The +.BI "\-d" " delay" +option sets the time argument for an +.BR alarm (2); +if -d 0 is specified, the alarm is set to 0, which will never send the +.B SIGALRM +and update the display. + +.SH AUTHORS +Branko Lankester, David Engel , and +Michael K. Johnson . + +Please send bug reports to diff --git a/tload.c b/tload.c new file mode 100644 index 00000000..1d346bbe --- /dev/null +++ b/tload.c @@ -0,0 +1,156 @@ +/* + * tload.c - terminal version of xload + * + * Options: + * -s initial scaling exponent (default = 6) + * -d delay + * + * Copyright (c) 1992 Branko Lankester + * /proc changes by David Engel (david@ods.com) + * Made a little more efficient by Michael K. Johnson (johnsonm@sunsite.unc.edu) + */ +#include "proc/version.h" +#include "proc/sysinfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *screen; + +static int nrows = 25; +static int ncols = 80; +static int scr_size; +static int fd=1; +static int dly=5; +static jmp_buf jb; + +extern int optind; +extern char *optarg; + +static void alrm(int signo) +{ + (void)signo; + signal(SIGALRM, alrm); + alarm(dly); +} + +static void setsize(int i) +{ + struct winsize win; + + signal(SIGWINCH, setsize); + if (ioctl(fd, TIOCGWINSZ, &win) != -1) { + if (win.ws_col > 0) + ncols = win.ws_col; + if (win.ws_row > 0) + nrows = win.ws_row; + } + scr_size = nrows * ncols; + if (screen == NULL) + screen = (char *) malloc(scr_size); + else + screen = (char *) realloc(screen, scr_size); + + if (screen == NULL) { + perror(""); + exit(1); + } + memset(screen, ' ', scr_size-1); + *(screen + scr_size - 2) = '\0'; + if (i) + longjmp(jb, 0); +} + +int main(int argc, char **argv) +{ + int lines, row, col=0; + int i, opt; + double av[3]; + static double max_scale, scale_fact; + char *scale_arg = NULL; + + while ((opt = getopt(argc, argv, "s:d:V")) != -1) + switch (opt) { + case 's': scale_arg = optarg; break; + case 'd': dly = atoi(optarg); break; + case 'V': display_version(); exit(0); break; + default: + printf("usage: tload [-V] [-d delay] [-s scale] [tty]\n"); + exit(1); + } + + if (argc > optind) { + if ((fd = open(argv[optind], 1)) == -1) { + perror(argv[optind]); + exit(1); + } + } + + setsize(0); + + if (scale_arg) + max_scale = atof(scale_arg); + else + max_scale = nrows; + + scale_fact = max_scale; + + setjmp(jb); + col = 0; + alrm(0); + + while (1) { + + if (scale_fact < max_scale) + scale_fact *= 2.0; /* help it drift back up. */ + + loadavg(&av[0], &av[1], &av[2]); + + repeat: + lines = av[0] * scale_fact; + row = nrows-1; + + while (--lines >= 0) { + *(screen + row * ncols + col) = '*'; + if (--row < 0) { + scale_fact /= 2.0; + goto repeat; + } + } + while (row >= 0) + *(screen + row-- * ncols + col) = ' '; + + for (i = 1; ; ++i) { + char *p; + row = nrows - (i * scale_fact); + if (row < 0) + break; + if (*(p = screen + row * ncols + col) == ' ') + *p = '-'; + else + *p = '='; + } + + if (++col == ncols) { + --col; + memmove(screen, screen + 1, scr_size-1); + + for(row = nrows-2; row >= 0; --row) + *(screen + row * ncols + col) = ' '; + } + i = sprintf(screen, " %.2f, %.2f, %.2f", + av[0], av[1], av[2]); + if (i>0) + screen[i] = ' '; + + write(fd, "\033[H", 3); + write(fd, screen, scr_size - 1); + pause(); + } +} diff --git a/tmp-junk.c b/tmp-junk.c new file mode 100644 index 00000000..db011b17 --- /dev/null +++ b/tmp-junk.c @@ -0,0 +1,730 @@ +/* + * w.c v1.4 + * + * An alternative "w" program for Linux. + * Shows users and their processes. + * + * Copyright (c) Dec 1993, Oct 1994 Steve "Mr. Bassman" Bryant + * bassman@hpbbi30.bbn.hp.com (Old address) + * bassman@muttley.soc.staffs.ac.uk + * + * Info: + * I starting writing as an improvement of the w program included + * with linux. The idea was to add in some extra functionality to the + * program, and see if I could fix a couple of bugs which seemed to + * occur. + * Mr. Bassman, 10/94 + * + * Acknowledgments: + * + * The original version of w: + * Copyright (c) 1993 Larry Greenfield (greenfie@gauss.rutgers.edu) + * + * Uptime routine and w mods: + * Michael K. Johnson (johnsonm@stolaf.edu) + * + * + * Distribution: + * This program is freely distributable under the terms of copyleft. + * No warranty, no support, use at your own risk etc. + * + * Compilation: + * gcc -O -o w sysinfo.c whattime.c w.c + * + * Usage: + * w [-hfusd] [user] + * + * + * $Log: tmp-junk.c,v $ + * Revision 1.1 2002/02/01 22:46:37 csmall + * Initial revision + * + * Revision 1.5 1994/10/26 17:57:35 bassman + * Loads of stuff - see comments. + * + * Revision 1.4 1994/01/01 12:57:21 johnsonm + * Added RCS, and some other fixes. + * + * Revision history: + * Jan 01, 1994 (mkj): Eliminated GCC warnings, took out unnecessary + * dead variables in fscanf, replacing them with + * *'d format qualifiers. Also added RCS stuff. + * Oct 26, 1994 (bass): Tidied up the code, fixed bug involving corrupt + * utmp records. Added switch for From field; + * default is compile-time set. Added -d option + * as a remnant from BSD 'w'. Fixed bug so it now + * behaves if the first process on a tty isn't owned + * by the person first logged in on that tty, and + * also detects su'd users. Changed the tty format + * to the short one. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "proc/whattime.h" + + +#define TRUE 1 +#define FALSE 0 +/* + * Default setting for whether to have a From field. The -f switch + * toggles this - if the default is to have it, using -f will turn + * it off; if the default is not to have it, the -f switch will put + * it in. Possible values are TRUE (to have the field by default), + * and FALSE. + */ +#define DEFAULT_FROM TRUE +#define ZOMBIE "" + + +void put_syntax(); +char *idletime(); +char *logintime(); + +static char rcsid[]="$Id: tmp-junk.c,v 1.1 2002/02/01 22:46:37 csmall Exp $"; + + +void main (argc, argv) + +int argc; +char *argv[]; + +{ + int header=TRUE, long_format=TRUE, ignore_user=TRUE, + from_switch=DEFAULT_FROM, show_pid=FALSE, line_length; + int i, j; + struct utmp *utmp_rec; + struct stat stat_rec; + struct passwd *passwd_entry; + uid_t uid; + char username[9], tty[13], rhost[17], login_time[27]; + char idle_time[7], what[1024], pid[10]; + char out_line[1024], file_name[256]; + char search_name[9]; + int jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime; + char /*ch,*/ state, comm[1024], *columns_ptr; + FILE *fp; + + + search_name[0] = '\0'; + + + /* + * Process the command line + */ + if (argc > 1) + { + /* + * Args that start with '-' + */ + for (i = 1; ((i < argc) && (argv[i][0] == '-')); i ++) + { + for (j = 1; argv[i][j] != '\0'; j++) + { + switch (argv[i][j]) + { + case 'h': + header = FALSE; + break; + case 's': + long_format = FALSE; + break; + case 'u': + ignore_user = FALSE; + break; + case 'd': + show_pid = TRUE; + break; + case 'f': + if (DEFAULT_FROM == TRUE) + from_switch = FALSE; + else + from_switch = TRUE; + break; + default: + fprintf (stderr, "w: unknown option: '%c'\n", + argv[i][j]); + put_syntax (); + break; + } + } + } + + + /* + * Check for arg not starting with '-' (ie: username) + */ + if (argc > i) + { + strncpy (search_name, argv[i], 8); + search_name[8] = '\0'; + i ++; + + if (argc > i) + { + fprintf (stderr, "w: syntax error\n"); + put_syntax (); + } + } + } + + + + /* + * Check that /proc is actually there, or else we can't + * get all the information. + */ + if (chdir ("/proc")) + { + fprintf (stderr, "w: fatal error: cannot access /proc\n"); + perror (strerror(errno)); + exit (-1); + } + + + + /* + * Find out our screen width from $COLUMNS + */ + columns_ptr = getenv ("COLUMNS"); + if (columns_ptr == NULL) + { + struct winsize window; + + /* + * Try getting it directly + */ + if ((ioctl (1, TIOCGWINSZ, &window) != 1) && (window.ws_col > 0)) + line_length = window.ws_col; + else + line_length = 80; /* Default length assumed */ + } + else + line_length = atoi (columns_ptr); + + /* + * Maybe we should check whether there is enough space on + * the lines for the options selected... + */ + if (line_length < 60) + long_format = FALSE; + + line_length --; + + + /* + * Print whatever headers + */ + if (header == TRUE) + { + /* + * uptime: from MKJ's uptime routine, + * found in whattime.c + */ + print_uptime(); + + + /* + * Print relevant header bits + */ + printf ("User tty "); + + if (long_format == TRUE) + { + if (from_switch == TRUE) + printf ("From "); + + printf (" login@ idle JCPU PCPU "); + + if (show_pid == TRUE) + printf (" PID "); + + printf ("what\n"); + } + else + { + printf (" idle "); + + if (show_pid == TRUE) + printf (" PID "); + + printf ("what\n"); + } + } + + + + + /* + * Process user information. + */ + while ((utmp_rec = getutent())) + { + /* + * Check we actually want to see this record. + * It must be a valid active user process, + * and match a specified search name. + */ + if ( (utmp_rec->ut_type == USER_PROCESS) + && (strcmp(utmp_rec->ut_user, "")) + && ( (search_name[0] == '\0') + || ( (search_name[0] != '\0') + && !strncmp(search_name, utmp_rec->ut_user, 8) ) ) ) + { + /* + * Get the username + */ + strncpy (username, utmp_rec->ut_user, 8); + username[8] = '\0'; /* Set end terminator */ + + + /* + * Find out the uid of that user (from their + * passwd entry) + */ + uid = -1; + if ((passwd_entry = getpwnam (username)) != NULL) + { + uid = passwd_entry->pw_uid; + } + + /* + * Get (and clean up) the tty line + */ + for (i = 0; (utmp_rec->ut_line[i] > 32) && (i < 6); i ++) + tty[i] = utmp_rec->ut_line[i]; + + utmp_rec->ut_line[i] = '\0'; + tty[i] = '\0'; + + + /* + * Don't bother getting info if it's not asked for + */ + if (long_format == TRUE) + { + + /* + * Get the remote hostname; this can be up to 16 chars, + * but if any chars are invalid (ie: [^a-zA-Z0-9\.]) + * then the char is changed to a string terminator. + */ + if (from_switch == TRUE) + { + strncpy (rhost, utmp_rec->ut_host, 16); + rhost[16] = '\0'; + + } + + + /* + * Get the login time + * (Calculated by LG's routine, below) + */ + strcpy (login_time, logintime(utmp_rec->ut_time)); + } + + + + /* + * Get the idle time. + * (Calculated by LG's routine, below) + */ + strcpy (idle_time, idletime (tty)); + + + + /* + * That's all the info out of /etc/utmp. + * The rest is more difficult. We use the pid from + * utmp_rec->ut_pid to look in /proc for the info. + * NOTE: This is not necessarily the active pid, so we chase + * down the path of parent -> child pids until we find it, + * according to the information given in /proc//stat. + */ + + sprintf (pid, "%d", utmp_rec->ut_pid); + + what[0] = '\0'; + strcpy (file_name, pid); + strcat (file_name, "/stat"); + jcpu = 0; + pcpu = 0; + + if ((fp = fopen(file_name, "r"))) + { + while (what[0] == '\0') + { + /* + * Check /proc//stat to see if the process + * controlling the tty is the current one + */ + fscanf (fp, "%d %s %c %*d %*d %*d %*d %d " + "%*u %*u %*u %*u %*u %d %d %d %d", + &curr_pid, comm, &state, &tpgid, + &utime, &stime, &cutime, &cstime); + + fclose (fp); + + if (comm[0] == '\0') + strcpy (comm, "-"); + + /* + * Calculate jcpu and pcpu. + * JCPU is the time used by all processes and their + * children, attached to the tty. + * PCPU is the time used by the current process + * (calculated once after the loop, using last + * obtained values). + */ + if (!jcpu) + jcpu = cutime + cstime; + + /* + * Check for a zombie first... + */ + if (state == 'Z') + strcpy (what, ZOMBIE); + else if (curr_pid == tpgid) + { + /* + * If it is the current process, read cmdline + * If that's empty, then the process is swapped out, + * or is a zombie, so we use the command given in stat + * which is in normal round brackets, ie: "()". + */ + strcpy (file_name, pid); + strcat (file_name, "/cmdline"); + if ((fp = fopen(file_name, "r"))) + { + i = 0; + j = fgetc (fp); + while ((j != EOF) && (i < 256)) + { + if (j == '\0') + j = ' '; + + what[i] = j; + i++; + j = fgetc (fp); + } + what[i] = '\0'; + fclose (fp); + } + + if (what[0] == '\0') + strcpy (what, comm); + } + else + { + /* + * Check out the next process + * If we can't open it, use info from this process, + * so we have to check out cmdline first. + * + * If we're not using "-u" then should we just + * say "-" (or "-su") instead of a command line ? + * If so, we should strpcy(what, "-"); when we + * fclose() in the if after the stat() below. + */ + strcpy (file_name, pid); + strcat (file_name, "/cmdline"); + + if ((fp = fopen (file_name, "r"))) + { + i = 0; + j = fgetc (fp); + while ((j != EOF) && (i < 256)) + { + if (j == '\0') + j = ' '; + + what[i] = j; + i++; + j = fgetc (fp); + } + what[i] = '\0'; + fclose (fp); + } + + if (what[0] == '\0') + strcpy (what, comm); + + /* + * Now we have something in the what variable, + * in case we can't open the next process. + */ + sprintf (pid, "%d", tpgid); + strcpy (file_name, pid); + strcat (file_name, "/stat"); + + fp = fopen (file_name, "r"); + + if (fp && (ignore_user == FALSE)) + { + /* + * We don't necessarily go onto the next process, + * unless we are either ignoring who the effective + * user is, or it's the same uid + */ + stat (file_name, &stat_rec); + + /* + * If the next process is not owned by this + * user finish the loop. + */ + if (stat_rec.st_uid != uid) + { + fclose (fp); + + strcpy (what, "-su"); + /* + * See comment above somewhere; I've used + * "-su" here, as the next process is owned + * by someone else; this is generally + * because the user has done an "su" which + * then exec'd something else. + */ + } + else + what[0] = '\0'; + } + else if (fp) /* else we are ignoring uid's */ + what[0] = '\0'; + } + } + } + else /* Could not open first process for user */ + strcpy (what, "?"); + + + /* + * There is a bug somewhere in my version of linux + * which means that utmp records are not cleaned + * up properly when users log out. However, we + * can detect this, by the users first process + * not being there when we look in /proc. + */ + + + /* + * Don't output a line for "dead" users. + * This gets round a bug which doesn't update utmp/wtmp + * when users log out. + */ + if (what[0] != '?') + { +#ifdef 0 +/* This makes unix98 pty's not line up, so has been disabled - JEH. */ + /* + * Remove the letters 'tty' from the tty id + */ + if (!strncmp (tty, "tty", 3)) + { + for (i = 3; tty[i - 1] != '\0'; i ++) + tty[i - 3] = tty[i]; + } +#endif + + /* + * Common fields + */ + sprintf (out_line, "%-9.8s%-6.7s ", username, tty); + + + /* + * Format the line for output + */ + if (long_format == TRUE) + { + /* + * Calculate CPU usage + */ + pcpu = utime + stime; + jcpu /= 100; + pcpu /= 100; + + if (from_switch == TRUE) + sprintf (out_line, "%s %-16.15s", out_line, rhost); + + sprintf (out_line, "%s%8.8s ", out_line, login_time); + + } + + sprintf (out_line, "%s%6s", out_line, idle_time); + + + if (long_format == TRUE) + { + if (!jcpu) + strcat (out_line, " "); + else if (jcpu/60) + sprintf (out_line, "%s%3d:%02d", out_line, + jcpu/60, jcpu%60); + else + sprintf (out_line, "%s %2d", out_line, jcpu); + + if (!pcpu) + strcat (out_line, " "); + else if (pcpu/60) + sprintf (out_line, "%s%3d:%02d", out_line, + pcpu/60, pcpu%60); + else + sprintf (out_line, "%s %2d", out_line, pcpu); + } + + if (show_pid == TRUE) + sprintf (out_line, "%s %5.5s", out_line, pid); + + + strcat (out_line, " "); + strcat (out_line, what); + + + /* + * Try not to exceed the line length + */ + out_line[line_length] = '\0'; + + printf ("%s\n", out_line); + } + } + } +} + + + +/* + * put_syntax() + * + * Routine to print the correct syntax to call this program, + * and then exit out appropriately + */ +void put_syntax () +{ + fprintf (stderr, "usage: w [-hfsud] [user]\n"); + exit (-1); +} + + + +/* + * idletime() + * + * Routine which returns a string containing + * the idle time of a given user. + * + * This routine was lifted from the original w program + * by Larry Greenfield (greenfie@gauss.rutgers.edu) + * Copyright (c) 1993 Larry Greenfield + * + */ +char *idletime (tty) + +char *tty; + +{ + struct stat terminfo; + unsigned long idle; + char ttytmp[40]; + static char give[20]; + time_t curtime; + + curtime = time (NULL); + + sprintf (ttytmp, "/dev/%s", tty); + stat (ttytmp, &terminfo); + idle = (unsigned long) curtime - (unsigned long) terminfo.st_atime; + + if (idle >= (60 * 60)) /* more than an hour */ + { + if (idle >= (60 * 60 * 48)) /* more than two days */ + sprintf (give, "%2ludays", idle / (60 * 60 * 24)); + else + sprintf (give, " %2lu:%02u", idle / (60 * 60), + (unsigned) ((idle / 60) % 60)); + } + else + { + if (idle / 60) + sprintf (give, "%6lu", idle / 60); + else + give[0]=0; + } + + return give; +} + + + +/* + * logintime() + * + * Returns the time given in a suitable format + * + * This routine was lifted from the original w program + * by Larry Greenfield (greenfie@gauss.rutgers.edu) + * Copyright (c) 1993 Larry Greenfield + * + */ + +#undef ut_time + +char *logintime(ut_time) + +time_t ut_time; + +{ + time_t curtime; + struct tm *logintime, *curtm; + int hour, am, curday, logday; + static char give[20]; + static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", + "Sat" }; + static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" }; + + curtime = time(NULL); + curtm = localtime(&curtime); + curday = curtm->tm_yday; + logintime = localtime(&ut_time); + hour = logintime->tm_hour; + logday = logintime->tm_yday; + am = (hour < 12); + + if (!am) + hour -= 12; + + if (hour == 0) + hour = 12; + + /* + * This is a newer behavior: it waits 12 hours and the next day, and then + * goes to the 2nd time format. This should reduce confusion. + * It then waits only 6 days (not till the last moment) to go the last + * time format. + */ + if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday)) + { + if (curtime > (ut_time + (60 * 60 * 24 * 6))) + sprintf(give, "%2d%3s%2d", logintime->tm_mday, + month[logintime->tm_mon], (logintime->tm_year % 100)); + else + sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday], + hour, am ? "am" : "pm"); + } + else + sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm"); + + return give; +} + diff --git a/top.1 b/top.1 new file mode 100644 index 00000000..7be16674 --- /dev/null +++ b/top.1 @@ -0,0 +1,456 @@ +.\" This file Copyright 1992 Robert J. Nation +.\" (nation@rocket.sanders.lockheed.com) +.\" It may be distributed under the GNU Public License, version 2, or +.\" any higher version. See section COPYING of the GNU Public license +.\" for conditions under which this file may be redistributed. +.\" +.\" Modified 1994/04/25 Michael Shields +.\" Cleaned up, and my changes documented. New `.It' macro. Edited. +.\" modified 1996/01/27 Helmut Geyer to match my changes. +. +.de It +.TP 0.5i +.B "\\$1 " +.. +.TH TOP 1 "Feb 1 1993" "Linux" "Linux User's Manual" +.SH NAME +top \- display top CPU processes +.SH SYNOPSIS +.B top +.RB [ \- ] +.RB [ d +.IR delay ] +.RB [ p +.IR pid ] +.RB [ q ] +.RB [ c ] +.RB [ C ] +.RB [ S ] +.RB [ s ] +.RB [ i ] +.RB [ n +.IR iter ] +.RB [ b ] +.SH DESCRIPTION +.B top +provides an ongoing look at processor activity in real time. It +displays a listing of the most CPU-intensive tasks on the system, and +can provide an interactive interface for manipulating processes. +It can sort the tasks by CPU usage, memory usage and runtime. +.N top +can be better configured than the standard top from the procps suite. +Most features can either be selected by an interactive command or by +specifying the feature in the personal or system-wide configuration +file. See below for more information. +.PP +.SH "COMMAND\-LINE OPTIONS" +.It d +Specifies the delay between screen updates. You can change this with +the +.B s +interactive command. +.It p +Monitor only processes with given process id. +This flag can be given up to twenty times. This option is neither +available interactively nor can it be put into the configuration file. +.It q +This causes +.B top +to refresh without any delay. If the caller has superuser priviledges, +top runs with the highest possible priority. +.It S +Specifies cumulative mode, where each process is listed with the CPU +time that it +.I as well as its dead children +has spent. This is like the +.B -S +flag to +.BR ps (1). +See the discussion below of the +.B S +interactive command. +.It s +Tells +.B top +to run in secure mode. This disables the potentially dangerous of the +interactive commands (see below). A secure +.B top +is a nifty thing to leave running on a spare terminal. +.It i +Start +.B top +ignoring any idle or zombie processes. See the interactive command +.B i +below. +.It C +display total CPU states in addition to individual CPUs. This option +only affects SMP systems. +.It c +display command line instead of the command name only. The default +behaviour has been changed as this seems to be more useful. +.It n +Number of iterations. Update the display this number of times and then exit. +.It b +Batch mode. Useful for sending output from top to other programs or to a file. +In this mode, +.B top +will not accept command line input. It runs until it produces the number of +iterations requested with the +.B n +option or until killed. Output is plain text suitable for display on a dumb +terminal. +. +.SH "FIELD DESCRIPTIONS" +.B top +displays a variety of information about the processor state. The +display is updated every 5 seconds by default, but you can change that +with the +.B d +command-line option or the +.B s +interactive command. +.It "uptime" +This line displays the time the system has been up, and the three load +averages for the system. The load averages are the average number of +process ready to run during the last 1, 5 and 15 minutes. This line is +just like the output of +.BR uptime (1). +The uptime display may be toggled by the interactive +.B l +command. +.It processes +The total number of processes running at the time of the last update. +This is also broken down into the number of tasks which are running, +sleeping, stopped, or undead. The processes and states display may be +toggled by the +.B t +interactive command. +.It "CPU states" +Shows the percentage of CPU time in user mode, system mode, niced tasks, +and idle. (Niced tasks are only those whose nice value is negative.) +Time spent in niced tasks will also be counted in system and user time, +so the total will be more than 100%. The processes and states display +may be +toggled by the +.B t +interactive command. +.It Mem +Statistics on memory usage, including total available memory, free +memory, used memory, shared memory, and memory used for buffers. The +display of memory information may be toggled by the +.B m +interactive command. +.It Swap +Statistics on swap space, including total swap space, available swap +space, and used swap space. This and +.B Mem +are just like the output of +.BR free (1). +.It PID +The process ID of each task. +.It PPID +The parent process ID each task. +.It UID +The user ID of the task's owner. +.It USER +The user name of the task's owner. +.It PRI +The priority of the task. +.It NI +The nice value of the task. Negative nice values are higher priority. +.It SIZE +The size of the task's code plus data plus stack space, in kilobytes, +is shown here. +.It TSIZE +The code size of the task. This gives strange values for kernel +processes and is broken for ELF processes. +.It DSIZE +Data + Stack size. This is broken for ELF processes. +.It TRS +Text resident size. +.It SWAP +Size of the swapped out part of the task. +.It D +Size of pages marked dirty. +.It LC +Last used processor. (That this changes from time to time is not +a bug; Linux intentionally uses weak affinity. Also notice that +the very act of running top may break weak affinity and cause more +processes to change current CPU more often because of the extra +demand for CPU time.) +.It RSS +The total amount of physical memory used by the task, in kilobytes, is +shown here. For ELF processes used library pages are counted here, for +a.out processes not. +.It SHARE +The amount of shared memory used by the task is shown in this column. +.It STAT +The state of the task is shown here. The state is either +.B S +for sleeping, +.B D +for uninterruptible sleep, +.B R +for running, +.B Z +for zombies, or +.B T +for stopped or traced. These states are modified by trailing +.B < +for a process with negative nice value, +.B N +for a process with positive nice value, +.B W +for a swapped out process (this does not work correctly for kernel +processes). +.It WCHAN +depending on the availablity of either +.I /boot/psdatabase +or the kernel link map +.I /boot/System.map +this shows the address or the name of the kernel function the task +currently is sleeping in. +.It TIME +Total CPU time the task has used since it started. If cumulative mode +is on, this also includes the CPU time used by the process's children +which have died. You can set cumulative mode with the +.B S +command line option or toggle it with the interactive command +.BR S . +The header line will then be changed to +.BR CTIME . +.It %CPU +The task's share of the CPU time since the last screen update, expressed +as a percentage of total CPU time per processor. +.It %MEM +The task's share of the physical memory. +.It COMMAND +The task's command name, which will be truncated if it is too long to be +displayed on one line. Tasks in memory will have a full command line, +but swapped-out tasks will only have the name of the program in +parentheses (for example, "(getty)"). +.It "A , WP" +these fields from the kmem top are not supported. +. +.SH "INTERACTIVE COMMANDS" +Several single-key commands are recognized while +.B top +is running. Some are disabled if the +.B s +option has been given on the command line. +.It space +Immediately updates the display. +.It ^L +Erases and redraws the screen. +.It "h\fR or \fB?" +Displays a help screen giving a brief summary of commands, and the +status of secure and cumulative modes. +.It k +Kill a process. You will be prompted for the PID of the task, and the +signal to send to it. For a normal kill, send signal 15. For a sure, +but rather abrupt, kill, send signal 9. The default signal, as with +.BR kill (1), +is 15, +.BR SIGTERM . +This command is not available in secure mode. +.It i +Ignore idle and zombie processes. This is a toggle switch. +.It I +Toggle between Solaris (CPU percentage divided by total number of CPUs) +and Irix (CPU percentage calculated solely by amount of time) views. +This is a toggle switch that affects only SMP systems. +.It "n\fR or \fB#" +Change the number of processes to show. You will be prompted to enter +the number. This overrides automatic determination of the number of +processes to show, which is based on window size measurement. If 0 is +specified, then top will show as many processes as will fit on the +screen; this is the default. +.It q +Quit. +.It r +Re-nice a process. You will be prompted for the PID of the task, and +the value to nice it to. Entering a positve value will cause a process +to be niced to negative values, and lose priority. If root is running +.BR top , +a negative value can be entered, causing a process to get a higher than +normal priority. The default renice value is 10. This command is not +available in secure mode. +.It S +This toggles cumulative mode, the equivalent of +.BR "ps -S" , +i.e., that CPU times will include a process's defunct children. For +some programs, such as compilers, which work by forking into many +seperate tasks, normal mode will make them appear less demanding than +they actually are. For others, however, such as shells and +.BR init , +this behavior is correct. In any case, try cumulative mode for an +alternative view of CPU use. +.It s +Change the delay between updates. You will be prompted to enter the +delay time, in seconds, between updates. Fractional values are +recognized down to microseconds. Entering 0 causes continuous updates. +The default value is 5 seconds. Note that low values cause nearly +unreadably fast displays, and greatly raise the load. This command is +not available in secure mode. +.It "f\fR or \fBF" +Add fields to display or remove fields from the display. See below for +more information. +.It "o\fR or \fBO" +Change order of displayed fields. See below for more information. +.It l +toggle display of load average and uptime information. +.It m +toggle display of memory information. +.It t +toggle display of processes and CPU states information. +.It c +toggle display of command name or full command line. +.It N +sort tasks by pid (\fIn\fPumerically). +.It A +sort tasks by age (newest first). +.It P +sort tasks by CPU usage (default). +.It M +sort tasks by resident memory usage. +.It T +sort tasks by time / cumulative time. +.It W +Write current setup to +.IR ~/.toprc . +This is the recommended way to write a top configuration file. +. +.SH The Field and Order Screens +After pressing +.BR f , +.BR F , +.B o +or +.B O +you will be shown a screen specifying the field order on the top line +and short descriptions of the field contents. The field order string +uses the following syntax: If the letter in the filed string +corresponding to a field is upper case, the field will be displayed. +This is furthermore indicated by an asterisk in front of the field +description. +The order of the fields corresponds to the order of the letters in the +string. + From the field select screen you can toggle the display of a field by +pressing the corresponding letter. + From the order screen you may move a field to the left by pressing +the corresponding upper case letter resp. to the right by pressing the +lower case one. +. +.SH Configuration Files +Top reads it's default configuration from two files, +.I /etc/toprc +and +.IR ~/.toprc . +The global configuration file may be used to restrict the usage of top +to the secure mode for non-priviledged users. If this is desired, the +file should contain a 's' to specify secure mode and a digit d (2<=d<=9) +for the default delay (in seconds) on a single line. +. +The personal configuration file contains two lines. The first line +contains lower and upper letters to specify which fields in what +order are to be displayed. The letters correspond to the letters in the +Fields or Order screens from top. As this is not very instructive, it is +recommended to select fields and order in a running top process and to +save this using the +.I W +interactive command. +. +The second line is more interesting (and important). It contains +information on the other options. Most important, if you have saved a +configuration in secure mode, you will not get an insecure top without +removing the lower 's' from the second line of your +.IR ~/.toprc . +A digit specifies the delay time between updates, a capital 'S' +cumulative mode, a lower 'i' no-idle mode, a capital 'I' Irix view. As +in interactive mode, a lower 'm', 'l', and 't' suppresses the display +of memory, uptime resp. process and CPU state information. +Currently changing the default sorting order (by CPU usage) is not +supported. +. +.SH NOTES +This +.BR proc -based +.B top +works by reading the files in the +.B proc +filesystem, +mounted on +.IR /proc . +If +.I /proc +is not mounted, +.B top +will not work. +.PP +.B %CPU +shows the cputime/realtime percentage in the period of time between +updates. For the first update, a short delay is used, and +.B top +itself dominates the CPU usage. After that, +.B top +will drop back, and a more reliable estimate of CPU usage is available. +.PP +The +.B SIZE +and +.B RSS fields don't count the page tables and the +.B task_struct +of a process; this is at least 12K of memory that is always resident. +.B SIZE +is the virtual size of the process (code+data+stack). +.PP +Keep in mind that a process must die for its time to be recorded on its +parent by cumulative mode. Perhaps more useful behavior would be to +follow each process upwards, adding time, but that would be more +expensive, possibly prohibitively so. In any case, that would make +.BR top 's +behavior incompatible with +.BR ps . +. +.SH FILES +.I /etc/toprc +The global configuration file. +.I ~/.toprc +The personal configuration file. +. +.SH "SEE ALSO" +.BR ps (1), +.BR free (1), +.BR uptime (1), +.BR kill (1), +.BR renice (1). +. +.SH +BUGS +If the window is less than about 70x7, +.B top +will not format information correctly. + Many fields still have problems with ELF processes. + the help screens are not yet optimized for windows with less than +25 lines +. +.SH AUTHOR +.B top +was originally written by Roger Binns, based on Branko Lankester's + ps program. +Robert Nation re-wrote it +significantly to use the proc filesystem, based on Michael K. Johnson's + proc-based ps program. +Michael Shields made many changes, including +secure and cumulative modes and a general cleanup. +Tim Janik added age sorting and the ability to monitor +specific processes through their ids. + +Helmut Geyer +Heavily changed it to include support for configurable fields and other +new options, and did further cleanup and use of the new readproc interface. + +The "b" and "n" options contributed by George Bonser +for CapTech IT Services. + +Please send bug reports to diff --git a/top.c b/top.c new file mode 100644 index 00000000..848bf9b1 --- /dev/null +++ b/top.c @@ -0,0 +1,1746 @@ +/* + * top.c - show top CPU processes + * + * Copyright (c) 1992 Branko Lankester + * Copyright (c) 1992 Roger Binns + * Copyright (c) 1997 Michael K. Johnson + * + * Snarfed and HEAVILY modified in december 1992 for procps + * by Michael K. Johnson, johnsonm@sunsite.unc.edu. + * + * Modified Michael K. Johnson's ps to make it a top program. + * Also borrowed elements of Roger Binns kmem based top program. + * Changes made by Robert J. Nation (nation@rocket.sanders.lockheed.com) + * 1/93 + * + * Modified by Michael K. Johnson to be more efficient in cpu use + * 2/21/93 + * + * Changed top line to use uptime for the load average. Also + * added SIGTSTP handling. J. Cowley, 19 Mar 1993. + * + * Modified quite a bit by Michael Shields (mjshield@nyx.cs.du.edu) + * 1994/04/02. Secure mode added. "d" option added. Argument parsing + * improved. Switched order of tick display to user, system, nice, idle, + * because it makes more sense that way. Style regularized (to K&R, + * more or less). Cleaned up much throughout. Added cumulative mode. + * Help screen improved. + * + * Fixed kill buglet brought to my attention by Rob Hooft. + * Problem was mixing of stdio and read()/write(). Added + * getnum() to solve problem. + * 12/30/93 Michael K. Johnson + * + * Added toggling output of idle processes via 'i' key. + * 3/29/94 Gregory K. Nickonov + * + * Fixed buglet where rawmode wasn't getting restored. + * Added defaults for signal to send and nice value to use. + * 5/4/94 Jon Tombs. + * + * Modified 1994/04/25 Michael Shields + * Merged previous changes to 0.8 into 0.95. + * Allowed the use of symbolic names (e.g., "HUP") for signal input. + * Rewrote getnum() into getstr(), getint(), getsig(), etc. + * + * Modified 1995 Helmut Geyer + * added kmem top functionality (configurable fields) + * configurable order of process display + * Added options for dis/enabling uptime, statistics, and memory info. + * fixed minor bugs for ELF systems (e.g. SIZE, RSS fields) + * + * Modified 1996/05/18 Helmut Geyer + * Use of new interface and general cleanup. The code should be far more + * readable than before. + * + * Modified 1996/06/25 Zygo Blaxell + * Added field scaling code for programs that run more than two hours or + * take up more than 100 megs. We have lots of both on our production line. + * + * Modified 1998/02/21 Kirk Bauer + * Added the 'u' option to display only a selected user... plus it will + * take into account that not all 20 top processes are actually shown, + * so it can fit more onto the screen. I think this may help the + * 'don't show idle' mode, but I'm not sure. + * + * Modified 1997/07/27 & 1999/01/27 Tim Janik + * added `-p' option to display specific process ids. + * process sorting is by default disabled in this case. + * added `N' and `A' keys to sort the tasks Numerically by pid or + * sort them by Age (newest first). + * + * Modified 1999/10/22 Tim Janik + * miscellaneous minor fixes, including "usage: ..." output for + * unrecognized options. + * + * Modified 2000/02/07 Jakub Jelinek + * Only load System.map when we are going to display WCHAN. + * Show possible error messages from that load using SHOWMESSAGE. + * + * Modified 2000/07/10 Michael K. Johnson + * Integrated a patch to display SMP information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proc/sysinfo.h" +#include "proc/procps.h" +#include "proc/whattime.h" +#include "proc/sig.h" +#include "proc/version.h" +#include "proc/readproc.h" +#include "proc/status.h" +#include "proc/devname.h" +#include "proc/compare.h" + +#define PUTP(x) (tputs(x,1,putchar)) +#define BAD_INPUT -30 + +#include "top.h" /* new header for top specific things */ + +static int *cpu_mapping; +static int nr_cpu; + +/*####################################################################### + *#### Startup routines: parse_options, get_options, ############## + *#### setup_terminal and main ############## + *####################################################################### + */ + + /* + * parse the options string as read from the config file(s). + * if top is in secure mode, disallow changing of the delay time between + * screen updates. + */ +static void parse_options(char *Options, int secure) +{ + int i; + for (i = 0; i < strlen(Options); i++) { + switch (Options[i]) { + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (!secure) + Sleeptime = (float) Options[i] - '0'; + break; + case 'S': + Cumulative = 1; + headers[22][1] = 'C'; + break; + case 's': + Secure = 1; + break; + case 'i': + Noidle = 1; + break; + case 'm': + show_memory = 0; + header_lines -= 2; + break; + case 'M': + sort_type = S_MEM; + reset_sort_options(); + register_sort_function( -1, (cmp_t)mem_sort); + break; + case 'l': + show_loadav = 0; + header_lines -= 1; + break; + case 'P': + sort_type = S_PCPU; + reset_sort_options(); + register_sort_function( -1, (cmp_t)pcpu_sort); + break; + case 'N': + sort_type = S_NONE; + reset_sort_options(); + break; + case 'A': + sort_type = S_AGE; + reset_sort_options(); + register_sort_function( -1, (cmp_t)age_sort); + break; + case 't': + show_stats = 0; + header_lines -= 2; + break; + case 'T': + sort_type = S_TIME; + reset_sort_options(); + register_sort_function( -1, (cmp_t)time_sort); + break; + case 'c': + show_cmd = 0; + break; + case '\n': + break; + case 'I': + Irixmode = 0; + break; + default: + fprintf(stderr, "Wrong configuration option %c\n", i); + exit(1); + break; + } + } +} + +/* + * Read the configuration file(s). There are two files, once SYS_TOPRC + * which should only contain the secure switch and a sleeptime + * value iff ordinary users are to use top in secure mode only. + * + * The other file is $HOME/RCFILE. + * The configuration file should contain two lines (any of which may be + * empty). The first line specifies the fields that are to be displayed + * in the order you want them to. Uppercase letters specify fields + * displayed by default, lowercase letters specify fields not shown by + * default. The order of the letters in this line corresponds to the + * order of the displayed fileds. + * + * all Options but 'q' can be read from this config file + * The delay time option syntax differs from the commandline syntax: + * only integer values between 2 and 9 seconds are recognized + * (this is for standard configuration, so I think this should do). + * + * usually this file is not edited by hand, but written from top using + * the 'W' command. + */ + +static void get_options(void) +{ + FILE *fp; + char *pt; + char rcfile[MAXNAMELEN]; + char Options[256] = ""; + int i; + + nr_cpu = sysconf (_SC_NPROCESSORS_ONLN); + cpu_mapping = (int *) xmalloc (sizeof (int) * nr_cpu); + /* read cpuname */ + for (i=0; i< nr_cpu; i++) cpu_mapping[i]=i; + header_lines = 6 + nr_cpu; + strcpy(rcfile, SYS_TOPRC); + fp = fopen(rcfile, "r"); + if (fp != NULL) { + fgets(Options, 254, fp); + fclose(fp); + } + parse_options(Options, 0); + strcpy(Options, ""); + if (getenv("HOME")) { + strcpy(rcfile, getenv("HOME")); + strcat(rcfile, "/"); + } + strcat(rcfile, RCFILE); + fp = fopen(rcfile, "r"); + if (fp == NULL) { + strcpy(Fields, DEFAULT_SHOW); + } else { + if (fgets(Fields, 254, fp) != NULL) { + pt = strchr(Fields, '\n'); + if (pt) *pt = 0; + } + fgets(Options, 254, fp); + fclose(fp); + } + parse_options(Options, getuid()? Secure : 0); +} + +/* + * Set up the terminal attributes. + */ +static void setup_terminal(void) +{ + char *termtype; + struct termios newtty; + if (!Batch) + termtype = getenv("TERM"); + else + termtype = "dumb"; + if (!termtype) { + /* In theory, $TERM should never not be set, but in practice, + some gettys don't. Fortunately, vt100 is nearly always + correct (or pretty close). */ + termtype = "VT100"; + /* fprintf(stderr, PROGNAME ": $TERM not set\n"); */ + /* exit(1); */ + } + + /* + * Get termcap entries and window size. + */ + if(tgetent(NULL, termtype) != 1) { + fprintf(stderr, PROGNAME ": Unknown terminal \"%s\" in $TERM\n", + termtype); + exit(1); + } + + cm = tgetstr("cm", 0); + top_clrtobot = tgetstr("cd", 0); + cl = tgetstr("cl", 0); + top_clrtoeol = tgetstr("ce", 0); + ho = tgetstr("ho", 0); + md = tgetstr("md", 0); + mr = tgetstr("mr", 0); + me = tgetstr("me", 0); + + + if (Batch) return; /* the rest doesn't apply to batch mode */ + if (tcgetattr(0, &Savetty) == -1) { + perror(PROGNAME ": tcgetattr() failed"); + error_end(errno); + } + newtty = Savetty; + newtty.c_lflag &= ~ICANON; + newtty.c_lflag &= ~ECHO; + newtty.c_cc[VMIN] = 1; + newtty.c_cc[VTIME] = 0; + if (tcsetattr(0, TCSAFLUSH, &newtty) == -1) { + printf("cannot put tty into raw mode\n"); + error_end(1); + } + tcgetattr(0, &Rawtty); +} + +static int parseint(const char *src, const char *err) +{ + char *endp; + int num; + int len; + num = strtol(src, &endp, 0); + if (*endp == '\0') return num; + /* also accept prefixes of: infinite, infinity, maximum, all */ + len = strlen(src); + if(len<1) goto fail; + if(len<9 && !strncmp(src,"infinite",len)) return INT_MAX; + if(len<9 && !strncmp(src,"infinity",len)) return INT_MAX; + if(len<8 && !strncmp(src,"maximum" ,len)) return INT_MAX; + if(len<4 && !strncmp(src,"all" ,len)) return INT_MAX; +fail: + fprintf(stderr, err, src); + exit(1); +} + +static double parseflt(const char *src, const char *err) +{ + char *endp; + double num; + int len; + num = strtod(src, &endp); + if (*endp == '\0') return num; + /* also accept prefixes of: infinite, infinity, maximum, all */ + len = strlen(src); + if(len<1) goto fail; + if(len<9 && !strncmp(src,"infinite",len)) return (double)INT_MAX; + if(len<9 && !strncmp(src,"infinity",len)) return (double)INT_MAX; + if(len<8 && !strncmp(src,"maximum" ,len)) return (double)INT_MAX; + if(len<4 && !strncmp(src,"all" ,len)) return (double)INT_MAX; +fail: + fprintf(stderr, err, src); + exit(1); +} + +int main(int argc, char **argv) +{ + /* For select(2). */ + struct timeval tv; + fd_set in; + /* For parsing arguments. */ + char *cp; + /* The key read in. */ + char c; + + struct sigaction sact; + + setlocale(LC_ALL, ""); + get_options(); + + /* set to PCPU sorting */ + register_sort_function( -1, (cmp_t)pcpu_sort); + + /* + * Parse arguments. + */ + (void)argc; + argv++; + while (*argv) { + cp = *argv++; + while (*cp) { + switch (*cp) { + case 'd': + if (cp[1]) { + Sleeptime = parseflt(++cp, PROGNAME ": Bad delay time %s'\n"); + goto breakargv; + } else if (*argv) { /* last char in an argv, use next as arg */ + Sleeptime = parseflt(cp = *argv++, PROGNAME ": Bad delay time %s'\n"); + goto breakargv; + } else { + fprintf(stderr, "-d requires an argument\n"); + exit(1); + } + break; + case 'n': + if (cp[1]) { + Loops = parseint(++cp, PROGNAME ": Bad value %s'\n"); + goto breakargv; + } else if (*argv) { /* last char in an argv, use next as arg */ + Loops = parseint(cp = *argv++, PROGNAME ": Bad value %s'\n"); + goto breakargv; + } + break; + + case 'q': + if (!getuid()) + /* set priority to -10 in order to stay above kswapd */ + if (setpriority(PRIO_PROCESS, getpid(), -10)) { + /* We check this just for paranoia. It's not + fatal, and shouldn't happen. */ + perror(PROGNAME ": setpriority() failed"); + } + Sleeptime = 0; + break; + case 'p': + if (monpids_index >= monpids_max) { + fprintf(stderr, PROGNAME ": More than %u process ids specified\n", + monpids_max); + exit(1); + } + if (cp[1]) { + if (sscanf(++cp, "%d", &monpids[monpids_index]) != 1 || + monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) { + fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp); + exit(1); + } + } else if (*argv) { /* last char in an argv, use next as arg */ + if (sscanf(cp = *argv++, "%d", &monpids[monpids_index]) != 1 || + monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) { + fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp); + exit(1); + } + } else { + fprintf(stderr, "-p requires an argument\n"); + exit(1); + } + if (!monpids[monpids_index]) + monpids[monpids_index] = getpid(); + /* default to no sorting when monitoring process ids */ + if (!monpids_index++) { + sort_type = S_NONE; + reset_sort_options(); + } + cp = "_"; + break; + case 'b': + Batch = 1; + break; + case 'c': + show_cmd = !show_cmd; + break; + case 'S': + Cumulative = 1; + break; + case 'i': + Noidle = 1; + break; + case 's': + Secure = 1; + break; + case 'C': + CPU_states = 1; + break; + case '-': + break; /* Just ignore it */ + case 'v': + case 'V': + fprintf(stdout, "top (%s)\n", procps_version); + exit(0); + case 'h': + fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n"); + exit(0); + default: + fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp); + fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n"); + exit(1); + } + cp++; + } + breakargv: + } + + if (nr_cpu > 1 && CPU_states) + header_lines++; + + meminfo(); /* need kb_main_total value filled in */ + + setup_terminal(); + window_size(0); + /* + * Set up signal handlers. + */ + sact.sa_handler = end; + sact.sa_flags = 0; + sigemptyset(&sact.sa_mask); + sigaction(SIGHUP, &sact, NULL); + sigaction(SIGINT, &sact, NULL); + sigaction(SIGQUIT, &sact, NULL); + sact.sa_handler = stop; + sact.sa_flags = SA_RESTART; + sigaction(SIGTSTP, &sact, NULL); + sact.sa_handler = window_size; + sigaction(SIGWINCH, &sact, NULL); + sigaction(SIGCONT, &sact, NULL); + + /* loop, collecting process info and sleeping */ + while (1) { + if (Loops > 0) + Loops--; + /* display the tasks */ + show_procs(); + /* sleep & wait for keyboard input */ + if (Loops == 0) + end(0); + if (!Batch) + { + tv.tv_sec = Sleeptime; + tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000; + FD_ZERO(&in); + FD_SET(0, &in); + if (select(1, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1) + do_key(c); + } else { + sleep(Sleeptime); + } + } +} + +/*####################################################################### + *#### Signal handled routines: error_end, end, stop, window_size ### + *#### Small utilities: make_header, getstr, getint, getfloat, getsig ### + *####################################################################### + */ + + + /* + * end when exiting with an error. + */ +static void error_end(int rno) +{ + if (!Batch) + tcsetattr(0, TCSAFLUSH, &Savetty); + PUTP(tgoto(cm, 0, Lines - 1)); + fputs("\r\n", stdout); + exit(rno); +} +/* + * Normal end of execution. + */ +static void end(int signo) +{ + (void)signo; + if (!Batch) + tcsetattr(0, TCSAFLUSH, &Savetty); + PUTP(tgoto(cm, 0, Lines - 1)); + fputs("\r\n", stdout); + exit(0); +} + +/* + * SIGTSTP catcher. + */ +static void stop(int signo) +{ + (void)signo; + /* Reset terminal. */ + if (!Batch) + tcsetattr(0, TCSAFLUSH, &Savetty); + PUTP(tgoto(cm, 0, Lines - 3)); + fflush(stdout); + raise(SIGSTOP); + /* Later... */ + if (!Batch) + tcsetattr (0, TCSAFLUSH, &Rawtty); +} + +/* + * Reads the window size and clear the window. This is called on setup, + * and also catches SIGWINCHs, and adjusts Maxlines. Basically, this is + * the central place for window size stuff. + */ +static void window_size(int signo) +{ + struct winsize ws; + (void)signo; + if((ioctl(1, TIOCGWINSZ, &ws) != -1) && (ws.ws_col>73) && (ws.ws_row>7)){ + Cols = ws.ws_col; + Lines = ws.ws_row; + }else{ + Cols = tgetnum("co"); + Lines = tgetnum("li"); + } + if (!Batch) + clear_screen(); + /* + * calculate header size, length of cmdline field ... + */ + Numfields = make_header(); +} + +/* + * this prints a possible message from open_psdb_message + */ +static void top_message(const char *format, ...) { + va_list arg; + int n; + char buffer[512]; + + va_start (arg, format); + n = vsnprintf (buffer, 512, format, arg); + va_end (arg); + if (n > -1 && n < 512) + SHOWMESSAGE(("%s", buffer)); +} + +/* + * this adjusts the lines needed for the header to the current value + */ +static int make_header(void) +{ + int i, j; + + j = 0; + for (i = 0; i < strlen(Fields); i++) { + if (Fields[i] < 'a') { + pflags[j++] = Fields[i] - 'A'; + if (Fields[i] == 'U' && CL_wchan_nout == -1) { + CL_wchan_nout = 0; + /* for correct handling of WCHAN fields, we have to do distingu + * between kernel versions */ + /* get kernel symbol table, if needed */ + if (open_psdb_message(NULL, top_message)) { + CL_wchan_nout = 1; + } else { + psdbsucc = 1; + } + } + } + } + strcpy(Header, ""); + for (i = 0; i < j; i++) + strcat(Header, headers[pflags[i]]); + /* readjust window size ... */ + Maxcmd = Cols - strlen(Header) + 7; + Maxlines = Display_procs ? Display_procs : Lines - header_lines; + if (Maxlines > Lines - header_lines) + Maxlines = Lines - header_lines; + return (j); +} + + + +/* + * Get a string from the user; the base of getint(), et al. This really + * ought to handle long input lines and errors better. NB: The pointer + * returned is a statically allocated buffer, so don't expect it to + * persist between calls. + */ +static char *getstr(void) +{ + static char line[BUFSIZ]; /* BUFSIZ from ; arbitrary */ + int i = 0; + + /* Must make sure that buffered IO doesn't kill us. */ + fflush(stdout); + fflush(stdin); /* Not POSIX but ok */ + + do { + read(STDIN_FILENO, &line[i], 1); + } while (line[i++] != '\n' && i < sizeof(line)); + line[--i] = 0; + + return (line); +} + + +/* + * Get an integer from the user. Display an error message and + * return BAD_INPUT if it's invalid; else return the number. + */ +static int getint(void) +{ + char *line; + int i; + int r; + + line = getstr(); + + for (i = 0; line[i]; i++) { + if (!isdigit(line[i]) && line[i] != '-') { + SHOWMESSAGE(("That's not a number!")); + return (BAD_INPUT); + } + } + + /* An empty line is a legal error (hah!). */ + if (!line[0]) + return (BAD_INPUT); + + sscanf(line, "%d", &r); + return (r); +} + + +/* + * Get a float from the user. Just like getint(). + */ +static float getfloat(void) +{ + char *line; + int i; + float r; + + line = getstr(); + + for (i = 0; line[i]; i++) { + if (!isdigit(line[i]) && line[i] != '.' && line[i] != '-') { + SHOWMESSAGE(("That's not a float!")); + return (BAD_INPUT); + } + } + + /* An empty line is a legal error (hah!). */ + if (!line[0]) + return (BAD_INPUT); + + sscanf(line, "%f", &r); + return (r); +} + + +/* + * Get a signal number or name from the user. Return the number, or -1 + * on error. + */ +static int getsig(void) +{ + char *line; + + /* This is easy. */ + line = getstr(); + return signal_name_to_number(line); +} + +/*####################################################################### + *#### Routine for sorting on used time, resident memory and %CPU ##### + *#### It would be easy to include full sorting capability as in ##### + *#### ps, but I think there is no real use for something that ##### + *#### complicated. Using register_sort_function or parse_sort_opt ##### + *#### you just have to do the natural thing and it will work. ##### + *####################################################################### + */ + +static int time_sort (proc_t **P, proc_t **Q) +{ + if (Cumulative) { + if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) < + ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return -1; + if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) > + ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) + return 1; + } else { + if( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime)) + return -1; + if( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime)) + return 1; + } + return 0; +} + +static int pcpu_sort (proc_t **P, proc_t **Q) +{ + if( (*P)->pcpu < (*Q)->pcpu ) return -1; + if( (*P)->pcpu > (*Q)->pcpu ) return 1; + return 0; +} + +static int mem_sort (proc_t **P, proc_t **Q) +{ + if( (*P)->vm_rss < (*Q)->vm_rss ) return -1; + if( (*P)->vm_rss > (*Q)->vm_rss ) return 1; + return 0; +} + +int age_sort (proc_t **P, proc_t **Q) +{ + if( (*P)->start_time < (*Q)->start_time ) return -1; + if( (*P)->start_time > (*Q)->start_time ) return 1; + return 0; +} + +/*####################################################################### + *#### Routines handling the field selection/ordering screens: ######## + *#### show_fields, change_order, change_fields ######## + *####################################################################### + */ + + /* + * Display the specification line of all fields. Upper case indicates + * a displayed field, display order is according to the order of the + * letters. A short description of each field is shown as well. + * The description of a displayed field is marked by a leading + * asterisk (*). + */ +static void show_fields(void) +{ + int i, row, col; + char *p; + + clear_screen(); + PUTP(tgoto(cm, 3, 0)); + printf("Current Field Order: %s\n", Fields); + for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) { + row = i % (Lines - 3) + 3; + col = i / (Lines - 3) * 40; + PUTP(tgoto(cm, col, row)); + for (p = headers[i]; *p == ' '; ++p); + printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A', + p, headers2[i]); + } +} + +/* + * change order of displayed fields + */ +static void change_order(void) +{ + char c, ch, *p; + int i; + + show_fields(); + for (;;) { + PUTP(tgoto(cm, 0, 0)); + PUTP(top_clrtoeol); + PUTP(tgoto(cm, 3, 0)); + PUTP(mr); + printf("Current Field Order: %s", Fields); + PUTP(me); + putchar('\n'); + PUTP(tgoto(cm, 0, 1)); + printf("Upper case characters move a field to the left, lower case to the right"); + fflush(stdout); + if (!Batch) { /* should always be true, but... */ + tcsetattr(0, TCSAFLUSH, &Rawtty); + read(0, &c, 1); + tcsetattr(0, TCSAFLUSH, &Savetty); + } + i = toupper(c) - 'A'; + if ((p = strchr(Fields, i + 'A')) != NULL) { + if (isupper(c)) + p--; + if ((p[1] != '\0') && (p >= Fields)) { + ch = p[0]; + p[0] = p[1]; + p[1] = ch; + } + } else if ((p = strchr(Fields, i + 'a')) != NULL) { + if (isupper(c)) + p--; + if ((p[1] != '\0') && (p >= Fields)) { + ch = p[0]; + p[0] = p[1]; + p[1] = ch; + } + } else { + break; + } + } + Numfields = make_header(); +} +/* + * toggle displayed fields + */ +static void change_fields(void) +{ + int i, changed = 0; + int row, col; + char c, *p; + char tmp[2] = " "; + + show_fields(); + for (;;) { + PUTP(tgoto(cm, 0, 0)); + PUTP(top_clrtoeol); + PUTP(tgoto(cm, 3, 0)); + PUTP(mr); + printf("Current Field Order: %s", Fields); + PUTP(me); + putchar('\n'); + PUTP(tgoto(cm, 0, 1)); + if (!Batch) { /* should always be true, but... */ + printf("Toggle fields with a-x, any other key to return: "); + fflush(stdout); + tcsetattr(0, TCSAFLUSH, &Rawtty); + read(0, &c, 1); + tcsetattr(0, TCSAFLUSH, &Savetty); + } + i = toupper(c) - 'A'; + if (i >= 0 && i < sizeof headers / sizeof headers[0]) { + row = i % (Lines - 3) + 3; + col = i / (Lines - 3) * 40; + PUTP(tgoto(cm, col, row)); + if ((p = strchr(Fields, i + 'A')) != NULL) { /* deselect Field */ + *p = i + 'a'; + putchar(' '); + } else if ((p = strchr(Fields, i + 'a')) != NULL) { /* select previously */ + *p = i + 'A'; /* deselected field */ + putchar('*'); + } else { /* select new field */ + tmp[0] = i + 'A'; + strcat(Fields, tmp); + putchar('*'); + } + changed = 1; + fflush(stdout); + } else + break; + } + if (changed) + Numfields = make_header(); +} + +/* Do the scaling stuff: interprets time in seconds, formats it to + * fit width, and returns pointer to static char*. + */ +static char *scale_time(int t,int width) +{ + static char buf[100]; + + /* Try successively higher units until it fits */ + + sprintf(buf,"%d:%02d",t/60,t%60); /* minutes:seconds */ + if (strlen(buf)<=width) + return buf; + + t/=60; /* minutes */ + sprintf(buf,"%dm",t); + if (strlen(buf)<=width) + return buf; + + t/=60; /* hours */ + sprintf(buf,"%dh",t); + if (strlen(buf)<=width) + return buf; + + t/=24; /* days */ + sprintf(buf,"%dd",t); + if (strlen(buf)<=width) + return buf; + + t/=7; /* weeks */ + sprintf(buf,"%dw",t); + return buf; /* this is our last try; + if it still doesn't fit, too bad. */ + + /* FIXME: if someone has a 16-way SMP running over a year... */ +} + +/* scale_k(k,width,unit) - interprets k as a count, formats to fit width. + if unit is 0, k is a byte count; 1 is a kilobyte + count; 2 for megabytes; 3 for gigabytes. +*/ + +static char *scale_k(int k,int width,int unit) +{ + /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */ + static double scale[]={1024,1024*1024,1024*1024*1024,0}; + /* kilo, mega, giga, tera */ + static char unitletters[]={'K','M','G','T',0}; + static char buf[100]; + char *up; + double *dp; + + /* Try successively higher units until it fits */ + + sprintf(buf,"%d",k); + if (strlen(buf)<=width) + return buf; + + for (up=unitletters+unit,dp=scale ; *dp ; ++dp,++up) { + sprintf(buf,"%.1f%c",k / *dp,*up); + if (strlen(buf)<=width) + return buf; + sprintf(buf,"%d%c",(int)(k / *dp),*up); + if (strlen(buf)<=width) + return buf; + } + + /* Give up; give them what we got on our shortest attempt */ + return buf; +} + +/* + *####################################################################### + *#### Routines handling the main top screen: ######## + *#### show_task_info, show_procs, show_memory, do_stats ######## + *####################################################################### + */ + /* + * Displays infos for a single task + */ +static void show_task_info(proc_t *task) +{ + int i,j; + unsigned int t; + char *cmdptr; + char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p; + + for (i = 0; i < Numfields; i++) { + tmp[0] = 0; + switch (pflags[i]) { + case P_PID: + sprintf(tmp, "%5d ", task->pid); + break; + case P_PPID: + sprintf(tmp, "%5d ", task->ppid); + break; + case P_EUID: + sprintf(tmp, "%4d ", task->euid); + break; + case P_EUSER: + sprintf(tmp, "%-8.8s ", task->euser); + break; + case P_PCPU: + sprintf(tmp, "%4.1f ", (float)task->pcpu / 10); + break; + case P_LCPU: + sprintf(tmp, "%2d ", task->processor); + break; + case P_PMEM: { + unsigned pmem; + pmem = task->vm_rss * 1000ULL / kb_main_total; + if (pmem > 999) pmem = 999; + sprintf(tmp, "%2u.%u ", pmem/10U, pmem%10U); + } + break; + case P_TTY: { + char outbuf[9]; + dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV); + sprintf(tmp, "%-8.8s ", outbuf); + } + break; + case P_PRI: + sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0)); + break; + case P_NICE: + sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0)); + break; + case P_PAGEIN: + sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0)); + break; + case P_TSIZ: + sprintf(tmp, "%5.5s ", + scale_k(((task->end_code - task->start_code) / 1024), 5, 1)); + break; + case P_DSIZ: + sprintf(tmp, "%5.5s ", + scale_k(((task->vsize - task->end_code) / 1024), 5, 1)); + break; + case P_SIZE: + sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1)); + break; + case P_TRS: + sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1)); + break; + case P_SWAP: + sprintf(tmp, "%4.4s ", + scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1)); + break; + case P_SHARE: + sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1)); + break; + case P_A: + sprintf(tmp, "%3.3s ", "NYI"); + break; + case P_WP: + sprintf(tmp, "%3.3s ", "NYI"); + break; + case P_DT: + sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0)); + break; + case P_RSS: /* rss, not resident (which includes IO memory) */ + sprintf(tmp, "%4.4s ", + scale_k((task->rss << CL_pg_shift), 4, 1)); + break; + case P_WCHAN: + if (!CL_wchan_nout) + sprintf(tmp, "%-9.9s ", wchan(task->wchan)); + else + sprintf(tmp, "%-9lx", task->wchan); + break; + case P_STAT: + sprintf(tmp, "%-4.4s ", status(task)); + break; + case P_TIME: + t = (task->utime + task->stime) / Hertz; + if (Cumulative) + t += (task->cutime + task->cstime) / Hertz; + sprintf(tmp, "%6.6s ", scale_time(t,6)); + break; + case P_COMMAND: + if (!show_cmd && task->cmdline && *(task->cmdline)) { + j=0; + while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){ +/* #if 0 */ /* This is useless? FIXME */ + if (j > 0) + strcat(tmp3, " "); +/* #endif */ + strncat(tmp3, (task->cmdline)[j], 1000); + j++; + } + cmdptr = tmp3; + } else { + cmdptr = task->cmd; + } + if (strlen(cmdptr) > Maxcmd) + cmdptr[Maxcmd - 1] = 0; + sprintf(tmp, "%s", cmdptr); + tmp3[0]=0; + break; + case P_FLAGS: + sprintf(tmp, "%8lx ", task->flags); + break; + } + strcat(tmp2, tmp); + } + if (strlen(tmp2) > Cols - 1) + tmp2[Cols - 1] = 0; + + /* take care of cases like: + perl -e 'foo + bar foo bar + foo + # end of perl script' + */ + for (p=tmp2;*p;++p) + if (!isgraph(*p)) + *p=' '; + + printf("\n%s", tmp2); + PUTP(top_clrtoeol); +} + +/* + * This is the real program! Read process info and display it. + * One could differentiate options of readproctable2, perhaps it + * would be useful to support the PROC_UID and PROC_TTY + * as command line options. + */ +static void show_procs(void) +{ + static proc_t **p_table=NULL; + static int proc_flags; + int count; + int ActualLines; + float elapsed_time; + static int first=0; + + if (first==0) { + proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR|PROC_FILLSTATUS|PROC_FILLSTAT; + if (monpids_index) + proc_flags |= PROC_PID; + p_table=readproctab2(proc_flags, p_table, monpids); + elapsed_time = get_elapsed_time(); + do_stats(p_table, elapsed_time, 0); + sleep(1); + first=1; + } + if (first && Batch) + fputs("\n\n",stdout); + /* Display the load averages. */ + PUTP(ho); + PUTP(md); + if (show_loadav) { + printf("%s", sprint_uptime()); + PUTP(top_clrtoeol); + putchar('\n'); + } + p_table=readproctab2(proc_flags, p_table, monpids); + /* Immediately find out the elapsed time for the frame. */ + elapsed_time = get_elapsed_time(); + /* Display the system stats, calculate percent CPU time + * and sort the list. */ + do_stats(p_table, elapsed_time,1); + /* Display the memory and swap space usage. */ + show_meminfo(); + if (strlen(Header) + 2 > Cols) + Header[Cols - 2] = 0; + PUTP(mr); + fputs(Header, stdout); + PUTP(top_clrtoeol); + PUTP(me); + + /* + * Finally! Loop through to find the top task, and display it. + * Lather, rinse, repeat. + */ + count = 0; + ActualLines = 0; + while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) { + char Stat; + + Stat = p_table[count]->state; + + if ( (!Noidle || (Stat != 'S' && Stat != 'Z')) && + ( (CurrUser[0] == '\0') || + (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) { + + /* + * Show task info. + */ + show_task_info(p_table[count]); + if (!Batch) + ActualLines++; + } + count++; + } + PUTP(top_clrtobot); + PUTP(tgoto(cm, 0, header_lines - 2)); + fflush(stdout); +} + + +/* + * Finds the current time (in microseconds) and calculates the time + * elapsed since the last update. This is essential for computing + * percent CPU usage. + */ +static float get_elapsed_time(void) +{ + struct timeval t; + static struct timeval oldtime; + struct timezone timez; + float elapsed_time; + + gettimeofday(&t, &timez); + elapsed_time = (t.tv_sec - oldtime.tv_sec) + + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0; + oldtime.tv_sec = t.tv_sec; + oldtime.tv_usec = t.tv_usec; + return (elapsed_time); +} + +/* + * Reads the memory info and displays it. Returns the total memory + * available, for use in percent memory usage calculations. + */ +static void show_meminfo(void) +{ + meminfo(); /* read+parse /proc/meminfo */ + if (show_memory) { + printf( + "Mem: %8dK total, %8dK used, %8dK free, %8dK buffers", + kb_main_total, + kb_main_used, + kb_main_free, + kb_main_buffers + ); + PUTP(top_clrtoeol); + putchar('\n'); + printf( + "Swap: %8dK total, %8dK used, %8dK free, %8dK cached", + kb_swap_total, + kb_swap_used, + kb_swap_free, + kb_main_cached + ); + PUTP(top_clrtoeol); + putchar('\n'); + } + PUTP(me); + PUTP(top_clrtoeol); + putchar('\n'); +} + + +/* + * Calculates the number of tasks in each state (running, sleeping, etc.). + * Calculates the CPU time in each state (system, user, nice, etc). + * Calculates percent cpu usage for each task. + */ +static void do_stats(proc_t** p, float elapsed_time, int pass) +{ + proc_t *this; + int arrindex, total_time, cpumap, i, n = 0; + int sleeping = 0, stopped = 0, zombie = 0, running = 0; + double system_ticks, user_ticks, nice_ticks, idle_ticks; + static int prev_count = 0; + int systime, usrtime; + + /* start with one page as a reasonable allocate size */ + static int save_history_size = + sizeof(long)*1024 / sizeof(struct save_hist); + static struct save_hist *save_history; + struct save_hist *New_save_hist; + + if (!save_history) + save_history = xcalloc(NULL, sizeof(struct save_hist)*save_history_size); + New_save_hist = xcalloc(NULL, sizeof(struct save_hist)*save_history_size); + + /* + * Make a pass through the data to get stats. + */ + arrindex = 0; + while (p[n]->pid != -1) { + this = p[n]; + switch (this->state) { + case 'S': + case 'D': + sleeping++; + break; + case 'T': + stopped++; + break; + case 'Z': + zombie++; + break; + case 'R': + running++; + break; + default: + /* Don't know how to handle this one. */ + break; + } + + /* + * Calculate time in this process. Time is sum of user time + * (usrtime) plus system time (systime). + */ + total_time = this->utime + this->stime; + if (arrindex >= save_history_size) { + save_history_size *= 2; + save_history = xrealloc(save_history, sizeof(struct save_hist)*save_history_size); + New_save_hist = xrealloc(New_save_hist, sizeof(struct save_hist)*save_history_size); + } + New_save_hist[arrindex].ticks = total_time; + New_save_hist[arrindex].pid = this->pid; + systime = this->stime; + usrtime = this->utime; + New_save_hist[arrindex].stime = systime; + New_save_hist[arrindex].utime = usrtime; + + /* find matching entry from previous pass */ + for (i = 0; i < prev_count; i++) { + if (save_history[i].pid == this->pid) { + total_time -= save_history[i].ticks; + systime -= save_history[i].stime; + usrtime -= save_history[i].utime; + + i = prev_count; + } + } + + /* + * Calculate percent cpu time for this task. + */ + this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time; + if (this->pcpu > 999) + this->pcpu = 999; + + arrindex++; + n++; + } + + /* + * Display stats. + */ + if (pass > 0 && show_stats) { + printf("%d processes: %d sleeping, %d running, %d zombie, " + "%d stopped", + n, sleeping, running, zombie, stopped); + PUTP(top_clrtoeol); + putchar('\n'); + four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks); + printf("CPU states:" + " %# 5.1f%% user, %# 5.1f%% system," + " %# 5.1f%% nice, %# 5.1f%% idle", + user_ticks, + system_ticks, + nice_ticks, + idle_ticks + ); + PUTP(top_clrtoeol); + putchar('\n'); + } + /* + * Save this frame's information. + */ + for (i = 0; i < n; i++) { + /* copy the relevant info for the next pass */ + save_history[i].pid = New_save_hist[i].pid; + save_history[i].ticks = New_save_hist[i].ticks; + save_history[i].stime = New_save_hist[i].stime; + save_history[i].utime = New_save_hist[i].utime; + } + free(New_save_hist); + + prev_count = n; + qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp); +} + + +/* + * Process keyboard input during the main loop + */ +static void do_key(char c) +{ + int numinput, i; + char rcfile[MAXNAMELEN]; + FILE *fp; + + /* + * First the commands which don't require a terminal mode switch. + */ + if (c == 'q') + end(0); + else if (c == ' ') + return; + else if (c == 12) { + clear_screen(); + return; + } else if (c == 'I') { + Irixmode=(Irixmode) ? 0 : 1; + return; + } + + /* + * Switch the terminal to normal mode. (Will the original + * attributes always be normal? Does it matter? I suppose the + * shell will be set up the way the user wants it.) + */ + if (!Batch) tcsetattr(0, TCSANOW, &Savetty); + + /* + * Handle the rest of the commands. + */ + switch (c) { + case '?': + case 'h': + PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr); + printf("Proc-Top Revision 1.2"); + PUTP(me); putchar('\n'); + printf("Secure mode "); + PUTP(md); + fputs(Secure ? "on" : "off", stdout); + PUTP(me); + fputs("; cumulative mode ", stdout); + PUTP(md); + fputs(Cumulative ? "on" : "off", stdout); + PUTP(me); + fputs("; noidle mode ", stdout); + PUTP(md); + fputs(Noidle ? "on" : "off", stdout); + PUTP(me); + fputs("\n\n", stdout); + printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN); + if (!Batch) tcsetattr(0, TCSANOW, &Rawtty); + (void) getchar(); + break; + case 'i': + Noidle = !Noidle; + SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off")); + break; + case 'u': + SHOWMESSAGE(("Which User (Blank for All): ")); + strcpy(CurrUser,getstr()); + break; + case 'k': + if (Secure) + SHOWMESSAGE(("\aCan't kill in secure mode")); + else { + int pid, signo; + PUTP(md); + SHOWMESSAGE(("PID to kill: ")); + pid = getint(); + if (pid == BAD_INPUT) + break; + PUTP(top_clrtoeol); + SHOWMESSAGE(("Kill process %d with what signal? [15] ", pid)); + PUTP(me); + signo = getsig(); + /* FIXME: -1 may mean an unknown signal */ + if (signo == -1) + signo = SIGTERM; + if (kill(pid, signo)) + SHOWMESSAGE(("\aKill of PID %d with %d failed: %s", + pid, signo, strerror(errno))); + } + break; + case 'l': + SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off")); + if (show_loadav) { + show_loadav = 0; + header_lines--; + } else { + show_loadav = 1; + header_lines++; + } + Numfields = make_header(); + break; + case 'm': + SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off")); + if (show_memory) { + show_memory = 0; + header_lines -= 2; + } else { + show_memory = 1; + header_lines += 2; + } + Numfields = make_header(); + break; + case 'M': + SHOWMESSAGE(("Sort by memory usage")); + sort_type = S_MEM; + reset_sort_options(); + register_sort_function(-1, (cmp_t)mem_sort); + break; + case 'n': + case '#': + printf("Processes to display (0 for unlimited): "); + numinput = getint(); + if (numinput != -1) { + Display_procs = numinput; + window_size(0); + } + break; + case 'r': + if (Secure) + SHOWMESSAGE(("\aCan't renice in secure mode")); + else { + int pid, val; + + printf("PID to renice: "); + pid = getint(); + if (pid == BAD_INPUT) + break; + PUTP(tgoto(cm, 0, header_lines - 2)); + PUTP(top_clrtoeol); + printf("Renice PID %d to value: ", pid); + val = getint(); + if (val == BAD_INPUT) + val = 10; + if (setpriority(PRIO_PROCESS, pid, val)) + SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s", + pid, val, strerror(errno))); + } + break; + case 'P': + SHOWMESSAGE(("Sort by CPU usage")); + sort_type = S_PCPU; + reset_sort_options(); + register_sort_function(-1, (cmp_t)pcpu_sort); + break; + case 'A': + SHOWMESSAGE(("Sort by age")); + sort_type = S_AGE; + reset_sort_options(); + register_sort_function(-1, (cmp_t)age_sort); + break; + case 'N': + SHOWMESSAGE(("Sort numerically by pid")); + sort_type = S_NONE; + reset_sort_options(); + break; + case 'c': + show_cmd = !show_cmd; + SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line")); + break; + case 'S': + Cumulative = !Cumulative; + SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off")); + if (Cumulative) + headers[22][1] = 'C'; + else + headers[22][1] = ' '; + Numfields = make_header(); + break; + case 's': + if (Secure) + SHOWMESSAGE(("\aCan't change delay in secure mode")); + else { + double tmp; + printf("Delay between updates: "); + tmp = getfloat(); + if (!(tmp < 0)) + Sleeptime = tmp; + } + break; + case 't': + SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off")); + if (show_stats) { + show_stats = 0; + header_lines -= 2; + } else { + show_stats = 1; + header_lines += 2; + } + Numfields = make_header(); + break; + case 'T': + SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : "")); + sort_type = S_TIME; + reset_sort_options(); + register_sort_function( -1, (cmp_t)time_sort); + break; + case 'f': + case 'F': + change_fields(); + break; + case 'o': + case 'O': + change_order(); + break; + case 'W': + if (getenv("HOME")) { + strcpy(rcfile, getenv("HOME")); + strcat(rcfile, "/"); + strcat(rcfile, RCFILE); + fp = fopen(rcfile, "w"); + if (fp != NULL) { + fprintf(fp, "%s\n", Fields); + i = (int) Sleeptime; + if (i < 2) + i = 2; + if (i > 9) + i = 9; + fprintf(fp, "%d", i); + if (Secure) + fprintf(fp, "%c", 's'); + if (Cumulative) + fprintf(fp, "%c", 'S'); + if (!show_cmd) + fprintf(fp, "%c", 'c'); + if (Noidle) + fprintf(fp, "%c", 'i'); + if (!show_memory) + fprintf(fp, "%c", 'm'); + if (!show_loadav) + fprintf(fp, "%c", 'l'); + if (!show_stats) + fprintf(fp, "%c", 't'); + if (!Irixmode) + fprintf(fp, "%c", 'I'); + fprintf(fp, "\n"); + fclose(fp); + SHOWMESSAGE(("Wrote configuration to %s", rcfile)); + } else { + SHOWMESSAGE(("Couldn't open %s", rcfile)); + } + } else { + SHOWMESSAGE(("Couldn't get $HOME -- not saving")); + } + break; + default: + SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c)); + } + + /* + * Return to raw mode. + */ + if (!Batch) tcsetattr(0, TCSANOW, &Rawtty); + return; +} + + +/*##################################################################### + *####### A readproctable function that uses already allocated ##### + *####### table entries. ##### + *##################################################################### + */ +#define Do(x) (flags & PROC_ ## x) + +static proc_t** readproctab2(int flags, proc_t** tab, ...) { + PROCTAB* PT = NULL; + static proc_t *buff; + int n = 0; + static int len = 0; + va_list ap; + + va_start(ap, tab); /* pass through args to openproc */ + if (Do(UID)) { + /* temporary variables to ensure that va_arg() instances + * are called in the right order + */ + uid_t* u; + int i; + + u = va_arg(ap, uid_t*); + i = va_arg(ap, int); + PT = openproc(flags, u, i); + } + else if (Do(PID)) { + PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */ + /* work around a bug in openproc() */ + PT->procfs = NULL; + /* share some process time, since we skipped opendir("/proc") */ + usleep (50*1000); + } + else if (Do(TTY) || Do(STAT)) + PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */ + else + PT = openproc(flags); + va_end(ap); + buff = (proc_t *) 1; + while (ncmdline) { + free((void*)*tab[n]->cmdline); + tab[n]->cmdline = NULL; + } + buff = readproc(PT, tab[n]); + if (buff) n++; + } + if (buff) { + do { /* (ii) not yet allocated chunks */ + tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */ + buff = readproc(PT, NULL); /* final null to terminate */ + if(buff) tab[n]=buff; + len++; + n++; + } while (buff); /* stop when NULL reached */ + tab[n-1] = xcalloc(NULL, sizeof (proc_t)); + tab[n-1]->pid=-1; /* Mark end of Table */ + } else { + if (n == len) { + tab = xrealloc(tab, (n+1)*sizeof(proc_t*)); + tab[n] = xcalloc(NULL, sizeof (proc_t)); + len++; + } + tab[n]->pid=-1; /* Use this instead of NULL when not at the end of */ + } /* the allocated space */ + closeproc(PT); + return tab; +} diff --git a/top.desktop b/top.desktop new file mode 100644 index 00000000..cf1c1917 --- /dev/null +++ b/top.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Top +Type=Application +Comment=Repeatedly display processes by CPU time, memory usage, etc. +Exec=top +Terminal=true diff --git a/top.h b/top.h new file mode 100644 index 00000000..e7cc2e2e --- /dev/null +++ b/top.h @@ -0,0 +1,228 @@ +/* + * top.h header file 1996/05/18, + * + * function prototypes, global data definitions and string constants. + */ + +static proc_t** readproctab2(int flags, proc_t** tab, ...); +static void parse_options(char *Options, int secure); +static void get_options(void); +static void error_end(int rno); +static void end(int signo); +static void stop(int signo); +static void window_size(int signo); +static int make_header(void); +static char *getstr(void); +static int getsig(void); +static float getfloat(void); +static int time_sort(proc_t **P, proc_t **Q); +static int pcpu_sort(proc_t **P, proc_t **Q); +static int mem_sort(proc_t **P, proc_t **Q); +static int age_sort(proc_t **P, proc_t **Q); +static void show_fields(void); +static void change_order(void); +static void change_fields(void); +static void show_task_info(proc_t *task); +static void show_procs(void); +static float get_elapsed_time(void); +static void show_meminfo(void); +static void do_stats(proc_t** p, float elapsed_time, int pass); +static void do_key(char c); + + +/* configurable field display support */ + +static int pflags[30]; +static int Numfields; + + + /* Name of the config file (in $HOME) */ +#ifndef RCFILE +#define RCFILE ".toprc" +#endif + +#ifndef SYS_TOPRC +#define SYS_TOPRC "/etc/toprc" +#endif + +#define MAXLINES 2048 +#define MAXNAMELEN 1024 + +/* this is what procps top does by default, so let's do this, if nothing is + * specified + */ +#ifndef DEFAULT_SHOW +/* 0 1 2 3 */ +/* 0123456789012345678901234567890 */ +#define DEFAULT_SHOW "AbcDgHIjklMnoTP|qrsuzyV{EFWX" +#endif +static char Fields[256] = ""; + + +/* This structure stores some critical information from one frame to + the next. mostly used for sorting. Added cumulative and resident fields. */ +struct save_hist { + int ticks; + int pid; + int pcpu; + int utime; + int stime; +}; + + /* The original terminal attributes. */ +static struct termios Savetty; + /* The new terminal attributes. */ +static struct termios Rawtty; + /* Cached termcap entries. */ +static char *cm, *cl, *top_clrtobot, *top_clrtoeol, *ho, *md, *me, *mr; + /* Current window size. Note that it is legal to set Display_procs + larger than can fit; if the window is later resized, all will be ok. + In other words: Display_procs is the specified max number of + processes to display (zero for infinite), and Maxlines is the actual + number. */ +static int Lines, Cols, Maxlines, Display_procs; + /* Maximum length to display of the command line of a process. */ +static unsigned Maxcmd; + + /* Controls how long we sleep between screen updates. Accurate to + microseconds. */ +static float Sleeptime = 5; + /* for opening/closing the system map */ +static int psdbsucc = 0; + /* Mode flags. */ +static int Irixmode = 1; +static int Secure = 0; +static int Cumulative = 0; +static int Noidle = 0; + +static int CPU_states = 0; +static char CurrUser[BUFSIZ]; + +static int CL_pg_shift = (PAGE_SHIFT - 10); +static int CL_wchan_nout = -1; + +static int show_stats = 1; /* show status summary */ +static int show_memory = 1; /* show memory summary */ +static int show_loadav = 1; /* show load average and uptime */ +static int show_cmd = 1; /* show command name instead of commandline */ + +static pid_t monpids[520]; /* randomly chosen value */ +static const int monpids_max = sizeof(monpids)/sizeof(pid_t); +static int monpids_index = 0; + +static int Loops = -1; /* number of iterations. -1 loops forever */ +static int Batch = 0; /* batch mode. Collect no input, dumb output */ + +/* sorting order: cpu%, mem, time (cumulative, if in cumulative mode) */ +enum { + S_PCPU, S_MEM, S_TIME, S_AGE, S_NONE +}; +/* default sorting by CPU% */ +static int sort_type = S_PCPU; + +/* flags for each possible field. At the moment up to 30 are supported */ +enum { + P_PID, P_PPID, P_EUID, P_EUSER, + P_PCPU, P_PMEM, P_TTY, P_PRI, + P_NICE, P_PAGEIN, P_TSIZ, P_DSIZ, + P_SIZE, P_TRS, P_SWAP, P_SHARE, + P_A, P_WP, P_DT, P_RSS, + P_WCHAN, P_STAT, P_TIME, P_COMMAND, + P_LCPU, P_FLAGS, P_END +}; +/* corresponding headers */ +static char *headers[] = +{ + " PID ", " PPID ", " UID ", + "USER ", "%CPU ", "%MEM ", + "TTY ", "PRI ", " NI ", + "PAGEIN ", "TSIZE ", "DSIZE ", + " SIZE ", " TRS ", "SWAP ", + "SHARE ", " A ", " WP ", + " D ", " RSS ", "WCHAN ", + "STAT ", " TIME ", "COMMAND", + "LC ", + " FLAGS " +}; +/* corresponding field desciptions */ +static char *headers2[] = +{ + "Process Id", "Parent Process Id", "User Id", + "User Name", "CPU Usage", "Memory Usage", + "Controlling tty", "Priority", "Nice Value", + "Page Fault Count", "Code Size (kb)", "Data+Stack Size (kb)", + "Virtual Image Size (kb)", "Resident Text Size (kb)", "Swapped kb", + "Shared Pages (kb)", "Accessed Page count", "Write Protected Pages", + "Dirty Pages", "Resident Set Size (kb)", "Sleeping in Function", + "Process Status", "CPU Time", "Command", + "Last used CPU (expect this to change regularly)", + "Task Flags (see linux/sched.h)" +}; + + /* The header printed at the top of the process list.*/ +static char Header[MAXLINES]; + + /* The response to the interactive 'h' command. */ +#define HELP_SCREEN "\ +Interactive commands are:\n\ +\n\ +space\tUpdate display\n\ +^L\tRedraw the screen\n\ +fF\tadd and remove fields\n\ +oO\tChange order of displayed fields\n\ +h or ?\tPrint this list\n\ +S\tToggle cumulative mode\n\ +i\tToggle display of idle proceses\n\ +I\tToggle between Irix and Solaris views (SMP-only)\n\ +c\tToggle display of command name/line\n\ +l\tToggle display of load average\n\ +m\tToggle display of memory information\n\ +t\tToggle display of summary information\n\ +k\tKill a task (with any signal)\n\ +r\tRenice a task\n\ +N\tSort by pid (Numerically)\n\ +A\tSort by age\n\ +P\tSort by CPU usage\n\ +M\tSort by resident memory usage\n\ +T\tSort by time / cumulative time\n\ +u\tShow only a specific user\n\ +n or #\tSet the number of process to show\n\ +s\tSet the delay in seconds between updates\n\ +W\tWrite configuration file ~/.toprc\n\ +q\tQuit" +#define SECURE_HELP_SCREEN "\ +Interactive commands available in secure mode are:\n\ +\n\ +space\tUpdate display\n\ +^L\tRedraw the screen\n\ +fF\tadd and remove fields\n\ +h or ?\tPrint this list\n\ +S\tToggle cumulative mode\n\ +i\tToggle display of idle proceses\n\ +c\tToggle display of command name/line\n\ +l\tToggle display of load average\n\ +m\tToggle display of memory information\n\ +t\tToggle display of summary information\n\ +n or #\tSet the number of process to show\n\ +u\tShow only a specific user\n\ +oO\tChange order of displayed fields\n\ +W\tWrite configuration file ~/.toprc\n\ +q\tQuit" + + /* Number of lines needed to display the header information. */ +static int header_lines; + +/* ############## Some Macro definitions for screen handling ######### */ + /* String to use in error messages. */ +#define PROGNAME "top" + /* Clear the screen. */ +#define clear_screen() \ + printf("%s", cl) + /* Show an error in the context of the spiffy full-screen display. */ +#define SHOWMESSAGE(x) do { \ + printf("%s%s%s%s", tgoto(cm, 0, header_lines-2), top_clrtoeol,md,mr); \ + printf x; \ + printf ("%s",me); \ + fflush(stdout); \ + sleep(2); \ + } while (0) diff --git a/uptime.1 b/uptime.1 new file mode 100644 index 00000000..ecb4e70b --- /dev/null +++ b/uptime.1 @@ -0,0 +1,34 @@ +.\" -*-Nroff-*- +.\" +.TH UPTIME 1 "26 Jan 1993" "Cohesive Systems" "Linux User's Manual" +.SH NAME +uptime \- Tell how long the system has been running. +.SH SYNOPSIS +.B uptime +.br +.BR uptime " [" "\-V" ] +.SH DESCRIPTION +.B uptime +gives a one line display of the following information. +The current time, +how long the system has been running, +how many users are currently logged on, +and the system load averages for the past 1, 5, and 15 minutes. +.sp +This is the same information contained in the header line displayed by +.BR w (1). +.SH FILES +.IR /var/run/utmp " information about who is currently logged on" +.br +.IR /proc " process information" +.SH AUTHORS +.B uptime +was written by Larry Greenfield and +Michael K. Johnson . + +Please send bug reports to +.SH "SEE ALSO" +.BR ps (1), +.BR top (1), +.BR utmp (5), +.BR w (1) diff --git a/uptime.c b/uptime.c new file mode 100644 index 00000000..e63621ef --- /dev/null +++ b/uptime.c @@ -0,0 +1,9 @@ +#include +#include "proc/whattime.h" +#include "proc/version.h" + +int main(int argc, char *argv[]) { + if(argc == 1) print_uptime(); + if((argc == 2) && (!strcmp(argv[1], "-V"))) display_version(); + return 0; +} diff --git a/utmp.c b/utmp.c new file mode 100644 index 00000000..936a7f6c --- /dev/null +++ b/utmp.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* examine and fix utmp entries. Note that the code for fixing entries + is not complete, and indeed does nothing at all at this time. No bug + reports, please, as I am still actively working on this. It is not + here for general use, but only so that I can ferret out any bugs that + exist on other peoples systems without having to log in to their systems + myself ;-) */ + + +int main (int argc, char **argv) { + + FILE *ut; /* /var/run/utmp */ + struct utmp uts; /* utmp record */ + char user[UT_NAMESIZE + 1]; + char host[17]; + char ch; + int print_all = 0, list = 0, fix = 0; + +/* get options */ + while ((ch = getopt(argc, argv, "laf")) != EOF) + switch (ch) { + case 'a': + print_all = 1; + break; + case 'l': + list = 1; + break; + case 'f': + fix = 1; + break; + } + +/* check argument options */ + if ( (!list && !print_all && !fix)) { + fprintf(stderr, "You must specify a command line option:\n\tl = list\n\ +\tf = fix\n\ta = all (requires l or f)\n"); + exit(1); + } + + + if (list) { + ut = fopen(UTMP_FILE, "r"); + while (fread(&uts, sizeof(uts), 1, ut)) + if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000')) + || print_all) { + strncpy(user, uts.ut_user, UT_NAMESIZE); + user[UT_NAMESIZE]=0; + strncpy(host, uts.ut_host, 16); + host[16]=0; + printf("ut_type: %d\n", uts.ut_type); + printf("ut_pid: %d\n", uts.ut_pid); + printf("ut_line: %s\n", uts.ut_line); + printf("ut_id: %2s\n", uts.ut_id); + printf("ut_time: %d\n", uts.ut_time); + printf("ut_user: %s\n", user); + printf("ut_host: %s\n", host); + printf("ut_addr: %d\n\n", uts.ut_addr); + } + fclose(ut); + } + + + if (fix) { + ut = fopen(UTMP_FILE, "r"); + while (fread(&uts, sizeof(uts), 1, ut)) + if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000')) + || print_all) { + /* Display entry in utmp */ + strncpy(user, uts.ut_user, UT_NAMESIZE); + user[UT_NAMESIZE]=0; + strncpy(host, uts.ut_host, 16); + host[16]=0; + printf("ut_type: %d\n", uts.ut_type); + printf("ut_pid: %d\n", uts.ut_pid); + printf("ut_line: %s\n", uts.ut_line); + printf("ut_id: %s2\n", uts.ut_id); + printf("ut_time: %d\n", uts.ut_time); + printf("ut_user: %s\n", user); + printf("ut_host: %s\n", host); + printf("ut_addr: %d\n\n", uts.ut_addr); + + printf("Modify this record? (y/N): "); fflush(stdout); + /* Ask if to delete or no */ + if ((ch = getchar()) == 'y' || ch == 'Y') { + while (getchar() != '\n'); + printf("Change ut_type? "); fflush(stdout); + if ((ch = getchar()) == 'y' || ch == 'Y') { + while (getchar() != '\n'); + printf("INIT, LOGIN, USER, or DEAD_PROCESS? (I/L/U/D): "); + fflush(stdout); + ch = getchar(); + switch (ch) { + case 'i': + case 'I': + uts.ut_type = INIT_PROCESS; + break; + case 'l': + case 'L': + uts.ut_type = LOGIN_PROCESS; + break; + case 'u': + case 'U': + uts.ut_type = USER_PROCESS; + break; + case 'd': + case 'D': + uts.ut_type = DEAD_PROCESS; + break; + default: + printf("Invalid choice: %c\n", ch); + } + if (ch != '\n') while ((ch = getchar()) != '\n'); + } + if (ch != '\n') while ((ch = getchar()) != '\n'); + printf("Change ut_id field? (y/N): "); fflush(stdout); + if ((ch = getchar()) == 'y' || ch == 'Y') { + while (getchar() != '\n'); + printf("Please enter the two characters for ut_id: "); + fflush(stdout); + uts.ut_id[0] = getchar(); + uts.ut_id[1] = getchar(); + while ((ch = getchar()) != '\n'); + } + if (ch != '\n') while ((ch = getchar()) != '\n'); + printf("Change the ut_user field? (y/N): "); fflush(stdout); + if ((ch = getchar()) == 'y' || ch == 'Y') { + int i; + while (getchar() != '\n'); + printf("Please enter the new ut_name, up to %c characters: ", + UT_NAMESIZE); + fflush(stdout); + for (i=0; i +.\" Distributed under the GPL, Copyleft 1994. +.TH VMSTAT 8 "27 July 1994 " "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual" +.SH NAME +vmstat \- Report virtual memory statistics +.SH SYNOPSIS +.ft B +.B vmstat +.RB [ "\-n" ] +.RI [ delay " [ " count ]] +.br +.BR vmstat [ "\-V" ] +.SH DESCRIPTION +\fBvmstat\fP reports information about processes, memory, paging, +block IO, traps, and cpu activity. + +The first report produced gives averages since the last reboot. Additional +reports give information on a sampling period of length \fIdelay\fP. +The process and memory reports are instantaneous in either case. + +.SS Options +The \fB-n\fP switch causes the header to be displayed only once rather than periodically. +.PP +.I delay +is the delay between updates in seconds. If no delay is specified, +only one report is printed with the average values since boot. +.PP +.I count +is the number of updates. If no count is specified and delay is +defined, \fIcount\fP defaults to infinity. +.PP +The \fB-V\fP switch results in displaying version information. +.PP +.SH FIELD DESCRIPTIONS +.SS +.B "Procs" +.nf +r: The number of processes waiting for run time. +b: The number of processes in uninterruptable sleep. +w: The number of processes swapped out but otherwise runnable. This + field is calculated, but Linux never desperation swaps. +.fi +.PP +.SS +.B "Memory" +.nf +swpd: the amount of virtual memory used (kB). +free: the amount of idle memory (kB). +buff: the amount of memory used as buffers (kB). +.fi +.PP +.SS +.B "Swap" +.nf +si: Amount of memory swapped in from disk (kB/s). +so: Amount of memory swapped to disk (kB/s). +.fi +.PP +.SS +.B "IO" +.nf +bi: Blocks sent to a block device (blocks/s). +bo: Blocks received from a block device (blocks/s). +.fi +.PP +.SS +.B "System" +.nf +in: The number of interrupts per second, including the clock. +cs: The number of context switches per second. +.if +.PP +.SS +.B "CPU " +These are percentages of total CPU time. +.nf +us: user time +sy: system time +id: idle time +.nf +.SH NOTES +.B "vmstat " +does not require special permissions. +.PP +These reports are intended to help identify system bottlenecks. Linux +.B "vmstat " +does not count itself as a running process. +.PP +All linux blocks are currently 1k, except for CD-ROM blocks which are 2k. +.PP +.SH FILES +.ta +.nf +/proc/meminfo +/proc/stat +/proc/*/stat +.fi + +.SH "SEE ALSO" +ps(1), top(1), free(1) +.PP +.SH BUGS +Does not tabulate the block io per device or count the number of system calls. +.SH AUTHOR +Written by Henry Ware . diff --git a/vmstat.c b/vmstat.c new file mode 100644 index 00000000..a3664e29 --- /dev/null +++ b/vmstat.c @@ -0,0 +1,278 @@ +#define VERSION Version: 0.99, last modified 15 January 94: ALPHA +#define PROGNAME "vmstat" +/* Copyright 1994 by Henry Ware . Copyleft same year. */ +/* This attempts to display virtual memory statistics. + vmstat does not need to be run as root. +*/ +/* TODO etc. + * Count the system calls. How? I don't even know where to start. + * add more items- aim at Posix 1003.2 (1003.7?) compliance, if relevant (I. + don't think it is, but let me know.) + * sometimes d(inter); report bugs to . +*/ + +#include "proc/sysinfo.h" +#include "proc/version.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NDEBUG !DEBUG + +#define BUFFSIZE 4096 +#define FALSE 0 +#define TRUE 1 + +static char buff[BUFFSIZE]; /* used in the procedures */ + +typedef unsigned long jiff; + + +/****************************************************************/ + + +static void usage(void) { + fprintf(stderr,"usage: %s [-V] [-n] [delay [count]]\n",PROGNAME); + fprintf(stderr," -V prints version.\n"); + fprintf(stderr," -n causes the headers not to be reprinted regularly.\n"); + fprintf(stderr," delay is the delay between updates in seconds. \n"); + fprintf(stderr," count is the number of updates.\n"); + exit(EXIT_FAILURE); +} + +static void crash(char *filename) { + perror(filename); + exit(EXIT_FAILURE); +} + +static int winhi(void) { + struct winsize win; + int rows = 24; + + if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_row > 0) + rows = win.ws_row; + + return rows; +} + + +static void showheader(void){ + printf("%8s%28s%8s%12s%11s%12s\n", + "procs","memory","swap","io","system","cpu"); + printf("%2s %2s %2s %6s %6s %6s %6s %3s %3s %5s %5s %4s %5s %3s %3s %3s\n", + "r","b","w","swpd","free","buff","cache","si","so","bi","bo", + "in","cs","us","sy","id"); +} + +static void getstat(jiff *cuse, jiff *cice, jiff *csys, jiff long *cide, + unsigned *pin, unsigned *pout, unsigned *s_in, unsigned *sout, + unsigned *itot, unsigned *i1, unsigned *ct) { + static int Stat; + + if ((Stat=open("/proc/stat", O_RDONLY, 0)) != -1) { + char* b; + buff[BUFFSIZE-1] = 0; /* ensure null termination in buffer */ + read(Stat,buff,BUFFSIZE-1); + close(Stat); + *itot = 0; + *i1 = 1; /* ensure assert below will fail if the sscanf bombs */ + b = strstr(buff, "cpu "); + sscanf(b, "cpu %lu %lu %lu %lu", cuse, cice, csys, cide); + b = strstr(buff, "page "); + sscanf(b, "page %u %u", pin, pout); + b = strstr(buff, "swap "); + sscanf(b, "swap %u %u", s_in, sout); + b = strstr(buff, "intr "); + sscanf(b, "intr %u %u", itot, i1); + b = strstr(buff, "ctxt "); + sscanf(b, "ctxt %u", ct); + } + else { + crash("/proc/stat"); + } +} + +static void getrunners(unsigned int *running, unsigned int *blocked, + unsigned int *swapped) { + static struct direct *ent; + static char filename[80]; + static int fd; + static unsigned size; + static char c; + DIR *proc; + + *running=0; + *blocked=0; + *swapped=0; + + if ((proc=opendir("/proc"))==NULL) crash("/proc"); + + while((ent=readdir(proc))) { + if (isdigit(ent->d_name[0])) { /*just to weed out the obvious junk*/ + sprintf(filename, "/proc/%s/stat", ent->d_name); + if ((fd = open(filename, O_RDONLY, 0)) != -1) { /*this weeds the rest*/ + read(fd,buff,BUFFSIZE-1); + sscanf(buff, "%*d %*s %c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u\n",&c,&size); + close(fd); + + if (c=='R') { + if (size>0) (*running)++; + else (*swapped)++; + } + else if (c=='D') { + if (size>0) (*blocked)++; + else (*swapped)++; + } + } + } + } + closedir(proc); + +#if 1 + /* is this next line a good idea? It removes this thing which + uses (hopefully) little time, from the count of running processes */ + (*running)--; +#endif +} + + + + +int main(int argc, char *argv[]) { + + const char format[]="%2u %2u %2u %6u %6u %6u %6u %3u %3u %5u %5u %4u %5u %3u %3u %3u\n"; + unsigned int height=22; /* window height, reset later if needed. */ +#if 0 + unsigned long int args[2]={0,0}; +#endif + unsigned int moreheaders=TRUE; + unsigned int tog=0; /* toggle switch for cleaner code */ + unsigned int i,hz; + unsigned int running,blocked,swapped; + jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2]; + jiff duse,dsys,didl,Div,divo2; + unsigned int pgpgin[2], pgpgout[2], pswpin[2], pswpout[2]; + unsigned int inter[2],ticks[2],ctxt[2]; + unsigned int per=0, pero2; + unsigned long num=0; + unsigned int kb_per_page = sysconf(_SC_PAGESIZE) / 1024; + + setlinebuf(stdout); + argc=0; /* redefined as number of integer arguments */ + per=1; + num=0; + for (argv++;*argv;argv++) { + if ('-' ==(**argv)) { + switch (*(++(*argv))) { + case 'V': + display_version(); + exit(0); + case 'n': + /* print only one header */ + moreheaders=FALSE; + break; + default: + /* no other aguments defined yet. */ + usage(); + } + } else { + argc++; + switch (argc) { + case 1: + if ((per = atoi(*argv)) == 0) + usage(); + num = ULONG_MAX; + break; + case 2: + num = atol(*argv); + break; + default: + usage(); + } /* switch */ + } + } + + if (moreheaders) { + int tmp=winhi()-3; + height=((tmp>0)?tmp:22); + } + + pero2=(per/2); + showheader(); + getrunners(&running,&blocked,&swapped); + meminfo(); + getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl, + pgpgin,pgpgout,pswpin,pswpout, + inter,ticks,ctxt); + duse= *cpu_use + *cpu_nic; + dsys= *cpu_sys; + didl= *cpu_idl; + Div= duse+dsys+didl; + hz=sysconf(_SC_CLK_TCK); /* get ticks/s from system */ + divo2= Div/2UL; + printf(format, + running,blocked,swapped, + kb_swap_used,kb_main_free,kb_main_buffers,kb_main_cached, + (*pswpin *kb_per_page*hz+divo2)/Div, + (*pswpout*kb_per_page*hz+divo2)/Div, + (*pgpgin *hz+divo2)/Div, + (*pgpgout *hz+divo2)/Div, + (*inter *hz+divo2)/Div, + (*ctxt *hz+divo2)/Div, + (100*duse+divo2)/Div, + (100*dsys+divo2)/Div, + (100*didl+divo2)/Div + ); + + for(i=1;i and Michael K. Johnson +. + +Please send bug reports to diff --git a/w.c b/w.c new file mode 100644 index 00000000..e847d869 --- /dev/null +++ b/w.c @@ -0,0 +1,298 @@ +/* w - show what logged in users are doing. Almost entirely rewritten from + * scratch by Charles Blake circa June 1996. Some vestigal traces of the + * original may exist. That was done in 1993 by Larry Greenfield with some + * fixes by Michael K. Johnson. + */ +#include "proc/version.h" +#include "proc/whattime.h" +#include "proc/readproc.h" +#include "proc/devname.h" +#include "proc/procps.h" +#include "proc/sysinfo.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ /* for HZ */ + +static int ignoreuser = 0; /* for '-u' */ +static proc_t **procs; /* our snapshot of the process table */ + +typedef struct utmp utmp_t; + +#ifdef W_SHOWFROM +# define FROM_STRING "on" +#else +# define FROM_STRING "off" +#endif + +/* Uh... same thing as UT_NAMESIZE */ +#define USERSZ (sizeof u->ut_user) + + +/* This routine is careful since some programs leave utmp strings + * unprintable. Always outputs at least 16 chars padded with spaces + * on the right if necessary. + */ +static void print_host(char* host, int len) { + char *last; + int width = 0; + + /* FIXME: there should really be a way to configure this... */ + /* for now, we'll just limit it to the 16 that the libc5 version + * of utmp uses. + */ + if (len > 16) len = 16; + last = host + len; + for ( ; host < last ; host++){ + if (isprint(*host) && *host != ' ') { + fputc(*host, stdout); + ++width; + } else { + break; + } + } + if(!width){ /* blank fields screw up parsers */ + fputc('-', stdout); + ++width; + } + /* if *any* unprintables(or blanks), replace rest of line with spaces */ + while (width < 16) { + fputc(' ', stdout); + ++width; + } +} + +/***** compact 7 char format for time intervals (belongs in libproc?) */ +static void print_time_ival7(time_t t, int centi_sec, FILE* fout) { + if((long)t < (long)0){ /* system clock changed? */ + printf(" ? "); + return; + } + if (t >= 48*60*60) /* > 2 days */ + fprintf(fout, " %2ludays", t/(24*60*60)); + else if (t >= 60*60) /* > 1 hour */ + fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60)); + else if (t > 60) /* > 1 minute */ + fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60); + else + fprintf(fout, " %2lu.%02us", t, centi_sec); +} + +/**** stat the device file to get an idle time */ +static time_t idletime(char *tty) { + struct stat sbuf; + if (stat(tty, &sbuf) != 0) + return 0; + return time(NULL) - sbuf.st_atime; +} + +/***** 7 character formatted login time */ +static void print_logintime(time_t logt, FILE* fout) { + char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, + *month [] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" }; + time_t curt; + struct tm *logtm, *curtm; + int hour; + char *merid; /* meridian indicator */ + int today; + + curt = time(NULL); + curtm = localtime(&curt); + /* localtime returns a pointer to static memory */ + today = curtm->tm_yday; + logtm = localtime(&logt); + hour = logtm->tm_hour; + merid = (hour < 12) ? "am" : "pm"; + if (hour >= 12) hour -= 12; + if (hour == 0) hour = 12; + if (curt - logt > 12*60*60 && logtm->tm_yday != today) { + if (curt - logt > 6*24*60*60) + fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon], + logtm->tm_year % 100); + else + fprintf(fout, " %3s%02d%s", weekday[logtm->tm_wday], hour, merid); + } else { + fprintf(fout, " %02d:%02d%s", hour, logtm->tm_min, merid); + } +} + + +/* This function scans the process table accumulating total cpu times for + * any processes "associated" with this login session. It also searches + * for the "best" process to report as "(w)hat" the user for that login + * session is doing currently. This the essential core of 'w'. + */ +static proc_t *getproc(utmp_t *u, char *tty, int *jcpu, int *found_utpid) { + int line; + proc_t **p, *best = NULL, *secondbest = NULL; + unsigned uid = ~0U; + + if(!ignoreuser){ + char buf[UT_NAMESIZE+1]; + struct passwd *passwd_data; /* pointer to static data */ + strncpy(buf,u->ut_user,UT_NAMESIZE); + buf[UT_NAMESIZE] = '\0'; + passwd_data = getpwnam(buf); + if(!passwd_data) return NULL; + uid = passwd_data->pw_uid; + /* OK to have passwd_data go out of scope here */ + } + line = tty_to_dev(tty); + *jcpu = *found_utpid = 0; + for(p = procs; *p; p++) { + if((**p).pid == u->ut_pid) *found_utpid = 1; + if((**p).tty != line) continue; + (*jcpu) += (**p).utime + (**p).stime; + secondbest = *p; + /* same time-logic here as for "best" below */ + if(! (secondbest && (**p).start_time <= secondbest->start_time) ){ + secondbest = *p; + } + if(!ignoreuser && uid != (**p).euid && uid != (**p).ruid) continue; + if((**p).pid != (**p).tpgid) continue; + if(best && (**p).start_time <= best->start_time) continue; + best = *p; + } + return best ? best : secondbest; +} + + +/***** showinfo */ +static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) { + int jcpu, ut_pid_found; + unsigned i; + char uname[USERSZ + 1] = "", + tty[5 + sizeof u->ut_line + 1] = "/dev/"; + proc_t *best; + + for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */ + if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/')) + tty[i+5] = u->ut_line[i]; + else + tty[i+5] = '\0'; + + best = getproc(u, tty + 5, &jcpu, &ut_pid_found); + + /* just skip if stale utmp entry (i.e. login proc doesn't exist). If there + * is a desire a cmdline flag could be added to optionally show it with a + * prefix of (stale) in front of cmd or something like that. + */ + if (!ut_pid_found) + return; + + strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */ + if (formtype) { + printf("%-9.8s%-9.8s", uname, u->ut_line); + if (from) + print_host(u->ut_host, sizeof u->ut_host); + print_logintime(u->ut_time, stdout); + if (*u->ut_line == ':') /* idle unknown for xdm logins */ + printf(" ?xdm? "); + else + print_time_ival7(idletime(tty), 0, stdout); + print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout); + if (best) { + int pcpu = best->utime + best->stime; + print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout); + } else + printf(" ? "); + } else { + printf("%-9.8s%-9.8s", u->ut_user, u->ut_line); + if (from) + print_host(u->ut_host, sizeof u->ut_host); + if (*u->ut_line == ':') /* idle unknown for xdm logins */ + printf(" ?xdm? "); + else + print_time_ival7(idletime(tty), 0, stdout); + } + fputs(" ", stdout); + if (best) { + if (best->cmdline) + print_strlist(stdout, best->cmdline, " ", maxcmd); + else + printf("%*.*s", -maxcmd, maxcmd, best->cmd); + } else { + printf("-"); + } + fputc('\n', stdout); +} + +/***** main */ +int main(int argc, char **argv) { + char *user = NULL; + utmp_t *u; + struct winsize win; + int header=1, longform=1, from=1, args, maxcmd=80, ch; + +#ifndef W_SHOWFROM + from = 0; +#endif + + for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++) + switch (ch) { + case 'h': header = 0; break; + case 'l': longform = 1; break; + case 's': longform = 0; break; + case 'f': from = !from; break; + case 'V': display_version(); exit(0); + case 'u': ignoreuser = 1; break; + default: + printf("usage: w -hlsufV [user]\n" + " -h skip header\n" + " -l long listing (default)\n" + " -s short listing\n" + " -u ignore uid of processes\n" + " -f toggle FROM field (default %s)\n" + " -V display version\n", FROM_STRING); + exit(1); + } + + if ((argv[optind])) + user = (argv[optind]); + + if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) + maxcmd = win.ws_col; + if (maxcmd < 71) { + fprintf(stderr, "%d column window is too narrow\n", maxcmd); + exit(1); + } + maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0); + if (maxcmd < 3) + fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col); + + procs = readproctab(PROC_FILLCMD | PROC_FILLUSR); + + if (header) { /* print uptime and headers */ + print_uptime(); + printf("USER TTY "); + if (from) + printf("FROM "); + if (longform) + printf(" LOGIN@ IDLE JCPU PCPU WHAT\n"); + else + printf(" IDLE WHAT\n"); + } + + utmpname(UTMP_FILE); + setutent(); + while ((u=getutent())) { + if (u->ut_type == USER_PROCESS && + (user ? !strncmp(u->ut_user, user, USERSZ) : *u->ut_user)) + showinfo(u, longform, maxcmd, from); + } + endutent(); + + return 0; +} diff --git a/watch.1 b/watch.1 new file mode 100644 index 00000000..f6021a6d --- /dev/null +++ b/watch.1 @@ -0,0 +1,82 @@ +.TH WATCH 1 "1999 Apr 3" " " "Linux User's Manual" +.SH NAME +watch \- execute a program periodically, showing output fullscreen +.SH SYNOPSIS +.B watch +.I [\-dhv] [\-n ] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=] [\-\-version] +.SH DESCRIPTION +.BR watch +runs +.I command +repeatedly, displaying its output (the first screenfull). This allows you to +watch the program output change over time. By default, the program is run +every 2 seconds; use +.I -n +or +.I --interval +to specify a different interval. +.PP +The +.I -d +or +.I --differences +flag will highlight the differences between successive updates. The +.I --cumulative +option makes highlighting "sticky", presenting a running display of all +positions that have ever changed. +.PP +.BR watch +will run until interrupted. +.SH NOTE +Note that +.I command +is given to "sh -c" +which means that you may need to use extra quoting to get the desired effect. +.PP +Note that POSIX option processing is used (i.e., option processing stops at +the first non-option argument). This means that flags after +.I command +don't get interpreted by +.BR watch +itself. +.SH EXAMPLES +.PP +To watch for mail, you might do +.IP +watch \-n 60 from +.PP +To watch the contents of a directory change, you could use +.IP +watch \-d ls \-l +.PP +If you're only interested in files owned by user joe, you might use +.IP +watch \-d 'ls \-l | fgrep joe' +.PP +To see the effects of quoting, try these out +.IP +watch echo $$ +.IP +watch echo '$$' +.IP +watch echo "'"'$$'"'" +.PP +You can watch for your administrator to install the latest kernel with +.IP +watch uname -r +.PP +(Just kidding.) +.SH BUGS +Upon terminal resize, the screen will not be correctly repainted until the +next scheduled update. All +.I --differences +highlighting is lost on that update as well. +.PP +Non-printing characters are stripped from program output. Use "cat -v" as +part of the command pipeline if you want to see them. +.SH AUTHORS +The original +.B watch +was written by Tony Rems in 1991, with mods and +corrections by Francois Pinard. It was reworked and new features added by +Mike Coleman in 1999. diff --git a/watch.c b/watch.c new file mode 100644 index 00000000..cd8ac4ea --- /dev/null +++ b/watch.c @@ -0,0 +1,277 @@ +/* watch -- execute a program repeatedly, displaying output fullscreen + * + * Based on the original 1991 'watch' by Tony Rems + * (with mods and corrections by Francois Pinard). + * + * Substantially reworked, new features (differences option, SIGWINCH + * handling, unlimited command length, long line handling) added Apr 1999 by + * Mike Coleman . + */ + + +#define VERSION "0.2.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct option longopts[] = + { + { "differences", optional_argument, 0, 'd' }, + { "help", no_argument, 0, 'h' }, + { "interval", required_argument, 0, 'n' }, + { "version", no_argument, 0, 'v' }, + { 0, 0, 0, 0 } + }; + +static char usage[] = "Usage: %s [-dhnv] [--differences[=cumulative]] [--help] [--interval=] [--version] \n"; + + +static char *progname; + +static int curses_started = 0; +static int height=24, width=80; +static int screen_size_changed=0; +static int first_screen=1; + + +#define min(x,y) ((x) > (y) ? (y) : (x)) +#define max(x,y) ((x) > (y) ? (x) : (y)) + + +static void +do_usage(void) +{ + fprintf(stderr, usage, progname); + exit(1); +} + + +static void +do_exit(int status) { + if (curses_started) + endwin(); + exit(status); +} + + +/* signal handler */ +static void +die(int notused) +{ + (void)notused; + do_exit(0); +} + + +static void +winch_handler(int notused) +{ + (void)notused; + screen_size_changed = 1; +} + + +static void +get_terminal_size(void) +{ + struct winsize w; + if (ioctl(2, TIOCGWINSZ, &w) == 0) + { + if (w.ws_row > 0) + height = w.ws_row; + if (w.ws_col > 0) + width = w.ws_col; + } +} + + +int +main(int argc, char *argv[]) +{ + int optc; + int option_differences=0, + option_differences_cumulative=0, + option_help=0, + option_version=0; + int interval=2; + char *command; + int command_length=0; /* not including final \0 */ + + progname = argv[0]; + + while ((optc = getopt_long(argc, argv, "+d::hn:v", longopts, (int *) 0)) + != EOF) + { + switch (optc) + { + case 'd': + option_differences = 1; + if (optarg) + option_differences_cumulative = 1; + break; + case 'h': + option_help = 1; + break; + case 'n': + { + char *s; + interval = strtol(optarg, &s, 10); + if (!*optarg || *s) + do_usage(); + } + break; + case 'v': + option_version = 1; + break; + default: + do_usage(); + break; + } + } + + if (option_version) + { + fprintf (stderr, "%s\n", VERSION); + if (!option_help) + exit(0); + } + + if (option_help) + { + fprintf(stderr, usage, progname); + fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr); + fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr); + fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr); + fputs(" -n, --interval=\t\tseconds to wait between updates\n", stderr); + fputs(" -v, --version\t\t\t\tprint the version number\n", stderr); + exit(0); + } + + if (optind >= argc) + do_usage(); + + command = strdup(argv[optind++]); + command_length = strlen(command); + for (;optind width - tsl - 1) + mvaddstr(0, width - tsl - 4, "... "); + mvaddstr(0, width - tsl + 1, ts); + free(header); + + if (!(p = popen(command, "r"))) + { + perror("popen"); + do_exit(2); + } + + for (y=2; y