--- /dev/null
+free:
+Brian Edmonds
+
+oldps:
+Branko Lankester <lankeste@fwi.uva.nl>
+Michael K. Johnson <johnsonm@redhat.com>
+Michael Shields <mjshield@nyx.cs.du.edu>
+Charles Blake <cblake@bbn.com>
+David Mossberger-Tang
+
+ps:
+Albert Cahalan <albert@users.sf.net>
+
+skill/kill/snice:
+Albert Cahalan <albert@users.sf.net>
+
+tload:
+Branko Lankester
+David Engel <david@ods.com>
+Michael K. Johnson <johnsonm@redhat.com>
+
+top:
+Jim Warner <warnerjc@worldnet.att.net>
+
+oldtop:
+Branko Lankester <lankeste@fwi.uva.nl>
+Roger Binns
+Robert Nation <nation@rocket.sanders.lockheed.com>
+Michael K. Johnson <johnsonm@redhat.com>
+Michael Shields <mjshield@nyx.cs.du.edu>
+Tim Janik <timj@gtk.org>
+Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+George Bonser <george@captech.com>
+
+uptime:
+Larry Greenfield <greenfie@gauss.rutgers.edu>
+Michael K. Johnson <johnsonm@sunsite.unc.edu>
+
+vmstat:
+Henry Ware <al172@yfn.ysu.edu>.
+
+w:
+Larry Greenfield <greenfie@gauss.rutgers.edu>
+Michael K. Johnson <johnsonm@redhat.com>
+Charles Blake
+
+watch:
+Tony Rems <rembo@unisoft.com>
+Mike Coleman <mkc@acm.org>
+
--- /dev/null
+BUG REPORTS
+
+Please read this file before sending in a bug report or patch.
+
+Also, PLEASE read the documentation first. 90% of the mail I get
+complaining about procps is due to the sender not having read the
+documentation!
+
+
+Where to send
+=============
+Send comments, bug reports, patches, etc., to albert@users.sf.net
+
+
+What to send
+============
+It is much more useful to me if a program really crashes to recompile it
+with make "CFLAGS=-ggdb -O", run it with "gdb prog" and "run" and send
+me a stack trace ('bt' command). That said, any bug report is still
+better than none.
+
+strace and ltrace output are very helpful:
+
+ strace -o output-file ps --blah
+ bzip2 output-file
+
+I also like "ps --info" output, even if there isn't a ps problem.
+
+Kernel-Dependent Patches
+========================
+If you send me patches which are specific to *running* with a particular
+kernel version of /proc, please condition them with the runtime determined
+variable 'linux_version_code' from libproc/version.c. It is the same
+number as the macro LINUX_VERSION_CODE for which the kernel /proc fs
+code was compiled.
+
+A macro is provide in libproc/version.h to construct the code from its
+components, e.g.
+ if (linux_version_code < LINUX_VERSION(2,5,41))
+ /* blah blah blah */
+A startup call to set_linux_version may also be necessary.
+
+Of course, if a bug is due to a change in kernel file formats, it would
+be best to first try to generalize the parsing, since the code is then
+more resilient against future change.
+
+Also unified diffs (diff -u) are my preference, context diffs (diff -c )
+are kind of usable, and standard diffs (diff) are more useless than a
+generic text description of what you did. Just use
+ diff -Naurd oldfile newfile
+or
+ diff -Naurd old-procps-dir new-procps-dir
+to create your diffs and you will make me happy. Also make sure to
+include a description of what the diff is for or I'm likely to ignore
+it because of general lack of time...
+
+It might be nice to get rid of miscellaneous compiler warnings, but
+don't bend over backwards to do it.
+
+
+Code Structure
+==============
+
+A goal is to encapsulate *all* parsing dependent on /proc
+file formats into the libproc library. If the API is general enough
+it can hopefully stabilize and then /proc changes might only require
+updating libproc.so. Beyond that having the set of utilities be simple
+command lines parsers and output formatters and encapsulating all kernel
+divergence in libproc is the way to go.
+
+Hence if you are submitting a new program or are fixing an old one, keep
+in mind that adding files to libproc which encapsulate such things is
+more desirable than patching the actual driver program. (well, except
+to move it toward the API of the library).
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ 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.)
+\f
+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.
+\f
+ 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.
+\f
+ 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
+\f
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 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.
--- /dev/null
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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
+\f
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+If you start a new file, you get to choose the style.
+If you change an existing file, follow the existing style.
+
+Hard tabs are OK, as long as you consider the tab stops to
+be every 8 characters. You can also use 2, 3, or 4 spaces.
+Tabs are kind of yucky, since cut-and-paste mangles them
+sometimes and they make "diff -Naurd old new" output less
+readable.
+
+Spaces within a line don't matter much, and won't be
+considered part of the style. Just make it readable:
+
+if(x){ // OK
+if( x ){ // OK
+if (x) { // OK
+if(x==y && a==b){ // OK
+if(x == y && a == b){ // poor
+if(x==y&&a==b){ // poor
+
+This is evil:
+
+szWinStallman
+FoulCodingStyle (int iInsanity)
+ {
+ if (iInsanity)
+ {
+ GoHackEmacs () ;
+ }
+ else
+ {
+ SeekHelpForYourLisp () ;
+ }
+ }
+
+
+Curly braces belong at the end of a line. If you must, go ahead
+and make function bodies an exception to that rule. (as Linus does)
+
+Big fprintf() calls and similar go like this:
+
+fprintf(fd, "%d %d %d %d %d %d\n",
+ sdfsdf_sdfsdf + sdfs_iii, // not an example of good names!
+ iijjij,
+ kjfkkj_sdfssss_sfff,
+ sdflkjfdskj + sdf - sfds,
+ jksss,
+ sfssss + wwwwfwfw
+);
+
+Keep these distinct: NULL, '\0', 0, 0.0
+
+Command-line parsers need to be bomb-proof. It is not acceptable
+to crash due to a messed up command-line. For an option "-x" that
+takes an argument, accept both "-x arg" and "-xarg". Remember to
+support "--" and "--version".
+
+Be extremely careful when handling data from other users.
+For example, it is a security hole if /proc/123/cmdline can
+overflow an array. It is often a security hole if you allow
+non-ASCII characters to be printed. Assuming the console is
+not in UTF-8 mode, all of these are bad: "\b\e\f\n\r\t\v\x9b".
+(the "\x9b" is valid in UTF-8 mode, but equivalent to "\e["
+when not in UTF-8 mode -- which gives control of terminal
+settings) It's best if you consider user-supplied data to
+be unsafe, since this makes for less work in case the code
+ends up needing to run setuid. Termcap data is user-supplied.
+Except for the above security issues, don't bother to check
+for something you can't handle... like printf() failing.
+It is expected that /dev exists and so on.
+
+Remember that a read() may return early, with partial data
+or with -1 and errno set to EINTR. You then must try again.
+
+char: may be signed or unsigned by default
+int: always 32-bit
+long long: always 64-bit
+pointer: either 32-bit or 64-bit
+long: same size as a pointer
+KLONG: same size as a pointer or long IN THE KERNEL
+
+Functions used in just one file must be marked static.
+Use the "const" and "restrict" keywords wherever you can.
+
+Put main() at the bottom of a file so you don't need all
+those ugly forward declarations.
+
+Avoid the strcat() function. It is slow. For some odd
+reason, snprintf() is faster than sprintf().
+
+Reuse memory allocations when you can. When using realloc(),
+do your increments by more than one. 25% is a nice amount.
+
+Avoid compile-time choices. They make documentation difficult,
+and they are not friendly to binary distribution.
+
+Write programs that can handle a million processes without
+getting hopelessly slow. Allow for /proc/123/cmdline to
+be at least 128 kB.
+
+The LGPL license is strongly preferred. This allows use of
+the code in the library.
--- /dev/null
+# procps Makefile
+# Albert Cahalan, 2002-2004
+#
+# Recursive make is considered harmful:
+# http://google.com/search?q=%22recursive+make+considered+harmful%22
+#
+# For now this Makefile uses explicit dependencies. The project
+# hasn't grown big enough to need something complicated, and the
+# dependency tracking files are an ugly annoyance.
+#
+# This file includes */module.mk files which add on to variables:
+# FOO += bar/baz
+#
+#
+# Set (or uncomment) SKIP if you wish to avoid something.
+# For example, you may prefer the /bin/kill from util-linux or bsdutils.
+
+
+VERSION := 3
+SUBVERSION := 2
+MINORVERSION := 8
+TARVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION)
+
+############ vars
+
+# so you can disable them or choose alternates
+ldconfig := ldconfig
+ln_f := ln -f
+ln_sf := ln -sf
+install := install -D --owner 0 --group 0
+
+# Lame x86-64 /lib64 and /usr/lib64 abomination:
+lib64 := lib$(shell [ -d /lib64 ] && echo 64)
+
+usr/bin := $(DESTDIR)/usr/bin/
+bin := $(DESTDIR)/bin/
+sbin := $(DESTDIR)/sbin/
+usr/proc/bin := $(DESTDIR)/usr/bin/
+man1 := $(DESTDIR)/usr/share/man/man1/
+man5 := $(DESTDIR)/usr/share/man/man5/
+man8 := $(DESTDIR)/usr/share/man/man8/
+lib := $(DESTDIR)/$(lib64)/
+usr/lib := $(DESTDIR)/usr/$(lib64)/
+usr/include := $(DESTDIR)/usr/include/
+
+#SKIP := $(bin)kill $(man1)kill.1
+
+BINFILES := $(usr/bin)uptime $(usr/bin)tload $(usr/bin)free $(usr/bin)w \
+ $(usr/bin)top $(usr/bin)vmstat $(usr/bin)watch $(usr/bin)skill \
+ $(usr/bin)snice $(bin)kill $(sbin)sysctl $(usr/bin)pmap \
+ $(usr/proc/bin)pgrep $(usr/proc/bin)pkill $(usr/bin)slabtop \
+ $(usr/proc/bin)pwdx
+
+MANFILES := $(man1)uptime.1 $(man1)tload.1 $(man1)free.1 $(man1)w.1 \
+ $(man1)top.1 $(man1)watch.1 $(man1)skill.1 $(man1)kill.1 \
+ $(man1)snice.1 $(man1)pgrep.1 $(man1)pkill.1 $(man1)pmap.1 \
+ $(man5)sysctl.conf.5 $(man8)vmstat.8 $(man8)sysctl.8 \
+ $(man1)slabtop.1 $(man1)pwdx.1
+
+TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \
+ Makefile procps.lsm procps.spec v t README.top CodingStyle \
+ sysctl.conf minimal.c $(notdir $(MANFILES)) dummy.c \
+ uptime.c tload.c free.c w.c top.c vmstat.c watch.c skill.c \
+ sysctl.c pgrep.c top.h pmap.c slabtop.c pwdx.c
+
+# Stuff (tests, temporary hacks, etc.) left out of the standard tarball
+# plus the top-level Makefile to make it work stand-alone.
+_TARFILES := Makefile
+
+CURSES := -lncurses
+
+# This seems about right for the dynamic library stuff.
+# Something like this is probably needed to make the SE Linux
+# library loading not conflict with embedded systems stuff.
+#
+#ifeq ($(SHARED),1)
+#ldl := -ldl
+#LIBTYPE := -DSHAREDLIB
+#else
+#LIBTYPE := -DSTATICLIB
+#endif
+
+# Preprocessor flags.
+PKG_CPPFLAGS := -D_GNU_SOURCE -I proc
+CPPFLAGS := -I/usr/include/ncurses
+ALL_CPPFLAGS := $(PKG_CPPFLAGS) $(CPPFLAGS)
+
+# Left out -Wconversion due to noise in glibc headers.
+# Left out -Wunreachable-code and -Wdisabled-optimization
+# because gcc spews many useless warnings with them.
+#
+# Since none of the PKG_CFLAGS things are truly required
+# to compile procps, they might best be moved to CFLAGS.
+# On the other hand, they aren't normal -O -g things either.
+#
+# Note that -O2 includes -fomit-frame-pointer only if the arch
+# doesn't lose some debugging ability.
+#
+PKG_CFLAGS := -fno-common -ffast-math \
+ -W -Wall -Wshadow -Wcast-align -Wredundant-decls \
+ -Wbad-function-cast -Wcast-qual -Wwrite-strings -Waggregate-return \
+ -Wstrict-prototypes -Wmissing-prototypes
+# Note that some stuff below is conditional on CFLAGS containing
+# an option that starts with "-g". (-g, -g2, -g3, -ggdb, etc.)
+CFLAGS := -O2 -s
+ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS)
+
+PKG_LDFLAGS := -Wl,-warn-common
+LDFLAGS :=
+ALL_LDFLAGS := $(PKG_LDFLAGS) $(LDFLAGS)
+
+############ Add some extra flags if gcc allows
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),tar)
+ifneq ($(MAKECMDGOALS),extratar)
+ifneq ($(MAKECMDGOALS),beta)
+
+# Unlike the kernel one, this check_gcc goes all the way to
+# producing an executable. There might be a -m64 that works
+# until you go looking for a 64-bit curses library.
+check_gcc = $(shell if $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) dummy.c $(ALL_LDFLAGS) $(1) -o /dev/null $(CURSES) > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+# Be 64-bit if at all possible. In a cross-compiling situation, one may
+# do "make m64=-m32 lib64=lib" to produce 32-bit executables. DO NOT
+# attempt to use a 32-bit executable on a 64-bit kernel. Packagers MUST
+# produce separate executables for ppc and ppc64, s390 and s390x,
+# i386 and x86-64, mips and mips64, sparc and sparc64, and so on.
+# Failure to do so will cause data corruption.
+m64 := $(call check_gcc,-m64,$(call check_gcc,-mabi=64,))
+ALL_CFLAGS += $(m64)
+
+ALL_CFLAGS += $(call check_gcc,-Wdeclaration-after-statement,)
+ALL_CFLAGS += $(call check_gcc,-Wpadded,)
+ALL_CFLAGS += $(call check_gcc,-Wstrict-aliasing,)
+
+# Adding -fno-gcse might be good for those files which
+# use computed goto.
+#ALL_CFLAGS += $(call check_gcc,-fno-gcse,)
+
+# if not debugging, enable things that could confuse gdb
+ifeq (,$(findstring -g,$(filter -g%,$(CFLAGS))))
+ALL_CFLAGS += $(call check_gcc,-fweb,)
+ALL_CFLAGS += $(call check_gcc,-frename-registers,)
+ALL_CFLAGS += $(call check_gcc,-fomit-frame-pointer,)
+endif
+
+# in case -O3 is enabled, avoid bloat
+ALL_CFLAGS += $(call check_gcc,-fno-inline-functions,)
+
+endif
+endif
+endif
+endif
+
+############ misc.
+
+# free.c pmap.c sysctl.c uptime.c vmstat.c watch.c pgrep.c skill.c tload.c top.c w.c
+# utmp.c oldtop.c tmp-junk.c minimal.c
+
+.SUFFIXES:
+.SUFFIXES: .a .o .c .s .h
+
+.PHONY: all clean do_all install tar extratar beta
+
+ALL := $(notdir $(BINFILES))
+
+CLEAN := $(notdir $(BINFILES))
+
+DIRS :=
+
+INSTALL := $(BINFILES) $(MANFILES)
+
+# want this rule first, use := on ALL, and ALL not filled in yet
+all: do_all
+
+-include */module.mk
+
+do_all: $(ALL)
+
+junk := DEADJOE *~ *.o core gmon.out
+
+# Remove $(junk) from all $(DIRS)
+CLEAN += $(junk) $(foreach dir,$(DIRS),$(addprefix $(dir), $(junk)))
+
+##########
+# not maintained because it isn't really needed:
+#
+#SRC :=
+#OBJ := $(patsubst %.c,%.o, $(filter %.c,$(SRC)))
+#
+#ifneq ($(MAKECMDGOALS),clean)
+#-include $(OBJ:.o=.d)
+#endif
+#
+#%.d: %.c
+# depend.sh $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< > $@
+############
+
+# don't want to type "make procps-$(TARVERSION).tar.gz"
+tar: $(TARFILES)
+ mkdir procps-$(TARVERSION)
+ (tar cf - $(TARFILES)) | (cd procps-$(TARVERSION) && tar xf -)
+ tar cf procps-$(TARVERSION).tar procps-$(TARVERSION)
+ gzip -9 procps-$(TARVERSION).tar
+
+extratar: $(_TARFILES)
+ mkdir procps-$(TARVERSION)
+ (tar cf - $(_TARFILES)) | (cd procps-$(TARVERSION) && tar xf -)
+ tar cf extra-$(TARVERSION).tar procps-$(TARVERSION)
+ gzip -9 extra-$(TARVERSION).tar
+
+beta: $(TARFILES) $(_TARFILES)
+ mkdir beta-$(TARVERSION)
+ (tar cf - $(TARFILES) $(_TARFILES)) | (cd beta-$(TARVERSION) && tar xf -)
+ tar cf beta-$(TARVERSION).tar beta-$(TARVERSION)
+ gzip -9 beta-$(TARVERSION).tar
+
+clean:
+ rm -f $(CLEAN)
+
+###### install
+
+$(BINFILES) : all
+ $(install) --mode a=rx $(notdir $@) $@
+
+$(MANFILES) : all
+ $(install) --mode a=r $(notdir $@) $@
+
+install: $(filter-out $(SKIP) $(addprefix $(DESTDIR),$(SKIP)),$(INSTALL))
+ cd $(usr/bin) && $(ln_f) skill snice
+ cd $(usr/proc/bin) && $(ln_f) pgrep pkill
+
+############ prog.c --> prog.o
+
+top.o : top.h
+
+%.o : %.c
+ $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c -o $@ $<
+
+w.o: w.c
+ $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(W_SHOWFROM) -c $<
+
+############ prog.o --> prog
+
+pmap w uptime tload free sysctl vmstat utmp pgrep skill pwdx: % : %.o $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@
+
+slabtop top: % : %.o $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES)
+
+watch: % : %.o
+ $(CC) $(ALL_CFLAGS) $^ $(ALL_LDFLAGS) -o $@ $(CURSES)
+
+############ progX --> progY
+
+snice kill: skill
+ $(ln_f) skill $@
+
+pkill: pgrep
+ $(ln_f) pgrep pkill
--- /dev/null
+procps-3.2.7 --> procps-3.2.8
+
+ps: allow "+" in sort specifications, as in man page rh208217
+ps: recognize SCHED_ISO and SCHED_IDLE
+ps: document SCHED_BATCH and add a "see also" for stime
+ps: man page less ambiguous
+top: normal exit code should be 0 #341272 #354255 rh199174
+top: misc fixes
+pgrep: usage error should exit with 2 #413383
+vmstat: use EXIT_FAILURE -- thanks Yoshio Nakamura #425492
+sysctl: fix crash -- thanks Steinar Gunderson #423704
+watch: tolerate umlauts #207103
+pmap: range limits with -A low,high
+update /dev/tty* info to May 2009 devices.txt
+don't read off end of string const rh469495 rh498182
+
+procps-3.2.6 --> procps-3.2.7
+
+top: document H option -- thanks Tony Ernst
+top: terabytes -- thanks Tony Ernst
+top: CPU usage column width fixes -- thanks Tony Ernst
+top: *roff change #326517
+ps: SCHED_BATCH is B
+ps: fix s format (signals) output with thread display
+watch: avoid integer overflow for the time delay
+pwdx: buffer overflow fixed -- thanks Ulf Harnhammar
+procps.spec needed a slash -- thanks Jesse Brandeburg
+w: stale utmp entries snuck in via uninitialized var -- thanks Robert A Basch
+pgrep/pkill: fix some realloc-related crashes #353894
+pgrep/pkill: g is criteria (PLD Linux, patch found in locked filing cabinet)
+sysctl: use - for stdin (PLD Linux: beware of the leopard)
+top: show CPU time stolen from a virtual machine
+
+procps-3.2.5 --> procps-3.2.6
+
+vmstat: /proc/stat buffer big enough for 1024 CPUs
+dietlibc needs termios.h for struct winsize -- thanks Thomas Ogrisegg
+top: can do per-task display -- thanks John Blackwood rh114012
+more MIPS crud -- thanks Jim Gifford and Ryan Oliver
+begin prep for setuid
+top: fix %CPU max on 2..9 CPU SMP -- thanks Ga*tan LEURENT rh110555
+ps: fix crash related to realloc -- thanks David Houlder
+ps: man page more detailed #334682
+spelling fixes #300333 #334684 #334685
+top: crash on resize fixed -- thanks Michal Maruska #320289
+vmstat: -p handles /dev/ and does not overflow #319523 #330969
+
+procps-3.2.4 --> procps-3.2.5
+
+display problem on 64-bit systems fixed #287947
+top: variable-width PID and PPID
+top: variable-width %CPU rh110555
+sysctl: better error messages
+ps: security labels can contain any printable ASCII
+top: help and version message on stdout, with exit(0) #283541
+ps: SIGTSTP and SIGTTOU shouldn't print bug email address #246123
+slabtop: compile with glibc 2.2.17 (and older, likely)
+slabtop: fix overflow on huge NUMA boxes #264640
+slabtop: accept any slabinfo 2.x format g77301 #287691 rh145369 rh145906
+ps: alignment after WCHAN fixed ub5385
+pmap: when no -x or -d option, show full path rh142751
+
+procps-3.2.3 --> procps-3.2.4
+
+support 64-bit MIPS with n32 binary
+sparc32 optimized for sparc32 again
+pwdx: new command -- thanks Nicholas Miell
+ps: UTF-8 username + command -- thanks Karel Zak rh134763,rh112518,rh134780
+ps: more room for some columns
+ps: tolerate SubDomain security module CONTEXT/LABEL data #277401
+watch: passes COLUMNS and LINES in environment
+top: in batch mode, tolerate unknown $TERM -- thanks Daniel Walsh
+pkill: quiet about processes that die before kill()
+
+procps-3.2.2 --> procps-3.2.3
+
+avoid truncating long usernames
+avoid warning about -lncurses when not linking (more)
+new names for shared libraries (packagers: watch out!)
+"make install" no longer rebuilds everything
+wchan now '*' for multi-threaded processes
+ps: new man page -- thanks Nicolas Francois
+ps: STAT shows l for multi-threaded processes
+vmstat: some overflow problems fixed -- thanks Holger Kiehl
+sysctl: man page fix
+
+procps-3.2.1 --> procps-3.2.2
+
+new packager (downstream maintainer) guidelines in README
+move striping from install command to CFLAGS
+new gcc options in use: -fweb, -frename-registers, etc.
+avoid warning about -lncurses when not linking -- thanks FLWM
+tolerate IA-64 headers without PAGE_SIZE
+ps: k option, same as --sort
+ps: personality-specific -x support (HP-UX and SVR4-MP)
+pgrep: man page SEE ALSO updated #226817
+sysctl: -q and -N options
+sysctl: better error handling of failed writes
+top: tolerate sparse CPU numbering
+top: try to handle terminals lacking rmam and smam #235003
+top: xterm dislikes clear-to-eol at eol (char lost)
+vmstat: fixed -d
+watch: allow sub-second intervals -- thanks Thomas Stewart
+
+procps-3.2.0 --> procps-3.2.1
+
+build even w/ curses in an odd location -- thanks to Segher Boessenkool
+ps: STAT flags for fg process group and session leader
+ps: STAT flags for swapped out process dropped (was broken)
+ps: new -M and Z options for security data (SE Linux, etc.)
+slabtop: detect broken /proc/slabinfo -- thanks to Fabian Frederick
+slabtop: ensure that error messages show up on the screen -- FF again
+
+procps-3.1.15 --> procps-3.2.0
+
+build on IA-64 again #227933
+pmap: output like Solaris 9, not Solaris 7
+ps: also handle SELinux on the 2.4.xx kernels
+top: during a ^Z, the terminal was messed up #228822
+future-proof the tty handling (thanks to Zhou Wei)
+slabtop (Chris Rivera and Robert Love) #226778
+pmap: detect the primary stack
+pmap: -d format
+free: report high and low memory
+
+procps-3.1.14 --> procps-3.1.15
+
+install to /lib64 if it exists
+hide kernel PID bug (Linux 2.4.13-pre1 to 2.4.MAX) #217278 #219730 #217525 #224470
+ps: faster threaded display
+top: auto-margin problem #217559
+ps: support NSA SELinux, all builds, Linux 2.6+ #193648
+sysctl: tweak man page for ESR's broken parser
+
+procps-3.1.13 --> procps-3.1.14
+
+top: displays on more genuine serial terminals
+handle 32-bit dev_t of Linux 2.6
+ps: finally, m and -m satisfy the original design
+ps: distinct per-thread and whole-process pending signals
+
+procps-3.1.12 --> procps-3.1.13
+
+ps: can display NPTL threads w/ kernel patch
+no seLinux for now (new kernel interface)
+
+procps-3.1.11 --> procps-3.1.12
+
+ps: explicit width ("ps -o pid,wchan:42,args")
+ps: $PS_FORMAT works properly #201575
+top: new Linux 2.6.0-test4 CPU stats shown
+top: multiple -p options work again
+top: fixed 4 GB wrap-around
+ps: has a set of tests to ensure correctness
+man page: /var/run/utmp, not /etc/utmp #206583
+required flags moved out of CFLAGS #205429
+RPM generation handles /lib64
+WCHAN skips leading '.'
+vmstat: numerous new features
+
+procps-3.1.10 --> procps-3.1.11
+
+compile with gcc 2.95 again (C99 issue)
+
+procps-3.1.9 --> procps-3.1.10
+
+handle GPLONLY_ symbols #143549 #188374
+kill: better man page
+skill: better man page
+ps: PID-like columns change width as needed
+top: COMMAND instead of Command
+vmstat: -m displays slabinfo
+vmstat: -d displays disk stats
+
+procps-3.1.8 --> procps-3.1.9
+
+memory sizes fixed for 64-bit w/ gcc 3.x #194376 #191933
+ps: detect broken OS install w/o /proc mounted #172735
+top: fix suspend/resume behavior
+top: ditch warning until a GOOD interface is found #188271
+kill: more info in the man page #182414
+ps: document the -o, o, -O, and O options #169301
+vmstat: choose units you like: 1000, 1024, 1000000...
+
+procps-3.1.7 --> procps-3.1.8
+
+top: fix keyboard handling (help screen, etc.)
+
+procps-3.1.6 --> procps-3.1.7
+
+Makefile: made SKIP feature easier to use
+watch: --help now explains -t, --no-title #182246
+ps: warning directs users to the FAQ
+top: batch mode can refresh by fractional seconds
+top: faster start-up
+top: do not refresh like crazy
+ps: better crash message
+
+procps-3.1.5 --> procps-3.1.6
+
+handle the 2.5.61 kernel
+top: memory leak fixed
+ps: new --ppid option selects by PPID
+watch: new --no-title option #179862
+handle SPARC Linux badness
+rare crash fixed
+compile with gcc 2.91.xx again
+more informative "ps --info"
+README update
+ps: compare more with "ps -C verylongname" #178127
+
+procps-3.1.4 --> procps-3.1.5
+
+ancient (2.x.xx era) data corruption fixed
+serious hidden-process problem (3.1.3+) fixed
+w: escape sequence vulnerability fixed
+
+procps-3.1.3 --> procps-3.1.4
+
+top: was trashing every "3" in a command name
+top: when killing a process, the PID was cut at a "3"
+top: more reliable %CPU
+update copyright dates (GPL & LGPL require this)
+RPM generation works now
+
+procps-3.1.2 --> procps-3.1.3
+
+uses /proc/*/wchan files when available
+top: user selection
+sysctl: add -e for Red Hat 8.0 boot scripts
+sysctl: the obvious --help, -V, and --version
+sysctl: some command line error checking
+w: stdout, not stderr -- thanks to Sander van Malssen
+
+procps-3.1.1 --> procps-3.1.2
+
+better RPM generation
+use C99 features
+some seLinux fixes
+now count Inact_laundry as needed #172163
+ps: fewer globals
+ps: hardware-enforced buffer protection
+ps: 1 kB smaller
+top: B command added (for bold on/off)
+top: handle old (and future) config files
+top: man page tweak
+top: old sort keys #167249
+top: out-of-bounds RT as "RT"
+top: several times faster
+top: t command fixed
+vmstat: -f
+vmstat: -s
+w: much faster
+watch: don't drop empty lines #171005
+watch: re-indented
+
+procps-3.1.0 --> procps-3.1.1
+
+vmstat faster on 2.5.xx kernels
+vmstat header fixed
+vmstat -a re-fixed
+
+procps-3.0.5 --> procps-3.1.0
+
+vmstat displays IO-wait time instead of bogus "w"
+can build w/o shared library (set SHARED=0)
+when IO-wait hidden, count as idle, not as sys
+pmap command added (like Sun has)
+do not crash GNU make 3.79
+top slightly faster
+
+procps-3.0.4 --> procps-3.0.5
+
+top tolerates super-wide displays
+better (?) RPM generation
+XConsole and top.desktop removed
+old build system removed
+code cleanup
+pgrep and pkill get "-o" (oldest matching process)
+had vmstat "bi" and "bo" output interchanged on 2.5.xx
+fix man page tbl directives
+top man page cleaned up
+
+procps-3.0.3 --> procps-3.0.4
+
+make top go faster
+Linux 2.2.xx ELF note warning removed
+only show IO-wait on recent kernels
+fix top's SMP stats
+fix top for "dumb" and "vt510" terminals
+in top, limit the priority values to -99 ... 99
+
+procps-3.0.2 --> procps-3.0.3
+
+more "make install" fixes
+lib CFLAGS working again
+top.1 codes fixed
+bad (int*) cast in top removed
+top runs faster
+libproc memory corruption fixed
+rant moved out of top.1 man page
+ability to SKIP installing things
+fixed ps --sort crash
+
+procps-3.0.1 --> procps-3.0.2
+
+top defaults to the old layout
+top defaults to sorting by %CPU
+fix top for non-SMP 2.2.xx and 2.0.xx
+new "make install" fixed
+vmstat -a fixed
+vmstat compiles with latest gcc-3.x
+vmstat does 64-bit time
+
+procps-3.0.0 --> procps-3.0.1
+
+sysctl handles net/ipv4/conf/eth1.0123/tag (VLAN interface)
+sysctl handles net.ipv4.conf.eth1/0123.tag (VLAN interface)
+"ps" is now about 2x faster than in procps-2.x.x
+"ps -F" now documented
+w works in KOI8-R locale
+vmstat documentation update
+"skill -n blah blah blah" lets you test options
+simple "make && make install" now
+
+procps-2.x.x --> procps-3.0.0
+
+designed to support Linux 2.0 through 2.5.41 and beyond
+new top, with optional: color, windowing, SMP stats
+runs faster
+more "it crashes" bugs fixed
+top shows IO-wait time
+vmstat can show active/inactive memory stats
+real-time info supported in ps
+correct "ps -o size" and "ps --sort size"
+new maintainers
+reduced memory usage for ps
+allow large PIDs to be specified
+SELINUX support is just a recompile away
+the "F" column shrank, so "ps -l" has more command name room
+64-bit time reduces the overflow problem
+support S/390, IA-64 emulator, and user-mode Linux
+oldps is gone
+configure script -- use "make -f Makefile.noam" as a backup
+"w" program better at determining what a user is doing
+more stable
+code at http://procps.sf.net/ now (SourceForge)
+
+Earlier changes, for those not using Debian already:
+
+more stable
+runs faster
+-F format option
+better error reporting in ps for unknown format specifiers
+BSD's sysctl options -b and -X
+top displays well on large-memory systems
+old BSD-style select-by-PID ("ps l$$")
+15-character user names
+ps 'f' ASCII art forest fixed
+add SIGSYS on i386
+top reports real RSS value
+large-memory systems work
+minimal ps program for embedded systems (minimal.c)
+BSD personality process selection fixed
+support locale (French) with ',' and '.' mixed up
+pgrep program
+includes the "kill" and "nice" programs
+don't chop non-tty ps output at 80 columns
--- /dev/null
+COMPATIBILITY
+
+ This code is intended for use with Linux 2.2.xx, 2.4.xx,
+ 2.6.xx, and hopefully all future kernels. You should be
+ running a system with libc 6, but libc 5 might work too.
+
+INSTALLATION
+
+ make
+ make install
+
+ Only the second ("make install") is needed if you just
+ want to build and install procps in the normal way.
+
+ If you wish to test before installing, use the scripts
+ named t, v, and p to ensure that the correct libproc
+ (the new one) is used during your testing.
+
+ You may set SKIP to avoid building or installing things.
+ For example:
+
+ make SKIP='/bin/kill /usr/share/man/man1/kill.1' install
+
+ Use SHARED=0 to build procps without shared libraries.
+ This may be useful for installing in your home directory.
+
+ make SHARED=0 DESTDIR=$HOME install
+
+ Suppose you wanted to install stuff in strange places.
+ You might do something like this:
+
+ make usr/bin=/tmp/Q/i/ DESTDIR=/tmp/Q install="install -D" ldconfig=echo install
+
+ If cross-compiling, you might need to set lib64 to
+ either "lib" or "lib64". You might need to set m64 to
+ -m64, -m32, or nothing at all. Some examples:
+
+ make lib64=lib m64=-m32 # for a bi-arch gcc
+ make lib64=lib64 CC=x86_64-gcc
+ make lib64=lib CC=alpha-gcc
+
+PACKAGING
+
+ If you are a downstream maintainer (packager) for a Linux distribution,
+ please avoid causing troubles. This section applies to you.
+
+ Send patches in regularly. Many patches made by vendors have been buggy,
+ some quite severely so. Sending in a patch will at least get it reviewed,
+ if not included. There is a procps test suite that must be passed.
+ Forward all bug reports. If your bug database is public and busy enough
+ to bother with, please make this known. Follow Debian's lead in making
+ the bug database easy to comment on via email w/o need for an account.
+
+ Do not change the user interface. Many of the programs are intended to be
+ compatible with Solaris, FreeBSD, AIX, IRIX, Tru64, and the UNIX standard.
+ Your nice new command options WILL BE BROKEN as needed to ensure that
+ procps remains compatible with the rest of the world. Sysadmins hate to
+ deal with incompatible behavior. If you need a new option, ask for it.
+
+ For normal packages, ensure that you do not add debugging flags
+ to the CFLAGS variable. If debugging flags are present, the Makefile
+ will avoid adding several optimizations that would interfere with gdb.
+
+ There should be no need to modify the Makefile. You can set variables
+ on the "make" command line or use "make -e" to pass variables from
+ the environment.
+
+BUG REPORTS
+
+ Debian users should use the Debian bug tracking system.
+ Email to albert@users.sf.net or csmall@debian.org or
+ procps-feedback@lists.sf.net will also work.
--- /dev/null
+Credit for this belongs to:
+Jim / James C. Warner, <warnerjc@worldnet.att.net>
+
+----------------------------------
+
+Ok, ok, I yield -- most of what follows has been removed from the manual page
+and packaged separately as this README (hey, it was only TEMPORARY insanity).
+
+Of course, that means that now absolutely nobody will ever read it.
+
+This is probably a good thing...
+
+
+## Table of Contents ---------------------------------------------------##
+ # the only darn thing that wasn't in the man page
+ CUSTOMIZING the Sources
+ # the following carry their original topic numbers
+ DIFFERENCES / New Features
+ Interface Etiquette
+ Expanded Configurable Display Support
+ Enhanced Field/Column Management
+ Customization Flexibility
+ NOTES and Rantings
+ The top Binary
+ Comparing Performance
+ Cost of Stuff
+ The top Sources
+ EXAMPLES of Windows
+ The 'A' Mode Command Toggle
+ STACKIN' & WHACKIN' Windows
+ ALL TOGETHER Now, Window(s)
+
+
+## CUSTOMIZING the Sources ---------------------------------------------##
+
+Listed below are the conditionals available should you wish to recompile
+this top. The author's favorite is: PRETEND4CPUS.
+
+That's the #define allowing you to simulate an SMP environment, and
+(perhaps) impress your friends. It's currently set to display four
+separate CPUs, but could easily be changed.
+
+ Caution: do NOT use this provision in an effort to impress someone
+ who truly possesses such a machine! The fact that all 4
+ CPUs show the same dynamic results will likely have the
+ opposite effect.
+
+
+//#define ATEOJ_REPORT /* report a bunch of stuff, at end-of-job */
+//#define CASEUP_HEXES /* show any hex values in upper case */
+//#define CASEUP_SCALE /* show scaled time/num suffix upper case */
+//#define CASEUP_SUMMK /* show memory summary kilobytes with 'K' */
+//#define POSIX_CMDLIN /* use '[ ]' for kernel threads, not '( )' */
+//#define PRETEND2_5_X /* pretend we're linux 2.5.x (for IO-wait) */
+//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */
+//#define PRETENDNOCAP /* use a terminal without essential caps */
+//#define SORT_SUPRESS /* *attempt* to reduce qsort overhead */
+//#define STDOUT_IOLBF /* disable our own stdout _IOFBF override */
+//#define USE_LIB_STA3 /* use lib status (3 ch) vs. proc_t (1 ch) */
+//#define WARN_NOT_SMP /* restrict '1' & 'I' commands to true smp */
+
+
+## 6. DIFFERENCES / New Features ---------------------------------------##
+ The following summarizes differences between this top and your
+ former top. It was originally based on procps-2.0.7. However,
+ except for the separate/summary CPU toggle, all of these differ-
+ ences also apply through procps-2.0.10.
+
+ 6a. Interface Etiquette
+ -*- Input and output are far more carefully implemented in
+ this top. You won't be subjected to 4 - 5 'Unknown command'
+ messages should you press the wrong key.
+
+ -*- You need suffer a confirmation message only when the results
+ of a command are not obvious by their effects on the display.
+
+ -*- The Help screen will no longer overflow, even when running
+ with a 24 row xterm (vt100).
+
+ -*- The fields selection/ordering screens do not carelessly
+ destroy important information through unintended line wraps.
+
+ -*- Should you narrow a xterm window to less than 80 columns
+ while this top is running, you will not be left with an
+ utterly worthless, embarrassing display.
+
+ 6b. Expanded Configurable Display Support
+ -*- In an SMP environment, you can choose between a summary dis-
+ play or you may show each cpu separately. No longer must
+ this choice be irrevocably made at startup.
+
+ -*- There are new fields and with this top, any field is
+ selectable for sorting. Plus, your sorted column can be
+ instantly reversed with just a single keystroke.
+
+ -*- You may optionally apply 2 distinct types of highlighting to
+ running tasks and/or sorted columns. With this top, you'll
+ be able to instantly spot running tasks and always know the
+ current sort field.
+
+ -*- While you could continue to use the more familiar (and
+ boring) monochrome display, you might want to try this top's
+ new color display. You can even create your own unique col-
+ ors used in summaries, messages, headings and tasks, each of
+ which can be made persistent until you choose to change them.
+
+ -*- Up to four separate windows can be displayed simultaneously,
+ giving you four separate ways to sort and view the tasks cur-
+ rently cluttering up your system. You could have one view by
+ pids, another by cpu usage, yet another showing memory con-
+ sumption. You get the idea...
+
+ -*- Each window comes with pre-configured (but user configurable)
+ fields and you can size each window individually.
+
+ -*- Virtually every one of this top's options (summaries, fields,
+ colors, sorted column, etc.) is separately configurable for
+ each of those four windows.
+
+ Heck, you can even change a window's name, if you don't care
+ for top's choices. Your changes will be reflected not only
+ when you're in what top calls alternate-display mode but also
+ on his special new 'Windows' help screen.
+
+ -*- And, [ ** Drum-Roll + Ta-Da ** ] with just one keystroke you
+ can quickly switch between full-screen and multiple window
+ modes! Or, with a different keystroke, toggle a single win-
+ dow Off for now, then On again later!!
+
+ 6c. Enhanced Field/Column Management
+ -*- Many Field/Column names have been changed to make them more
+ intuitive, more self-descriptive. And with this top you
+ won't be fooled with field choices that are "not yet imple-
+ mented".
+
+ -*- Task memory statistics are more meaningful and more accurate.
+
+ -*- You'll finally have complete display integrity regardless of
+ field selections, their order or screen width. And that
+ means the command column no longer need be kept as the right-
+ most field, lest your screen turn to <bleep> when all the
+ following columns get misaligned.
+
+ 6d. Customization Flexibility
+ -*- You have complete program naming freedom with no internal
+ ties to a specific personal configuration file. Symbolic
+ links could be used to establish different configuration
+ files reflecting the different personalities of your cus-
+ tomized "tops", under whatever aliases you've used.
+
+ Thus, you could have an alias for running top in 'Batch
+ mode', another for when you work from the Linux console and
+ maybe a third used with X-Windows. All of that, yet still
+ just a single binary image!
+
+ -*- All of your configuration choices can be preserved in a per-
+ sonal configuration file, including any changes made on a
+ per-window basis. Thus, once you personalize things they
+ remain personalized until you decide to change them again.
+ This top has been completely cured of:
+ i-cant-remember-so-please-do-that-all-over-again
+ ( and again, and again ... )
+
+ The bottom line is this: if you save your configuration
+ before quitting top, upon restart the display will appear
+ exactly as you left it. And that means you no longer have to
+ keep top running until-the-end-of-time (ok, a long time
+ anyway), lest your customizations go bye-bye.
+
+
+## 7. NOTES and Rantings -----------------------------------------------##
+ 7a. The top Binary
+ To whom it may (should) concern: this top, even with its vastly
+ expanded capabilities, is only slightly larger than the old top.
+ Were it not for extensive help text and additional sort callbacks,
+ it would be smaller.
+ Throw source carelessly at objectives, it will
+ produce equally careless machine instructions.
+ example: (num_pages - an_address)/1024 == duh?
+ kicker: document result as broken, due to elf!
+ ----------------------------------------------
+ I know you're out there, are you getting this?
+
+ Now, as for all those new capabilities like colors and windows and
+ highlighting, you'd expect this top to be the "mother of all pigs"
+ compared to old top -- right?
+
+ Yea, with this top expect following piglets:
+ . A smaller virtual image and resident footprint
+ . Slightly fewer major page faults
+ . A large reduction in minor page faults for SMP
+ . The same or better response time
+ . The same or even less CPU costs
+
+ Ideally any comparison of the old and new top should be against
+ the same libproc format (32-bit or 64-bit tics) and run in a true
+ or simulated SMP environment (producing separate CPU stats). This
+ latter requirement will coax old top into handling his own
+ '/proc/stat' access -- something this top always does, but with
+ less cost.
+
+ 7b. Comparing Performance
+ Even with equivalent libraries and '/proc/stat' access, it's dif-
+ ficult to accurately compare tops using their own displays.
+ Results for these cpu-intensive programs (who frequently exceed
+ their time-slice) generally show a wide disparity in %CPU. This
+ is due to differing call patterns, kernel preemptions and the tim-
+ ing of process snapshots. For slightly better results, start each
+ program with the following commands:
+ ./old-top -d 0.5
+ nice -n-10 ./new-top -d 0.4
+
+ While actually putting this top at a performance disadvantage, the
+ higher scheduling priority and staggered timing will periodically
+ yield a somewhat truer picture. You could even reverse those
+ roles and get similar results.
+
+ The most consistent performance results will be obtained 'off-
+ line', using your shell's time pipe or the time program itself.
+ And even in a single processor environment or without equivalent
+ libraries, total cpu costs (user time + system time) are similar.
+
+ However, this top's cpu costs ARE influenced by the capabilities
+ you choose to exploit, even if they don't SEEM to be reflected in
+ such timings. So let's examine some...
+
+ 7c. Cost of Stuff
+ Colors Cost -- Nada (almost).
+ Once the terminfo strings are built (at and during a user's
+ behest) they are SAVED with each window's stuff. And while
+ there will be extra tty escape sequences transmitted because of
+ colors, it makes no difference which 'char *' is actually used.
+
+ Highlighting Cost -- Nada (maybe), or blame it on Rio.
+ On second thought, let's blame it on the user.
+
+ For row highlighting, there is only the cost of those extra tty
+ escape sequences (same as for colors). For column highlight-
+ ing, there is a fairly significant cost associated with column
+ transition management combined with even more tty output.
+ These increased costs are incurred on every task display row.
+
+ Sooo... hey USER -- do NOT highlight COLUMNS. You shouldn't
+ need a constant visual reminder of your chosen sort field.
+ However, if you forget which field top is sorting it can serve
+ as a quick visual reminder.
+
+ Windows Cost -- Nada (if just 1 window).
+ If more than 1 window, almost certainly NOT Nada so blame it on
+ reality. Colors are not an issue, but those sort fields are.
+
+ If we could trust the user to always select the same 'c' state,
+ 'S' state and sort field (hey, why ya got multiple windows then
+ user, huh?) AND if we can trust someone to recompile top with a
+ #define enabled, then we could achieve 'Nada'.
+
+ Ok, not likely, so we're gonna' be doing multiple sorts. BUT,
+ it may not be as bad as it sounds. Those sorts involve point-
+ ers only. And, that's as good as it gets ! (right Mr. N?)
+
+ 7d. The top Sources
+ top.h
+ Unlike his predecessor, this top has a proper header file. It
+ contains ONLY declarations, NOT definitions. And there are
+ several conditionals present to help with further customiza-
+ tions and experimentation. All are Off by default.
+
+ top.c
+ Hopefully proves that source code needn't be a disorganized,
+ misaligned MESS. And, WHO says a source listing shouldn't
+ occasionally make you SMILE? Why, top.c even does a darn good
+ job of following the suggestions in a document hardly anybody
+ seems to observe.
+
+ the Linus Torvalds CodingStyle guidelines ...
+ -*- -*- -*- on indentation + etc. -*- -*- -*-
+ well almost all, except for those stinkin'...
+
+ I suppose even Linus Torvalds is entitled to err now and again.
+ How so you say? Tabs, me' bucko, stinkin' tabs! That, plus the
+ simplistic position regarding indentation espoused in that other-
+ wise excellent document.
+
+ -*- Rant On, and on -*-
+ Let's compare two approaches to the tab/indentation issue with a
+ small code sample using tabs then spaces. This snippet happens to
+ be the key to top's use of dynamic colors on many static screens,
+ while also ensuring screen width isn't exceeded so as to avoid
+ line wraps. We'll view just the first 40 columns, assuming one
+ wishes to occasionally provide comments to the right of actual
+ code (you do, don't you?).
+
+ Then YOU decide which approach makes the most SENSE!
+
+ Stinkin' Tabs versus Spaces: the Linus way
+ Hey, where'd my +----+----1----+----2----+----3----+----4+
+ many code lines | while (*sub_beg) { :
+ up-and-gone-to? | switch (*sub_end:
+ | case 0: :
+ Gosh, wonder if | \ Tabs Induced / :
+ Linus expects a | case 1: :
+ fellow to stick | + WASTE-Lands! + case 5: :
+ his comments on | :
+ the left side?! | + Not a Living + :
+ | :
+ Ever see source | + line-of-code + :
+ with not enough | :
+ whitespace; and | / To Be Found! \ :
+ this is better? | default::
+ | :
+ Oh lookie here, \ } :
+ there's just a hint of REAL code! ----> if (0 >= room) b:
+ / } /* end: while 'subtrin:
+ +----------------------------------------+
+
+ Spaces versus Stinkin' Tabs: the other way
+ +----+----1----+----2----+----3----+----4+
+ Wow, now this is | while (*sub_beg) { :
+ Visible hackin'! | switch (*sub_end) { :
+ | case 0: :
+ Hmmm, wonder how | *(sub_end + 1) = '\0'; :
+ many programmers | case 1: case 2: case 3: case:
+ read those lines | case 5: case 6: case 7: case:
+ from the LEFT to | cap = Curwin->captab[(int:
+ the RIGHT? This | *sub_end = '\0'; :
+ "innovation" may | PUTP("%s%.*s%s", cap, roo:
+ possibly benefit | room -= (sub_end - sub_be:
+ those particular | sub_beg = ++sub_end; :
+ kinds of people, | break; :
+ you agree? Duh! | default: :
+ | ++sub_end; :
+ AND, there might | } :
+ even be room for | if (0 >= room) break; :
+ unseen comments! | } /* end: while 'subtrings' */ :
+ +----------------------------------------+
+
+ Gosh, I just don't KNOW -- it's such a TOUGH choice...
+
+ Oh you Stinkin' Tabs: correspondence, Who-Cares; documentation,
+ Oh-Alright; even scripts, Well-If-You-Must. But you have NO place
+ within the code-space of MY C-source listing! So be gone
+ already!!
+
+ In Summation...
+ - If you want to use tabs to the right of the code, go-for-it.
+ But PLEASE, not ever in the C-source code-space, thank-you-
+ kindly. Just use three little ol' spaces (exactly 3, no-more,
+ no-less) where you WOULD have stuck a stinkin' tab.
+
+ We'll get far more READABLE files, much less WAISTED precious
+ horizontal space, more consistent CURSORS and on, and ON, AND
+ ON! Plus, without those awful *the-devil's-own-handiwork*, the
+ aforementioned document need NEVER speak of their EVILS again.
+
+ - Lastly, since SPACES (not stinkin' tabs) are SO beneficial,
+ maybe we should use just a few more of 'em. Some of those C-
+ thingies are VERY sensitive -- they don't like being TOUCHED
+ by any other syntax element! Which ones? Why these guys:
+
+ braces, reserved words and binary operators
+ ( it's the TRUTH, they told me themselves )
+
+ It's so EASY to keep 'em HAPPY! And lo-and-behold, the combi-
+ nation of <sp>thingy<sp> turns out to be a darn effective bug
+ repellent, too. So much so, one can actually code while
+ TOTALLY NUDE yet still avoid them ol' bug-bytes (sic-sic)!
+ step
+ down_from
+ me_punctilious
+ soap-box_once_again
+ [1 +5 +5 +5 = huh?]
+
+
+## 4c. EXAMPLES of Windows ---------------------------------------------##
+
+ -*- The 'A' Mode Command Toggle -*-
+ Here's what you'll see when you first invoke the alternate-display
+ mode interactive command.
+
+ This particular display was produce on a VT100 xterm, with only 24
+ rows. All four task displays are visible, but they could not be sized
+ the same. Available lines are parceled out in the fairest way possi-
+ ble so the last two task displays have an extra line each.
+
+ Notice the 'current' window name in the summary area -- it's been
+ emphasized because the associated task display is visible. Since
+ 1:Def has a task area, the full range of interactive commands would be
+ at your disposal. But remember, many of those commands will apply
+ only to window 1:Def.
+
+ +--------------------------------------+
+ 1:Def name is bold, |1:Def - 15:46:37 up 16:25, 9 users, :
+ thus all commands |Tasks: 76 total, 1 running, 75 sle:
+ will be available. |Cpu(s): 0.7% user, 1.3% system, :
+ |Mem: 126588k total, 123688k used,:
+ |Swap: 265032k total, 8232k used,:
+ |______________________________________:
+ Tough luck windows |1__PID_USER______PR__NI_%CPU____TIME+_:
+ #1 & 2 - you lost | 7343 jtwm 16 0 0.9 0:00.59:
+ one line each -- | 7339 jtwm 9 0 0.0 0:00.02:
+ guess you'll just |__7337_root_______9___0__0.0___0:01.30:
+ have to learn how |2__PID__PPID_Command____________TIME+_:
+ to live with it. | 997 952 kdeinit 17:59.59:
+ | 1115 952 kdeinit 2:16.47:
+ |__1803__1116_led_______________1:55.30:
+ |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_:
+ The #3 & #4 windows | 4634 12.3 15620 0 15m 860 14m :
+ better not gloat | 7337 11.3 14396 92 13m 36 13m :
+ over 1 extra line. | 923 10.6 30524 16m 13m 1120 12m :
+ That user could yet |___991__7.2__9492__316_9176___12_9164_:
+ sock 'em with the |4_UID_USER_____GROUP____TTY________PID:
+ 'n' command and | 43 xfs xfs ? 806:
+ take those lines, | 0 ykde users pts/7 5561:
+ plus others, away! | 0 wgnome users pts/7 5560:
+ | 0 root root pts/7 5325:
+ +--------------------------------------+
+
+ So, what say we start applying some of those "full range of interac-
+ tive commands"?
+
+ Onward + Downward...
+
+ -*- STACKIN' & WHACKIN' Windows -*-
+ Whoa, hold on mate. Someone has already whacked these windows. See,
+ there are no task areas for windows 1:Def and 4:Usr. Well, we can at
+ least retrace their steps...
+
+ Here's what was done, after issuing the 'A' command and entering
+ alternate-display mode.
+ 1) When #1 was the 'current' window, '-' was pressed,
+ toggling Off the associated task display
+ ( if 'l t m' had been applied to its summary, too )
+ ( then there'll be only a msg line when 'current' )
+ 2) Then the 'w' key was struck to cycle backward,
+ making 4:Usr the 'current' window
+ (could have used 'a a a', if one likes to type)
+ 3) Then step #1 was repeated, and bye-bye window #4
+ 4) Finally, window #2 was made the 'current' window
+ ( Q. how many keystrokes were used? )
+ ( A. minimum of 2: 'a a' or 'w w'. )
+
+ +--------------------------------------+
+ No 'l','t','m','1' |2:Top - 15:48:35 up 16:27, 9 users, :
+ commands have been |Tasks: 75 total, 1 running, 74 sle:
+ issued here, |Cpu(s): 2.0% user, 0.7% system, :
+ but... |Mem: 126588k total, 123712k used,:
+ |Swap: 265032k total, 8232k used,:
+ |______________________________________:
+ #2's been changed; |2__PID__PPID_Command____________TIME+_:
+ user applied a 'c' | 997 952 kdeinit: konsol 18:00.70:
+ command (when it | 1115 952 kdeinit: konsol 2:16.47:
+ was current) - now | 1803 1116 led tiptop.HELP 1:55.30:
+ shows cmd lines vs. | 923 922 X :0 1:09.60:
+ program names; | 973 1 klaptopdaemon 0:59.63:
+ still seems to be | 981 952 /usr/bin/artsd 0:48.63:
+ sorted on TIME+ | 987 1 kdeinit: kdeskt 0:24.34:
+ though |___991_____1_kdeinit:_kicker___0:04.59:
+ |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_:
+ This #3 guy appears | 4634 12.3 15620 0 15m 860 14m :
+ to still be running | 7337 11.3 14396 92 13m 36 13m :
+ with the supplied | 923 10.6 30544 16m 13m 1120 12m :
+ defaults, but no | 991 7.2 9492 316 9176 12 9164 :
+ telling what damage | 7329 7.0 9036 140 8896 36 8860 :
+ might have been | 1115 6.9 8956 160 8796 36 8760 :
+ done to it's | 987 6.4 8668 524 8144 20 8124 :
+ summary info stuff | 1131 6.4 8268 144 8124 36 8088 :
+ +--------------------------------------+
+
+ And that's what brought us to this current state. No, wait. Oh
+ lordy, will you look at that -- someone has changed the name of win-
+ dow #2 from 'Job' to 'Top'!
+
+ How'd they do that? Well, they just issued the 'g' interactive com-
+ mand, of course. That command is available whenever alternate-display
+ mode is active and always impacts just the 'current' window. Gosh,
+ you can even issue the 'g' command when 'l' has toggled Off the very
+ summary area line containing the window name!
+
+ Almost Done...
+
+ -*- ALL TOGETHER Now, Window(s) -*-
+ Here, the window 1:Def task display has been toggled Off but it
+ remains the 'current' window. Since there is no task area, many com-
+ mands will be restricted. However, the commands ('l', 't', 'm', '1')
+ affecting the summary area, as well as some other global commands
+ ('k', 'Z', etc.), would still be active.
+
+ Notice that the Mem and Swap lines are not shown. This means that the
+ loser (oops, user) has, in fact, issued the 'm' command! Now, if you
+ were to cycle the 'current' window with the 'a' or 'w' commands, the
+ task display would remain the same (except possibly growing/shrinking
+ slightly) but the summary area would change periodically.
+
+ The comments to the left of the image provide additional insights into
+ how things came to be. Note especially the comments for window 4:Usr
+ -- the one with some empty rows...
+
+ 1:Def no highlight, +--------------------------------------+
+ thus disabled cmds: |1:Def - 15:50:32 up 16:29, 9 users, :
+ b,i,n,u,x,y, etc. |Tasks: 75 total, 2 running, 73 sle:
+ & m = lost Mem/Swap |Cpu(s): 10.6% user, 0.0% system, :
+ |______________________________________:
+ 2:Job was very busy: |2__PID__PPID_Command____________TIME+_:
+ 'n' cmd, w/ 7 tasks | 80 1 ( khubd ) 0:00.00:
+ 'c' cmd, cmd line | 6 0 ( kreclaimd ) 0:00.00:
+ 'O' cmd, sort cmd | 9 1 ( mdrecoveryd ) 0:00.00:
+ 'R' cmd, sort bkwd | 11358 1 /bin/bash/ /usr 0:00.00:
+ 'x' cmd, hi column | 1297 1 /sbin/mingetty 0:00.00:
+ (when 2 WAS current) | 683 1 xinetd -stayali 0:00.00:
+ |___836_____1_login_--_root_____0:00.00:
+ 3:Mem has altered |3__PID_%MEM__VIRT_SWAP__RES_CODE_DATA_:
+ some std defaults: | 4634 12.3 15620 0 15m 860 14m :
+ 'y' turned Off | 7337 11.3 14396 92 13m 36 13m :
+ 'x' turned On | 923 10.6 30544 16m 13m 1120 12m :
+ (when 3 WAS current) | 991 7.2 9492 316 9176 12 9164 :
+ |__7329__7.0__9036__140_8896___36_8860_:
+ Huh? 4:Usr has some |4_UID_USER_____GROUP____TTY________PID:
+ blank rows! ? ? ? ? | 0 jtwm root pts/2 5561:
+ Aha, the 'i' command | 0 root root ? 5560:
+ applied (when 4 WAS | :
+ current); could be | :
+ reversed with '=', | :
+ when 4 IS current! +--------------------------------------+
+
+ Ok now, how about that 'current' window 1:Def and its unseen tasks?
+ At any time, you can quickly retrieve lost tasks in a number of ways:
+ 1) Press '-', toggling just the 'current' window
+ 2) Press '_', toggling all visible/invisible windows
+ ( 1:Def is the only window currently not shown )
+ ( afterward, it'll be the only window showing! )
+ * 3) Press '+', forcing all task displays to become visible
+ 4) Press 'A' to return to full-screen mode,
+ with only 1:Def tasks shown and without a window name
+
+ Now that should be enough ways of getting a task area visible again to
+ satisfy almost any user, don't ya think?
+
+ Note: Use #3 above when you've messed up your screen beyond
+ redemption. The four task displays will reappear, nice and even.
+ They will also have retained any customizations you had previously
+ applied, except for the 'i' (idle tasks) and 'n' (max tasks) com-
+ mands.
+
+ That's It ! Piece of Cake !! Enjoy them there windows !!!
+
--- /dev/null
+-------------------------- general ------------------------
+
+Consider using glibc obstacks for memory allocation.
+
+Implement /usr/proc/bin tools like Solaris has.
+The prstat command is interesting, like top in batch mode.
+SCO has a pstat command.
+
+Don't these really belong in the procps package?
+ killall pstree fuser lsof who
+(they are maintained elsewhere, which causes version problems)
+
+OpenBSD has a pfind command.
+
+Cache results of dev_to_tty.
+
+---------------------- kernel -------------------------
+
+Add an "adopted child" flag to mark processes that are not
+natural children of init. This can make --forest work better.
+
+Supply better data for top's CPU state display. Currently top has
+to subtract old numbers from new numbers and divide that result by
+the number of processors. The kernel won't even supply the number
+of processors in a portable way.
+
+Supply data for the ADDR and JOBC fields.
+
+Support & supply data for SL and RE.
+
+Add a /proc/*/tty symlink to eliminate guessing when /proc/*/fd is
+not accessable.
+
+Add /proc/*/.bindata files to avoid string parsing. It should be an array
+of 64-bit values on all machines. New entries go on the end and obsolete
+ones get filled in with something logical -- entries must never be deleted!
+
+Add all the stuff Solaris has. This would also replace ptrace.
+
+---------------------- watch --------------------------
+
+Tolerate UTF-8.
+
+Tolerate color, bold, underline, etc. #129334
+
+Tolerate stderr. #420377 #155227 #225549
+
+Tolerate VT100 line-drawing characters. Maybe translate them.
+
+---------------------- w --------------------------
+
+The LOGIN@ column sometimes has a space in it. This makes correct
+scripting difficult.
+
+Time formats are demented.
+
+---------------------- vmstat --------------------------
+
+Extract /proc/diskstats parsing from vmstat into libproc somewhere.
+
+--------------------- libproc ----------------------
+
+Stop storing fields with duplicate info (often different
+units: kB and pages, seconds and jiffies) in the proc_t struct.
+
+Use own readdir code (assembly language) because glibc sucks ass.
+
+---------------------- top -------------------------
+
+Share more stuff with ps.
+
+don't truncate long usernames
+
+have a --config option
+
+---------------- ps for now, maybe move to libproc ------------------
+
+With forest output and a tty named /dev/this_is_my_tty, the position
+of the command name gets messed up. (we print too many spaces) (fixed?)
+
+Fix missing stuff for these formats: FB_j FB_l FB_v HP_f HP_l HP_fl JFMT OL_m
+(jobc,cpu,sl,re,cpu,prmgrp,m_swap,m_share,vm_lib,m_dt)
+Note that "cpu" has two meanings.
+
+Add Beowulf support. This is ugly, since the current patches use a
+daemon to collect info and add a HOST field after the PID field.
+
+Query optimizer, put cheap/required process selection first.
+
+Avoid reading both /proc/*/status and /proc/*/stat.
+
+Support printing the client hostname (the FROM that w(1) uses) in place
+of a pty.
+
+Disambiguate narrow tty info. (/dev/tty7 == /dev/pts/7 now)
+1------8 1--4
+ttyS2 S2
+ttyI31 I31
+pts/7 7 Short form could be /999.
+pts/9999 9999 Short form could just be trunctuated to /999.
+tty7 7 Short form could be vc-7.
+tty63 63 Short form could be vc63.
+
+Internationalization, as specified by XPG3, Volume 1, Commands and Utilities.
+(and suggested by Unix98) LC_TIME affects date format.
+
+----------------------- ps -----------------------
+
+Add an option to select all processes that a user can kill.
+(related to RUID, EUID, tty, etc. -- but maybe ignore root power)
+
+Add a nice display option for killing things.
+ruser,euser,ppid,pid,pmem,stime,args
+
+For RT stuff:
+pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm
+
+For job control:
+stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
+
+Make the column alignment algorithm support this:
+ FOO BAR
+ 8 44444
+ 453 45
+ 45 2989
+ 63666 0
+ 34 333
+(useful for the UNIX tty and time values, since the time might look
+like 100-10:40:32 for old processes and the tty might have extra room)
+
+Improve long sort/format specifiers documentation and fill in the missing
+code as much as the kernel can support. Make sure that memory amounts are in
+pages when they should be and in kB when they should be, not backwards.
+
+output encoding: UTF8 --nul --null
+
+Make BSD formats use non-standard BSD time format, at least when it
+doesn't violate the "no whitespace" rule.
+
+Better unmangling of '?' as a tty. The shell destroys '?' when there
+is a filename that matches. If the argument seems like garbage,
+check for a file that might have screwed up the '?'.
+
+If the 'O' option is given something already implied by 'O',
+assume the user wanted a sorting option.
+
+Conflict:
+Digital THREAD is user,pcpu,pri,scnt,wchan,usertime,systime
+AIX THREAD is uname,pid,ppid,tid,S,C,PRI,scount,WCHAN,F,tty,bnd,comm
+AIX looks like this:
+ USER PID PPID TID S C PRI SC WCHAN FLAG TTY BND CMD
--- /dev/null
+// This is to test the compiler.
+
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Foul POS defines all sorts of stuff...
+#include <term.h>
+#undef tab
+
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <values.h>
+
+int main(int argc, char *argv[]){
+ (void)argc;
+ (void)argv;
+ return 0;
+}
--- /dev/null
+.\" -*-Nroff-*-
+.\" This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu.
+.\" Freely distributable under the terms of the GPL
+.TH FREE 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+free \- Display amount of free and used memory in the system
+.SH SYNOPSIS
+.BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s"
+.I delay
+.RB "] [" "\-t" "] [" "\-V" ]
+.SH DESCRIPTION
+\fBfree\fP displays the total amount of free and used physical and swap
+memory in the system, as well as the buffers used by the kernel.
+The shared memory column should be ignored; it is obsolete.
+.SS Options
+The \fB-b\fP switch displays the amount of memory in bytes; the
+\fB-k\fP switch (set by default) displays it in kilobytes; the \fB-m\fP
+switch displays it in megabytes.
+.PP
+The \fB-t\fP switch displays a line containing the totals.
+.PP
+The \fB-o\fP switch disables the display of a "buffer adjusted" line.
+If the -o option is not specified, \fBfree\fP subtracts buffer memory
+from the used memory and adds it to the free memory reported.
+.PP
+The \fB-s\fP switch activates continuous polling \fIdelay\fP seconds apart. You
+may actually specify any floating point number for \fIdelay\fP,
+.BR usleep (3)
+is used for microsecond resolution delay times.
+.PP
+The \fB\-V\fP displays version information.
+.SH FILES
+.ta
+.IR /proc/meminfo "\-\- memory information"
+.fi
+
+.SH "SEE ALSO"
+.BR ps (1),
+.BR slabtop (1),
+.BR vmstat (8),
+.BR top(1)
+
+.SH AUTHORS
+Written by Brian Edmonds.
+
+Send bug reports to <albert@users.sf.net>
+
--- /dev/null
+// free.c - free(1)
+// procps utility to display free memory information
+//
+// All new, Robert Love <rml@tech9.net> 18 Nov 2002
+// Original by Brian Edmonds and Rafal Maszkowski 14 Dec 1992
+//
+// This program is licensed under the GNU Library General Public License, v2
+//
+// Copyright 2003 Robert Love
+// Copyright 2004 Albert Cahalan
+
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+//#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define S(X) ( ((unsigned long long)(X) << 10) >> shift)
+
+const char help_message[] =
+"usage: free [-b|-k|-m|-g] [-l] [-o] [-t] [-s delay] [-c count] [-V]\n"
+" -b,-k,-m,-g show output in bytes, KB, MB, or GB\n"
+" -l show detailed low and high memory statistics\n"
+" -o use old format (no -/+buffers/cache line)\n"
+" -t display total for RAM + swap\n"
+" -s update every [delay] seconds\n"
+" -c update [count] times\n"
+" -V display version information and exit\n"
+;
+
+int main(int argc, char *argv[]){
+ int i;
+ int count = 0;
+ int shift = 10;
+ int pause_length = 0;
+ int show_high = 0;
+ int show_total = 0;
+ int old_fmt = 0;
+
+ /* check startup flags */
+ while( (i = getopt(argc, argv, "bkmglotc:s:V") ) != -1 )
+ switch (i) {
+ case 'b': shift = 0; break;
+ case 'k': shift = 10; break;
+ case 'm': shift = 20; break;
+ case 'g': shift = 30; break;
+ case 'l': show_high = 1; break;
+ case 'o': old_fmt = 1; break;
+ case 't': show_total = 1; break;
+ case 's': pause_length = 1000000 * atof(optarg); break;
+ case 'c': count = strtoul(optarg, NULL, 10); break;
+ case 'V': display_version(); exit(0);
+ default:
+ fwrite(help_message,1,strlen(help_message),stderr);
+ return 1;
+ }
+
+ do {
+ meminfo();
+ printf(" total used free shared buffers cached\n");
+ printf(
+ "%-7s %10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n", "Mem:",
+ S(kb_main_total),
+ S(kb_main_used),
+ S(kb_main_free),
+ S(kb_main_shared),
+ S(kb_main_buffers),
+ S(kb_main_cached)
+ );
+ // Print low vs. high information, if the user requested it.
+ // Note we check if low_total==0: if so, then this kernel does
+ // not export the low and high stats. Note we still want to
+ // print the high info, even if it is zero.
+ if (show_high) {
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "Low:",
+ S(kb_low_total),
+ S(kb_low_total - kb_low_free),
+ S(kb_low_free)
+ );
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "High:",
+ S(kb_high_total),
+ S(kb_high_total - kb_high_free),
+ S(kb_high_free)
+ );
+ }
+ if(!old_fmt){
+ unsigned KLONG buffers_plus_cached = kb_main_buffers + kb_main_cached;
+ printf(
+ "-/+ buffers/cache: %10Lu %10Lu\n",
+ S(kb_main_used - buffers_plus_cached),
+ S(kb_main_free + buffers_plus_cached)
+ );
+ }
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "Swap:",
+ S(kb_swap_total),
+ S(kb_swap_used),
+ S(kb_swap_free)
+ );
+ if(show_total){
+ printf(
+ "%-7s %10Lu %10Lu %10Lu\n", "Total:",
+ S(kb_main_total + kb_swap_total),
+ S(kb_main_used + kb_swap_used),
+ S(kb_main_free + kb_swap_free)
+ );
+ }
+ if(pause_length){
+ fputc('\n', stdout);
+ fflush(stdout);
+ if (count != 1) usleep(pause_length);
+ }
+ } while(pause_length && --count);
+
+ return 0;
+}
--- /dev/null
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for kill.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan; converted to a man page by
+.\" Michael K. Johnson
+.TH KILL 1 "November 21, 1999" "Linux" "Linux User's Manual"
+.SH NAME
+kill \- send a signal to a process
+
+.SH SYNOPSIS
+.TS
+l l.
+kill pid ... Send SIGTERM to every process listed.
+kill -signal pid ... Send a signal to every process listed.
+kill -s signal pid ... Send a signal to every process listed.
+kill -l List all signal names.
+kill -L List all signal names in a nice table.
+kill -l signal Convert a signal number into a name.
+kill -V,--version Show version of program
+.TE
+
+.SH DESCRIPTION
+The default signal for kill is TERM. Use -l or -L to list available signals.
+Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
+Negative PID values may be used to choose whole process groups; see the
+PGID column in ps command output. A PID of -1 is special; it indicates
+all processes except the kill process itself and init.
+
+.SH SIGNALS
+The signals listed below may be available for use with kill.
+When known constant, numbers and default behavior are shown.
+
+.TS
+lB rB lB lB
+lfCW r l l.
+Name Num Action Description
+.TH
+0 0 n/a exit code indicates if a signal may be sent
+ALRM 14 exit
+HUP 1 exit
+INT 2 exit
+KILL 9 exit this signal may not be blocked
+PIPE 13 exit
+POLL exit
+PROF exit
+TERM 15 exit
+USR1 exit
+USR2 exit
+VTALRM exit
+STKFLT exit may not be implemented
+PWR ignore may exit on some systems
+WINCH ignore
+CHLD ignore
+URG ignore
+TSTP stop may interact with the shell
+TTIN stop may interact with the shell
+TTOU stop may interact with the shell
+STOP stop this signal may not be blocked
+CONT restart continue if stopped, otherwise ignore
+ABRT 6 core
+FPE 8 core
+ILL 4 core
+QUIT 3 core
+SEGV 11 core
+TRAP 5 core
+SYS core may not be implemented
+EMT core may not be implemented
+BUS core core dump may fail
+XCPU core core dump may fail
+XFSZ core core dump may fail
+.TE
+
+.SH NOTES
+Your shell (command line interpreter) may have a built-in kill command.
+You may need to run the command described here as /bin/kill to solve
+the conflict.
+
+.SH EXAMPLES
+
+.SS
+.B "kill -9 -1"
+.nf
+Kill all processes you can kill.
+.fi
+.PP
+.SS
+.B "kill -l 11"
+.nf
+Translate number 11 into a signal name.
+.fi
+.PP
+.SS
+.B "kill -L"
+.nf
+List the available signal choices in a nice table.
+.fi
+.PP
+.SS
+.B "kill 123 543 2341 3453"
+.nf
+Send the default signal, SIGTERM, to all those processes.
+.fi
+.PP
+.SH "SEE ALSO"
+pkill(1) skill(1) kill(2) renice(1) nice(1) signal(7) killall(1)
+
+.SH STANDARDS
+This command meets appropriate standards. The -L flag is Linux-specific.
+
+.SH AUTHOR
+Albert Cahalan <albert@users.sf.net> wrote kill in 1999 to replace a
+bsdutils one that was not standards compliant. The util-linux one might
+also work correctly.
+
+Please send bug reports to <procps-feedback@lists.sf.net>
--- /dev/null
+/*
+ * Copyright 1998,2004 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/* This is a minimal /bin/ps, designed to be smaller than the old ps
+ * while still supporting some of the more important features of the
+ * new ps. (for total size, note that this ps does not need libproc)
+ * It is suitable for Linux-on-a-floppy systems only.
+ *
+ * Maintainers: do not compile or install for normal systems.
+ * Anyone needing this will want to tweak their compiler anyway.
+ */
+
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+
+#define DEV_ENCODE(M,m) ( \
+ ( (M&0xfff) << 8) | ( (m&0xfff00) << 12) | (m&0xff) \
+)
+
+///////////////////////////////////////////////////////
+#ifdef __sun__
+#include <sys/mkdev.h>
+#define _STRUCTURED_PROC 1
+#include <sys/procfs.h>
+#define NO_TTY_VALUE DEV_ENCODE(-1,-1)
+#define HZ 1 // only bother with seconds
+#endif
+
+///////////////////////////////////////////////////////
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#define NO_TTY_VALUE DEV_ENCODE(-1,-1)
+#define HZ 1 // only bother with seconds
+#endif
+
+///////////////////////////////////////////////////////
+#ifdef __linux__
+#include <asm/param.h> /* HZ */
+#include <asm/page.h> /* PAGE_SIZE */
+#define NO_TTY_VALUE DEV_ENCODE(0,0)
+#ifndef HZ
+#warning HZ not defined, assuming it is 100
+#define HZ 100
+#endif
+#endif
+
+///////////////////////////////////////////////////////////
+
+#ifndef PAGE_SIZE
+#warning PAGE_SIZE not defined, assuming it is 4096
+#define PAGE_SIZE 4096
+#endif
+
+
+
+static char P_tty_text[16];
+static char P_cmd[16];
+static char P_state;
+static int P_euid;
+static int P_pid;
+static int P_ppid, P_pgrp, P_session, P_tty_num, P_tpgid;
+static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime;
+static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_alarm;
+static unsigned long P_start_time, P_vsize;
+static long P_rss;
+static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip;
+static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch;
+static unsigned long P_wchan, P_nswap, P_cnswap;
+
+
+
+#if 0
+static int screen_cols = 80;
+static int w_count;
+#endif
+
+static int want_one_pid;
+static const char *want_one_command;
+static int select_notty;
+static int select_all;
+
+static int ps_format;
+static int old_h_option;
+
+/* we only pretend to support this */
+static int show_args; /* implicit with -f and all BSD options */
+static int bsd_c_option; /* this option overrides the above */
+
+static int ps_argc; /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg; /* index into ps_argv */
+static char *flagptr; /* current location in ps_argv[thisarg] */
+
+
+
+
+static void usage(void){
+ fprintf(stderr,
+ "-C select by command name (minimal ps only accepts one)\n"
+ "-p select by process ID (minimal ps only accepts one)\n"
+ "-e all processes (same as ax)\n"
+ "a all processes w/ tty, including other users\n"
+ "x processes w/o controlling ttys\n"
+ "-f full format\n"
+ "-j,j job control format\n"
+ "v virtual memory format\n"
+ "-l,l long format\n"
+ "u user-oriented format\n"
+ "-o user-defined format (limited support, only \"ps -o pid=\")\n"
+ "h no header\n"
+/*
+ "-A all processes (same as ax)\n"
+ "c true command name\n"
+ "-w,w wide output\n"
+*/
+ );
+ exit(1);
+}
+
+/*
+ * Return the next argument, or call the usage function.
+ * This handles both: -oFOO -o FOO
+ */
+static const char *get_opt_arg(void){
+ const char *ret;
+ ret = flagptr+1; /* assume argument is part of ps_argv[thisarg] */
+ if(*ret) return ret;
+ if(++thisarg >= ps_argc) usage(); /* there is nothing left */
+ /* argument is the new ps_argv[thisarg] */
+ ret = ps_argv[thisarg];
+ if(!ret || !*ret) usage();
+ return ret;
+}
+
+
+/* return the PID, or 0 if nothing good */
+static void parse_pid(const char *str){
+ char *endp;
+ int num;
+ if(!str) goto bad;
+ num = strtol(str, &endp, 0);
+ if(*endp != '\0') goto bad;
+ if(num<1) goto bad;
+ if(want_one_pid) goto bad;
+ want_one_pid = num;
+ return;
+bad:
+ usage();
+}
+
+/***************** parse SysV options, including Unix98 *****************/
+static void parse_sysv_option(void){
+ do{
+ switch(*flagptr){
+ /**** selection ****/
+ case 'C': /* end */
+ if(want_one_command) usage();
+ want_one_command = get_opt_arg();
+ return; /* can't have any more options */
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ case 'A':
+ case 'e':
+ select_all++;
+ select_notty++;
+case 'w': /* here for now, since the real one is not used */
+ break;
+ /**** output format ****/
+ case 'f':
+ show_args = 1;
+ /* FALL THROUGH */
+ case 'j':
+ case 'l':
+ if(ps_format) usage();
+ ps_format = *flagptr;
+ break;
+ case 'o': /* end */
+ /* We only support a limited form: "ps -o pid=" (yes, just "pid=") */
+ if(strcmp(get_opt_arg(),"pid=")) usage();
+ if(ps_format) usage();
+ ps_format = 'o';
+ old_h_option++;
+ return; /* can't have any more options */
+ /**** other stuff ****/
+#if 0
+ case 'w':
+ w_count++;
+ break;
+#endif
+ default:
+ usage();
+ } /* switch */
+ }while(*++flagptr);
+}
+
+/************************* parse BSD options **********************/
+static void parse_bsd_option(void){
+ do{
+ switch(*flagptr){
+ /**** selection ****/
+ case 'a':
+ select_all++;
+ break;
+ case 'x':
+ select_notty++;
+ break;
+ case 'p': /* end */
+ parse_pid(get_opt_arg());
+ return; /* can't have any more options */
+ /**** output format ****/
+ case 'j':
+ case 'l':
+ case 'u':
+ case 'v':
+ if(ps_format) usage();
+ ps_format = 0x80 | *flagptr; /* use 0x80 to tell BSD from SysV */
+ break;
+ /**** other stuff ****/
+ case 'c':
+ bsd_c_option++;
+#if 0
+ break;
+#endif
+ case 'w':
+#if 0
+ w_count++;
+#endif
+ break;
+ case 'h':
+ old_h_option++;
+ break;
+ default:
+ usage();
+ } /* switch */
+ }while(*++flagptr);
+}
+
+#if 0
+#include <termios.h>
+/* not used yet */
+static void choose_dimensions(void){
+ struct winsize ws;
+ char *columns;
+ /* screen_cols is 80 by default */
+ if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col;
+ columns = getenv("COLUMNS");
+ if(columns && *columns){
+ long t;
+ char *endptr;
+ t = strtol(columns, &endptr, 0);
+ if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t;
+ }
+ if(w_count && (screen_cols<132)) screen_cols=132;
+ if(w_count>1) screen_cols=999999999;
+}
+#endif
+
+static void arg_parse(int argc, char *argv[]){
+ int sel = 0; /* to verify option sanity */
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+ /**** iterate over the args ****/
+ while(++thisarg < ps_argc){
+ flagptr = ps_argv[thisarg];
+ switch(*flagptr){
+ case '0' ... '9':
+ show_args = 1;
+ parse_pid(flagptr);
+ break;
+ case '-':
+ flagptr++;
+ parse_sysv_option();
+ break;
+ default:
+ show_args = 1;
+ parse_bsd_option();
+ break;
+ }
+ }
+ /**** sanity check and clean-up ****/
+ if(want_one_pid) sel++;
+ if(want_one_command) sel++;
+ if(select_notty || select_all) sel++;
+ if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage();
+ if(bsd_c_option) show_args = 0;
+}
+
+#ifdef __sun__
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid) {
+ struct psinfo p; // /proc/*/psinfo, struct psinfo, psinfo_t
+ char buf[32];
+ int num;
+ int fd;
+ int tty_maj, tty_min;
+ snprintf(buf, sizeof buf, "/proc/%d/psinfo", pid);
+ if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
+ num = read(fd, &p, sizeof p);
+ close(fd);
+ if(num != sizeof p) return 0;
+
+ num = PRFNSZ;
+ if (num >= sizeof P_cmd) num = sizeof P_cmd - 1;
+ memcpy(P_cmd, p.pr_fname, num); // p.pr_fname or p.pr_lwp.pr_name
+ P_cmd[num] = '\0';
+
+ P_pid = p.pr_pid;
+ P_ppid = p.pr_ppid;
+ P_pgrp = p.pr_pgid;
+ P_session = p.pr_sid;
+ P_euid = p.pr_euid;
+ P_rss = p.pr_rssize;
+ P_vsize = p.pr_size;
+ P_start_time = p.pr_start.tv_sec;
+ P_wchan = p.pr_lwp.pr_wchan;
+ P_state = p.pr_lwp.pr_sname;
+ P_nice = p.pr_lwp.pr_nice;
+ P_priority = p.pr_lwp.pr_pri; // or pr_oldpri
+// P_ruid = p.pr_uid;
+// P_rgid = p.pr_gid;
+// P_egid = p.pr_egid;
+
+#if 0
+ // don't support these
+ P_tpgid; P_flags,
+ P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime;
+ P_cutime, P_cstime, P_timeout, P_alarm;
+ P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip;
+ P_signal, P_blocked, P_sigignore, P_sigcatch;
+ P_nswap, P_cnswap;
+#endif
+
+ // we like it Linux-encoded :-)
+ tty_maj = major(p.pr_ttydev);
+ tty_min = minor(p.pr_ttydev);
+ P_tty_num = DEV_ENCODE(tty_maj,tty_min);
+
+ snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
+#if 1
+ if (tty_maj == 24) snprintf(P_tty_text, sizeof P_tty_text, "pts/%-3u", tty_min);
+ if (P_tty_num == NO_TTY_VALUE) memcpy(P_tty_text, " ? ", 8);
+ if (P_tty_num == DEV_ENCODE(0,0)) memcpy(P_tty_text, "console", 8);
+#endif
+
+ if(P_pid != pid) return 0;
+ return 1;
+}
+#endif
+
+#ifdef __FreeBSD__
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid) {
+ char buf[400];
+ int num;
+ int fd;
+ char* tmp;
+ int tty_maj, tty_min;
+ snprintf(buf, 32, "/proc/%d/status", pid);
+ if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
+ num = read(fd, buf, sizeof buf - 1);
+ close(fd);
+ if(num<43) return 0;
+ buf[num] = '\0';
+
+ P_state = '-';
+
+ // FreeBSD /proc/*/status is seriously fucked. Unlike the Linux
+ // files, we can't use strrchr to find the end of a command name.
+ // Spaces in command names do not get escaped. To avoid spoofing,
+ // one may skip 20 characters and then look _forward_ only to
+ // find a pattern of entries that are {with,with,without} a comma.
+ // The entry without a comma is wchan. Then count backwards!
+ //
+ // Don't bother for now. FreeBSD isn't worth the trouble.
+
+ tmp = strchr(buf,' ');
+ num = tmp - buf;
+ if (num >= sizeof P_cmd) num = sizeof P_cmd - 1;
+ memcpy(P_cmd,buf,num);
+ P_cmd[num] = '\0';
+
+ num = sscanf(tmp+1,
+ "%d %d %d %d "
+ "%d,%d "
+ "%*s "
+ "%ld,%*d "
+ "%ld,%*d "
+ "%ld,%*d "
+ "%*s "
+ "%d %d ",
+ &P_pid, &P_ppid, &P_pgrp, &P_session,
+ &tty_maj, &tty_min,
+ /* SKIP funny flags thing */
+ &P_start_time, /* SKIP microseconds */
+ &P_utime, /* SKIP microseconds */
+ &P_stime, /* SKIP microseconds */
+ /* SKIP &P_wchan, for now -- it is a string */
+ &P_euid, &P_euid // don't know which is which
+ );
+/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+
+ snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
+ P_tty_num = DEV_ENCODE(tty_maj,tty_min);
+// tty decode is 224 to 256 bytes on i386
+#if 1
+ tmp = NULL;
+ if (tty_maj == 5) tmp = " ttyp%c ";
+ if (tty_maj == 12) tmp = " ttyv%c ";
+ if (tty_maj == 28) tmp = " ttyd%c ";
+ if (P_tty_num == NO_TTY_VALUE) tmp = " ? ";
+ if (P_tty_num == DEV_ENCODE(0,0)) tmp = "console";
+ if (P_tty_num == DEV_ENCODE(12,255)) tmp = "consolectl";
+ if (tmp) {
+ snprintf(
+ P_tty_text,
+ sizeof P_tty_text,
+ tmp,
+ "0123456789abcdefghijklmnopqrstuvwxyz"[tty_min&31]
+ );
+ }
+#endif
+
+ if(num < 9) return 0;
+ if(P_pid != pid) return 0;
+ return 1;
+}
+#endif
+
+#ifdef __linux__
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid) {
+ char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
+ int num;
+ int fd;
+ char* tmp;
+ struct stat sb; /* stat() used to get EUID */
+ snprintf(buf, 32, "/proc/%d/stat", pid);
+ if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
+ num = read(fd, buf, sizeof buf - 1);
+ fstat(fd, &sb);
+ P_euid = sb.st_uid;
+ close(fd);
+ if(num<80) return 0;
+ buf[num] = '\0';
+ tmp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+ *tmp = '\0'; /* replace trailing ')' with NUL */
+ /* parse these two strings separately, skipping the leading "(". */
+ memset(P_cmd, 0, sizeof P_cmd); /* clear */
+ sscanf(buf, "%d (%15c", &P_pid, P_cmd); /* comm[16] in kernel */
+ num = sscanf(tmp + 2, /* skip space after ')' too */
+ "%c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu %lu %lu "
+ "%ld %ld %ld %ld %ld %ld "
+ "%lu %lu "
+ "%ld "
+ "%lu %lu %lu %lu %lu %lu "
+ "%u %u %u %u " /* no use for RT signals */
+ "%lu %lu %lu",
+ &P_state,
+ &P_ppid, &P_pgrp, &P_session, &P_tty_num, &P_tpgid,
+ &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime,
+ &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_alarm,
+ &P_start_time, &P_vsize,
+ &P_rss,
+ &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip,
+ &P_signal, &P_blocked, &P_sigignore, &P_sigcatch,
+ &P_wchan, &P_nswap, &P_cnswap
+ );
+/* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+ P_vsize /= 1024;
+ P_rss *= (PAGE_SIZE/1024);
+
+ memcpy(P_tty_text, " ? ", 8);
+ if (P_tty_num != NO_TTY_VALUE) {
+ int tty_maj = (P_tty_num>>8)&0xfff;
+ int tty_min = (P_tty_num&0xff) | ((P_tty_num>>12)&0xfff00);
+ snprintf(P_tty_text, sizeof P_tty_text, "%3d,%-3d", tty_maj, tty_min);
+ }
+
+ if(num < 30) return 0;
+ if(P_pid != pid) return 0;
+ return 1;
+}
+#endif
+
+static const char *do_time(unsigned long t){
+ int hh,mm,ss;
+ static char buf[32];
+ int cnt = 0;
+ t /= HZ;
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t);
+ snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss);
+ return buf;
+}
+
+static const char *do_user(void){
+ static char buf[32];
+ static struct passwd *p;
+ static int lastuid = -1;
+ if(P_euid != lastuid){
+ p = getpwuid(P_euid);
+ if(p) snprintf(buf, sizeof buf, "%-8.8s", p->pw_name);
+ else snprintf(buf, sizeof buf, "%5d ", P_euid);
+ }
+ return buf;
+}
+
+static const char *do_cpu(int longform){
+ static char buf[8];
+ strcpy(buf," - ");
+ if(!longform) buf[2] = '\0';
+ return buf;
+}
+
+static const char *do_mem(int longform){
+ static char buf[8];
+ strcpy(buf," - ");
+ if(!longform) buf[2] = '\0';
+ return buf;
+}
+
+static const char *do_stime(void){
+ static char buf[32];
+ strcpy(buf," - ");
+ return buf;
+}
+
+static void print_proc(void){
+ switch(ps_format){
+ case 0:
+ printf("%5d %s %s", P_pid, P_tty_text, do_time(P_utime+P_stime));
+ break;
+ case 'o':
+ printf("%d\n", P_pid);
+ return; /* don't want the command */
+ case 'l':
+ printf(
+ "0 %c %5d %5d %5d %s %3d %3d - "
+ "%5ld %06x %s %s",
+ P_state, P_euid, P_pid, P_ppid, do_cpu(0),
+ (int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024),
+ (unsigned)(P_wchan&0xffffff), P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'f':
+ printf(
+ "%8s %5d %5d %s %s %s %s",
+ do_user(), P_pid, P_ppid, do_cpu(0), do_stime(), P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'j':
+ printf(
+ "%5d %5d %5d %s %s",
+ P_pid, P_pgrp, P_session, P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'u'|0x80:
+ printf(
+ "%8s %5d %s %s %5ld %4ld %s %c %s %s",
+ do_user(), P_pid, do_cpu(1), do_mem(1), P_vsize, P_rss, P_tty_text, P_state,
+ do_stime(), do_time(P_utime+P_stime)
+ );
+ break;
+ case 'v'|0x80:
+ printf(
+ "%5d %s %c %s %6d - - %5d %s",
+ P_pid, P_tty_text, P_state, do_time(P_utime+P_stime), (int)P_maj_flt,
+ (int)P_rss, do_mem(1)
+ );
+ break;
+ case 'j'|0x80:
+ printf(
+ "%5d %5d %5d %5d %s %5d %c %5d %s",
+ P_ppid, P_pid, P_pgrp, P_session, P_tty_text, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime)
+ );
+ break;
+ case 'l'|0x80:
+ printf(
+ "0 %5d %5d %5d %3d %3d "
+ "%5ld %4ld %06x %c %s %s",
+ P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice,
+ P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, P_tty_text, do_time(P_utime+P_stime)
+ );
+ break;
+ default:
+ ;
+ }
+ if(show_args) printf(" [%s]\n", P_cmd);
+ else printf(" %s\n", P_cmd);
+}
+
+
+int main(int argc, char *argv[]){
+ arg_parse(argc, argv);
+#if 0
+ choose_dimensions();
+#endif
+ if(!old_h_option){
+ const char *head;
+ switch(ps_format){
+ default: /* can't happen */
+ case 0: head = " PID TTY TIME CMD"; break;
+ case 'l': head = "F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD"; break;
+ case 'f': head = "USER PID PPID C STIME TTY TIME CMD"; break;
+ case 'j': head = " PID PGID SID TTY TIME CMD"; break;
+ case 'u'|0x80: head = "USER PID %CPU %MEM VSZ RSS TTY S START TIME COMMAND"; break;
+ case 'v'|0x80: head = " PID TTY S TIME MAJFL TRS DRS RSS %MEM COMMAND"; break;
+ case 'j'|0x80: head = " PPID PID PGID SID TTY TPGID S UID TIME COMMAND"; break;
+ case 'l'|0x80: head = "F UID PID PPID PRI NI VSZ RSS WCHAN S TTY TIME COMMAND"; break;
+ }
+ printf("%s\n",head);
+ }
+ if(want_one_pid){
+ if(stat2proc(want_one_pid)) print_proc();
+ else exit(1);
+ }else{
+ struct dirent *ent; /* dirent handle */
+ DIR *dir;
+ int ouruid;
+ int found_a_proc;
+ found_a_proc = 0;
+ ouruid = getuid();
+ dir = opendir("/proc");
+ while(( ent = readdir(dir) )){
+ if(*ent->d_name<'0' || *ent->d_name>'9') continue;
+ if(!stat2proc(atoi(ent->d_name))) continue;
+ if(want_one_command){
+ if(strcmp(want_one_command,P_cmd)) continue;
+ }else{
+ if(!select_notty && P_tty_num==NO_TTY_VALUE) continue;
+ if(!select_all && P_euid!=ouruid) continue;
+ }
+ found_a_proc++;
+ print_proc();
+ }
+ closedir(dir);
+ exit(!found_a_proc);
+ }
+ return 0;
+}
--- /dev/null
+Why does "ps -aux" complain about a bogus '-'?
+
+According to the POSIX and UNIX standards, the above command asks to display
+all processes with a TTY (generally the commands users are running) plus all
+processes owned by a user named "x". If that user doesn't exist, then ps will
+assume you really meant "ps aux". The warning is given to gently break you of a
+habit that will cause you trouble if a user named "x" were created.
+
+
+Why don't I see SMP (per-CPU) stats in top?
+
+You didn't enable it. Press '?' for built-in help or read the man page. Per-CPU
+stats are disabled by default because they take up too much space. Some Linux
+systems have hundreds of CPUs.
+
+
+Why do long usernames get printed as numbers?
+
+The UNIX and POSIX standards require that user names and group names be printed
+as decimal integers when there is not enough room in the column. Truncating the
+names, besides being a violation of the standard, would lead to confusion
+between names like MichelleRichards and MichelleRichardson. The UNIX and POSIX
+way to change column width is to rename the column:
+
+ ps -o pid,user=CumbersomeUserNames -o comm
+
+The easy way is to directly specify the desired width:
+
+ ps -o pid,user:19,comm
+
+
+Why is %CPU underreported for multi-threaded (Java, etc.) apps?
+
+You need to upgrade to the 2.6.10 kernel at least. Older kernels do not provide
+a reasonable way to get this information.
+
+
+Why do ps and top show threads individually?
+
+The 2.4.xx kernel does not provide proper support for grouping threads by
+process. Hacks exist to group them anyway, but such hacks will falsely group
+similar tasks and will fail to group tasks due to race conditions. The hacks
+are also slow. As none of this is acceptable in a critical system tool, task
+grouping is not currently available for the 2.4.xx kernel. The 2.6.xx kernel
+allows for proper thread grouping and reporting. To take advantage of this,
+your programs must use a threading library that features the CLONE_THREAD flag.
+The NPTL pthreads provided by recent glibc releases use CLONE_THREAD.
+
+
+What systems are supported?
+
+Linux 2.4.xx and 2.6.xx are commonly tested and expected to work well. SMP is
+well supported. Multi-node cluster views require a multi-node /proc filesystem;
+without that you will see a single-node view.
+
+
+Where to I send bug reports?
+
+You may use the Debian bug tracking system or send your report to
+procps-feedback@lists.sf.net (no subscription required) instead.
+
+
+Why are there so many procps projects?
+
+The original maintainer seems to have had little time for procps. Whatever his
+reasons, the project didn't get maintained. Starting in 1997, Albert Cahalan
+wrote a new ps program for the package. For the next few years, Albert quietly
+helped the Debian package maintainer fix bugs. In 2001, Rik van Riel decided to
+do something about what appeared to be the lack of a maintainer. He picked up
+the buggy old code in Red Hat's CVS and started adding patches. Meanwhile,
+other people have patched procps in a great many ways. In 2002, Albert moved
+procps to this site. This was done to ensure that years of testing and bug
+fixes would not be lost. The major version number was changed to 3, partly to
+avoid confusing users and partly because the top program has been redone.
+
+
+Why does ps get signal 17?
+
+No ps release has ever had this problem. Most likely your system has been
+broken into. You might want to install a more recent version of the OS. If
+you'd rather take your chances, simply upgrade procps.
+
--- /dev/null
+9532714b6846013ca9898984ba4cd7e0 procps-3.2.8.tar.gz
--- /dev/null
+diff --git a/Makefile b/Makefile
+index 3f67bbf..e4a1adc 100644
+--- a/Makefile
++++ b/Makefile
+@@ -67,7 +67,7 @@ TARFILES := AUTHORS BUGS NEWS README TODO COPYING COPYING.LIB \
+ # plus the top-level Makefile to make it work stand-alone.
+ _TARFILES := Makefile
+
+-CURSES := -lncursesw
++CURSES := -ltinfo -lncursesw
+
+ # This seems about right for the dynamic library stuff.
+ # Something like this is probably needed to make the SE Linux
--- /dev/null
+--- procps-3.2.2/watch.c.misc 2004-07-10 14:58:26.000000000 -0400
++++ procps-3.2.2/watch.c 2004-07-19 10:54:05.905274300 -0400
+@@ -24,6 +24,9 @@
+ #include <unistd.h>
+ #include <locale.h>
+ #include "proc/procps.h"
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
+
+ static struct option longopts[] = {
+ {"differences", optional_argument, 0, 'd'},
+@@ -93,6 +96,7 @@
+ main(int argc, char *argv[])
+ {
+ int optc;
++ int fd;
+ int option_differences = 0,
+ option_differences_cumulative = 0,
+ option_help = 0, option_version = 0;
+@@ -213,7 +217,13 @@
+ mvaddstr(0, width - tsl + 1, ts);
+ free(header);
+ }
+-
++ close(0);
++ fd=open("/dev/null",O_RDWR);
++ if (fd!=0) {
++ perror("open");
++ do_exit(2);
++ }
++
+ if (!(p = popen(command, "r"))) {
+ perror("popen");
+ do_exit(2);
--- /dev/null
+--- procps-3.2.1/w.c.selinux-workaround 2003-09-20 02:42:26.000000000 -0400
++++ procps-3.2.1/w.c 2004-06-15 10:45:53.000000000 -0400
+@@ -162,6 +162,10 @@
+ if(best && tmp->start_time <= best->start_time) continue;
+ best = tmp;
+ }
++ /* It is there but SELinux wouldn't allow us to know the detail. Really
++ w should just be given rights */
++ if(!kill(u->ut_pid, 0) || errno != ESRCH)
++ *found_utpid = 1;
+ return best ? best : secondbest;
+ }
+
--- /dev/null
+--- procps-3.2.6/proc/version.h.FAQ 2002-12-15 01:08:32.000000000 +0100
++++ procps-3.2.6/proc/version.h 2005-10-31 20:29:18.000000000 +0100
+@@ -16,6 +16,7 @@
+
+ extern void display_version(void); /* display suite version */
+ extern const char procps_version[]; /* global buf for suite version */
++extern const char procps_number_version[]; /* global buf for suite number version */
+
+ extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE
+ in /usr/include/linux/version.h */
+--- procps-3.2.6/proc/version.c.FAQ 2003-01-29 02:11:43.000000000 +0100
++++ procps-3.2.6/proc/version.c 2005-10-31 20:29:18.000000000 +0100
+@@ -16,8 +16,10 @@
+
+ #ifdef MINORVERSION
+ const char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION;
++const char procps_number_version[] = VERSION "." SUBVERSION "." MINORVERSION;
+ #else
+-const char procps_version[] = "procps version " VERSION "." SUBVERSION;
++const char procps_version[] = "procps version " VERSION "." SUBVERSION;
++const char procps_number_version[] = VERSION "." SUBVERSION;
+ #endif
+
+ void display_version(void) {
+--- procps-3.2.6/proc/library.map.FAQ 2005-03-14 05:32:40.000000000 +0100
++++ procps-3.2.6/proc/library.map 2005-10-31 20:32:39.000000000 +0100
+@@ -9,7 +9,7 @@
+ escape_str; escape_strlist;
+ openproc; closeproc;
+ tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
+- display_version; procps_version; linux_version_code;
++ display_version; procps_version; procps_number_version; linux_version_code;
+ Hertz; smp_num_cpus; have_privs;
+ sprint_uptime; uptime; user_from_uid; print_uptime; loadavg;
+ pretty_print_signals; print_given_signals; unix_print_signals; signal_name_to_number; signal_number_to_name;
+--- procps-3.2.6/ps/parser.c.FAQ 2005-10-31 20:29:18.000000000 +0100
++++ procps-3.2.6/ps/parser.c 2005-10-31 20:29:18.000000000 +0100
+@@ -1238,7 +1238,7 @@
+ // as SysV options... and you're screwed if you've been patching
+ // out the friendly warning. Cut-over is likely to be in 2005.
+ if(!(personality & PER_FORCE_BSD))
+- fprintf(stderr, "Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html\n");
++ fprintf(stderr, "Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-%s/FAQ\n",procps_number_version);
+ // Remember: contact albert@users.sf.net or procps-feedback@lists.sf.net
+ // if you should feel tempted. Be damn sure you understand all
+ // the issues. The same goes for other stuff too, BTW. Please ask.
--- /dev/null
+--- procps-3.2.3/top.c~ 2004-09-28 13:39:31.352488862 -0400
++++ procps-3.2.3/top.c 2004-09-28 13:42:47.659416849 -0400
+@@ -3201,12 +3201,15 @@
+
+ int main (int dont_care_argc, char *argv[])
+ {
++ struct stat isproc;
+ (void)dont_care_argc;
+ before(*argv);
+ // +-------------+
+ windows_stage1(); // top (sic) slice
+ configs_read(); // > spread etc, <
+ parse_args(&argv[1]); // > lean stuff, <
++ if (stat("/proc/self", &isproc)==-1)
++ std_err("/proc is not mounted, required for output data");
+ whack_terminal(); // > onions etc. <
+ windows_stage2(); // as bottom slice
+ // +-------------+
--- /dev/null
+--- procps-3.2.5/top.h.pseudo 2005-03-17 13:32:05.305659080 +0100
++++ procps-3.2.5/top.h 2005-03-17 13:34:04.837487480 +0100
+@@ -149,8 +149,13 @@
+ int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \
+ if (Batch) _ptr = _str; \
+ else { \
+- _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \
+- if (memcmp(_ptr, _str, _len)) { \
++ int _rest; \
++ _ptr = &Pseudo_scrn[Pseudo_row * Pseudo_cols]; \
++ _rest = Pseudo_size-(_ptr-Pseudo_scrn); \
++ Pseudo_row = (Pseudo_row+1) % Screen_rows; \
++ if (_rest < _len) \
++ _len = _rest; \
++ if (memcmp(_ptr, _str, _len)) { \
+ memcpy(_ptr, _str, _len); \
+ } else { \
+ _ptr = "\n"; \
--- /dev/null
+--- procps-3.2.4beta/proc/escape.c.0x9b 2004-10-23 19:44:28.000000000 +0200
++++ procps-3.2.4beta/proc/escape.c 2004-11-04 15:22:00.196344424 +0100
+@@ -79,6 +79,7 @@
+ // multibyte - printable
+ // Got space?
+ if (my_cells+wlen > *maxcells || my_bytes+1+len >= bufsize) break;
++#ifdef OUTPUT_PARANOID
+ // 0x9b is control byte for some terminals
+ if (memchr(src, 0x9B, len)) {
+ // unsafe multibyte
+@@ -87,13 +88,16 @@
+ my_cells++;
+ my_bytes++;
+ } else {
++#endif
+ // safe multibyte
+ memcpy(dst, src, len);
+ my_cells += wlen;
+ dst += len;
+ my_bytes += len;
+ src += len;
++#ifdef OUTPUT_PARANOID
+ }
++#endif
+ }
+ }
+ //fprintf(stdout, "cells: %d\n", my_cells);
--- /dev/null
+--- procps-3.2.6/sysctl.c.writeonly 2005-01-05 22:00:47.000000000 +0100
++++ procps-3.2.6/sysctl.c 2005-11-08 11:24:19.000000000 +0100
+@@ -127,7 +127,8 @@
+ char *restrict tmpname;
+ char *restrict outname;
+ char inbuf[1025];
+- FILE *restrict fp;
++ FILE *restrict fp = NULL;
++ struct stat st;
+
+ if (!name || !*name) {
+ fprintf(stderr, ERR_INVALID_KEY, name);
+@@ -144,7 +145,13 @@
+ outname = strdup(name);
+ slashdot(outname,'/','.'); /* change / to . */
+
+- fp = fopen(tmpname, "r");
++ if (stat(tmpname, &st)==0) {
++ if (st.st_mode & (S_IRUSR|S_IROTH|S_IRGRP))
++ fp = fopen(tmpname, "r");
++ else
++ /* ignore write-only files */
++ return -1;
++ }
+
+ if (!fp) {
+ switch(errno) {
+@@ -158,12 +165,16 @@
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+ rc = -1;
+ break;
++ case EINVAL: /* fopen()/stat() invalid argument */
++ case ENOTTY: /* inappropriate ioctl for device */
++ break;
+ default:
+ fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
+ rc = -1;
+ break;
+ }
+ } else {
++ errno=0;
+ if(fgets(inbuf, sizeof inbuf - 1, fp)) {
+ // this loop is required, see
+ // /sbin/sysctl -a | egrep -6 dev.cdrom.info
+@@ -183,7 +194,9 @@
+ }
+ }
+ } while(fgets(inbuf, sizeof inbuf - 1, fp));
+- } else {
++
++ /* fgets() returns NULL and errno without change if the file is empty */
++ } else if (errno) {
+ switch(errno) {
+ case EACCES:
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
--- /dev/null
+--- procps-3.2.5/top.c.rc 2005-06-28 22:36:24.000000000 +0200
++++ procps-3.2.5/top.c 2005-06-28 22:40:27.000000000 +0200
+@@ -2393,7 +2393,10 @@
+ int i;
+
+ for (i = 0; i < GROUPSMAX; i++) {
+- win_names(&Winstk[i], Winstk[i].rc.winname);
++ /* Please, never use something like sprintf(x, "%s", x); ... see win_names() */
++ char buf[WINNAMSIZ];
++ strncpy(buf, Winstk[i].rc.winname, WINNAMSIZ);
++ win_names(&Winstk[i], buf);
+ capsmk(&Winstk[i]);
+ }
+ // rely on this next guy to force a call (eventually) to reframewins
--- /dev/null
+--- procps-3.2.6/ps/output.c~ 2005-10-29 23:18:19.000000000 -0400
++++ procps-3.2.6/ps/output.c 2005-12-13 16:27:15.000000000 -0500
+@@ -1090,7 +1090,7 @@
+ return snprintf(outbuf, COLWID, "*");
+ }
+
+-
++#ifdef STATICLIB
+ /****************** FLASK & seLinux security stuff **********************/
+ // move the bulk of this to libproc sometime
+
+@@ -1122,7 +1122,7 @@
+ return 1;
+ }
+
+-#if 0
++#else
+ // This needs more study, considering:
+ // 1. the static linking option (maybe disable this in that case)
+ // 2. the -z and -Z option issue
+@@ -1134,7 +1134,7 @@
+ char *context;
+
+ if(!ps_getpidcon && !tried_load){
+- void *handle = dlopen("libselinux.so.1", RTLD_NOW);
++ void *handle = dlopen("libselinux.so", RTLD_NOW);
+ if(handle){
+ dlerror();
+ ps_getpidcon = dlsym(handle, "getpidcon");
+--- procps-3.2.6/Makefile~ 2005-10-30 01:27:04.000000000 -0500
++++ procps-3.2.6/Makefile 2005-12-13 16:32:09.000000000 -0500
+@@ -73,12 +73,12 @@
+ # Something like this is probably needed to make the SE Linux
+ # library loading not conflict with embedded systems stuff.
+ #
+-#ifeq ($(SHARED),1)
+-#ldl := -ldl
+-#LIBTYPE := -DSHAREDLIB
+-#else
+-#LIBTYPE := -DSTATICLIB
+-#endif
++ifeq ($(SHARED),1)
++ldl := -ldl
++LIBTYPE := -DSHAREDLIB
++else
++LIBTYPE := -DSTATICLIB
++endif
+
+ # Preprocessor flags.
+ PKG_CPPFLAGS := -D_GNU_SOURCE -I proc
+@@ -103,7 +103,7 @@
+ # Note that some stuff below is conditional on CFLAGS containing
+ # an option that starts with "-g". (-g, -g2, -g3, -ggdb, etc.)
+ CFLAGS := -O2 -s
+-ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS)
++ALL_CFLAGS := $(PKG_CFLAGS) $(CFLAGS) $(LIBTYPE)
+
+ PKG_LDFLAGS := -Wl,-warn-common
+ LDFLAGS :=
--- /dev/null
+--- procps-3.2.7/top.c.kzak 2006-08-31 10:44:42.000000000 +0200
++++ procps-3.2.7/top.c 2006-08-31 10:46:15.000000000 +0200
+@@ -121,6 +121,7 @@
+ Secure_mode = 0; // set if some functionality restricted
+
+ static int VIRT_vmsize = 0; //use vm_size for VIRT
++static int CPU_loop = 0; // wait for reliable CPU data
+
+ /* Some cap's stuff to reduce runtime calls --
+ to accomodate 'Batch' mode, they begin life as empty strings */
+@@ -1679,6 +1680,8 @@
+
+ if (( getenv("STATSIZE")) && ( atoi(getenv("STATSIZE")) == 1 ))
+ VIRT_vmsize = 1;
++ if (( getenv("CPULOOP")) && ( atoi(getenv("CPULOOP")) == 1 ))
++ CPU_loop = 1;
+ }
+
+
+@@ -2978,6 +2981,7 @@
+ {
+ static proc_t **p_table = NULL;
+ static CPU_t *smpcpu = NULL;
++ static int first = 0;
+
+ // whoa first time, gotta' prime the pump...
+ if (!p_table) {
+@@ -3025,6 +3029,28 @@
+
+ smpcpu = cpus_refresh(smpcpu);
+
++ if (first==0 && CPU_loop)
++ {
++ int i;
++ CPU_t *cpu;
++ for (i = 0; i < Cpu_tot; i++) {
++ cpu = &smpcpu[i];
++ cpu->u_sav = cpu->u;
++ cpu->s_sav = cpu->s;
++ cpu->n_sav = cpu->n;
++ cpu->i_sav = cpu->i;
++ cpu->w_sav = cpu->w;
++ cpu->x_sav = cpu->x;
++ cpu->y_sav = cpu->y;
++ cpu->z_sav = cpu->z;
++ }
++ tv.tv_sec = Rc.delay_time;
++ tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000;
++ select(0, NULL, NULL, NULL, &tv);
++
++ smpcpu = cpus_refresh(smpcpu);
++ }
++
+ if (CHKw(Curwin, View_CPUSUM)) {
+ // display just the 1st /proc/stat line
+ summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");
+@@ -3050,6 +3076,8 @@
+ }
+
+ SETw(Curwin, NEWFRAM_cwo);
++
++ first = 1;
+ return p_table;
+ }
+
+--- procps-3.2.7/top.1.kzak 2006-08-31 10:44:42.000000000 +0200
++++ procps-3.2.7/top.1 2006-08-31 10:44:42.000000000 +0200
+@@ -1183,6 +1183,13 @@
+ Send bug reports to:
+ Albert D\. Cahalan, <albert@users.sf.net>
+
++The top command calculates Cpu(s) by looking at the change in CPU time values
++between samples. When you first run it, it has no previous sample to compare
++to, so these initial values are the percentages since boot. It means you need
++at least two loops or you have to ignore summary output from the first loop.
++This is problem for example for batch mode. There is a possible workaround if
++you define the CPULOOP=1 environment variable. The top command will be run one
++extra hidden loop for CPU data before standard output.
+
+ .\" ----------------------------------------------------------------------
+ .SH 8. HISTORY Former top
--- /dev/null
+--- procps-3.2.6/top.1.env-vmsize 2006-04-19 16:50:37.000000000 +0200
++++ procps-3.2.6/top.1 2006-04-19 16:59:23.000000000 +0200
+@@ -395,7 +395,8 @@
+ o:\fB VIRT\fR \*(EM Virtual Image (kb)
+ The total amount of \*(MV used by the task.
+ It includes all code, data and shared libraries plus pages that have been
+-swapped out.
++swapped out. (Note: you can define the STATSIZE=1 environment variable and the VIRT
++will be calculated from the /proc/#/state VmSize field.)
+
+ VIRT = SWAP + RES.
+
+--- procps-3.2.6/top.c.env-vmsize 2006-04-19 16:35:20.000000000 +0200
++++ procps-3.2.6/top.c 2006-04-19 16:50:25.000000000 +0200
+@@ -120,6 +120,8 @@
+ Loops = -1, // number of iterations, -1 loops forever
+ Secure_mode = 0; // set if some functionality restricted
+
++static int VIRT_vmsize = 0; //use vm_size for VIRT
++
+ /* Some cap's stuff to reduce runtime calls --
+ to accomodate 'Batch' mode, they begin life as empty strings */
+ static char Cap_clr_eol [CAPBUFSIZ],
+@@ -1667,6 +1669,9 @@
+ Fieldstab[P_PPD].fmts = pid_fmt;
+ Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits;
+ }
++
++ if (( getenv("STATSIZE")) && ( atoi(getenv("STATSIZE")) == 1 ))
++ VIRT_vmsize = 1;
+ }
+
+
+@@ -2194,6 +2199,7 @@
+ }
+ if (!Frames_libflags) Frames_libflags = L_DEFAULT;
+ if (selection_type=='p') Frames_libflags |= PROC_PID;
++ if (VIRT_vmsize) Frames_libflags |= L_status;
+ }
+
+
+@@ -3157,7 +3163,10 @@
+ MKCOL(p->ruser);
+ break;
+ case P_VRT:
+- MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
++ if (VIRT_vmsize)
++ MKCOL(scale_num(PAGES_TO_KB(p->vm_size), w, s));
++ else
++ MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
+ break;
+ case P_WCH:
+ if (No_ksyms) {
--- /dev/null
+diff -up procps-3.2.7/free.1.hlmem procps-3.2.7/free.1
+--- procps-3.2.7/free.1.hlmem 2009-02-24 10:54:35.000000000 +0100
++++ procps-3.2.7/free.1 2009-02-24 11:00:26.000000000 +0100
+@@ -7,7 +7,7 @@ free \- Display amount of free and used
+ .SH SYNOPSIS
+ .BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s"
+ .I delay
+-.RB "] [" "\-t" "] [" "\-V" ]
++.RB "] [" "\-t" "] [" "\-l" "] [" "\-V" "]
+ .SH DESCRIPTION
+ \fBfree\fP displays the total amount of free and used physical and swap
+ memory in the system, as well as the buffers used by the kernel.
+@@ -28,7 +28,9 @@ may actually specify any floating point
+ .BR usleep (3)
+ is used for microsecond resolution delay times.
+ .PP
+-The \fB\-V\fP displays version information.
++The \fB-l\fP switch shows detailed low and high memory statistics.
++.PP
++The \fB\-V\fP switch displays version information.
+ .SH FILES
+ .ta
+ .IR /proc/meminfo "\-\- memory information"
--- /dev/null
+--- procps-3.2.7/proc/readproc.c.kzak 2006-06-16 10:18:13.000000000 +0200
++++ procps-3.2.7/proc/readproc.c 2006-09-27 11:25:13.000000000 +0200
+@@ -432,14 +432,17 @@
+ if(fd==-1) return NULL;
+
+ /* read whole file into a memory buffer, allocating as we go */
+- while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
++ while ((n = read(fd, buf, sizeof buf - 1)) >= 0) {
+ if (n < (int)(sizeof buf - 1))
+ end_of_file = 1;
+- if (n == 0 && rbuf == 0)
++ if (n == 0 && rbuf == 0) {
++ close(fd);
+ return NULL; /* process died between our open and read */
++ }
+ if (n < 0) {
+ if (rbuf)
+ free(rbuf);
++ close(fd);
+ return NULL; /* read error */
+ }
+ if (end_of_file && buf[n-1]) /* last read char not null */
--- /dev/null
+--- procps-3.2.7/ps/ps.1.eip64 2007-06-14 11:08:35.000000000 +0200
++++ procps-3.2.7/ps/ps.1 2007-06-14 11:03:28.000000000 +0200
+@@ -1156,6 +1156,14 @@
+ or\ a\ decimal representation otherwise.
+ T}
+
++rip RIP T{
++64-bit instruction pointer.
++T}
++
++rsp RSP T{
++64-bit stack pointer.
++T}
++
+ rss RSS T{
+ resident set size, the non\-swapped physical memory that
+ a task has used (in\ kiloBytes).
+--- procps-3.2.7/ps/output.c.eip64 2007-06-14 11:08:24.000000000 +0200
++++ procps-3.2.7/ps/output.c 2007-06-14 10:58:21.000000000 +0200
+@@ -764,10 +764,18 @@
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp));
+ }
+
++static int pr_rsp(char *restrict const outbuf, const proc_t *restrict const pp){
++ return snprintf(outbuf, COLWID, "%016lx", (unsigned long)(pp->kstk_esp));
++}
++
+ static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip));
+ }
+
++static int pr_rip(char *restrict const outbuf, const proc_t *restrict const pp){
++ return snprintf(outbuf, COLWID, "%016lx", (unsigned long)(pp->kstk_eip));
++}
++
+ /* This function helps print old-style time formats */
+ static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) {
+ if(!t) return snprintf(dst, COLWID, " -");
+@@ -1403,7 +1411,9 @@
+ {"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT},
+ {"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT},
+ {"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */
++{"rip", "RIP", pr_rip, sr_kstk_eip, 16, 0, LNX, TO|RIGHT},
+ {"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
++{"rsp", "RSP", pr_rsp, sr_kstk_eip, 16, 0, LNX, TO|RIGHT},
+ {"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */
+ {"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/
+ {"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/
--- /dev/null
+--- procps-3.2.7/ps/output.c.libselinux 2007-08-27 08:34:13.000000000 +0200
++++ procps-3.2.7/ps/output.c 2007-08-27 08:34:30.000000000 +0200
+@@ -1141,7 +1141,7 @@
+ char *context;
+
+ if(!ps_getpidcon && !tried_load){
+- void *handle = dlopen("libselinux.so", RTLD_NOW);
++ void *handle = dlopen("libselinux.so.1", RTLD_NOW);
+ if(handle){
+ dlerror();
+ ps_getpidcon = dlsym(handle, "getpidcon");
--- /dev/null
+diff -up procps-3.2.7/ps/ps.1.ps-man-fmt procps-3.2.7/ps/ps.1
+--- procps-3.2.7/ps/ps.1.ps-man-fmt 2008-09-01 11:43:18.000000000 +0200
++++ procps-3.2.7/ps/ps.1 2008-09-01 11:43:41.000000000 +0200
+@@ -30,7 +30,7 @@
+ .\" the space needed for the 1st two columns.
+ .\" Making it messy: inches, ens, points, scaled points...
+ .\"
+-.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-20u)n)
++.nr ColSize ((\n[.l] - \n[.i]) / 1n - 20)
+ .\"
+ .\" This is for command options
+ .nr OptSize (16u)
--- /dev/null
+--- procps-3.2.7/ps/output.c.jitter 2007-04-26 13:15:47.000000000 +0200
++++ procps-3.2.7/ps/output.c 2007-04-26 13:31:24.000000000 +0200
+@@ -77,7 +77,6 @@
+ static int wide_signals; /* true if we have room */
+
+ static unsigned long seconds_since_1970;
+-static unsigned long time_of_boot;
+ static unsigned long page_shift;
+
+
+@@ -1952,7 +1951,6 @@
+ // available space: page_size*outbuf_pages-SPACE_AMOUNT
+
+ seconds_since_1970 = time(NULL);
+- time_of_boot = seconds_since_1970 - seconds_since_boot;
+
+ meminfo();
+
+--- procps-3.2.7/ps/common.h.jitter 2005-01-27 04:43:22.000000000 +0100
++++ procps-3.2.7/ps/common.h 2007-04-26 12:44:01.000000000 +0200
+@@ -302,6 +302,7 @@
+ extern int screen_cols;
+ extern int screen_rows;
+ extern unsigned long seconds_since_boot;
++extern unsigned long time_of_boot;
+ extern selection_node *selection_list;
+ extern unsigned simple_select;
+ extern sort_node *sort_list;
+--- procps-3.2.7/ps/global.c.jitter 2005-10-30 01:43:34.000000000 +0200
++++ procps-3.2.7/ps/global.c 2007-04-26 13:26:38.000000000 +0200
+@@ -70,6 +70,7 @@
+ int screen_cols = -1;
+ int screen_rows = -1;
+ unsigned long seconds_since_boot = -1;
++unsigned long time_of_boot = -1;
+ selection_node *selection_list = (selection_node *)0xdeadbeef;
+ unsigned simple_select = 0xffffffff;
+ sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */
+@@ -361,7 +362,23 @@
+ look_up_our_self(&p);
+ set_screen_size();
+ set_personality();
+-
++ int fd;
++ char *buf[BUFFSIZE];
++ const char *b;
++
++ /* get boot time from /proc/stat */
++ fd = open("/proc/stat", O_RDONLY, 0);
++ if (fd != -1) {
++ buf[BUFFSIZE-1] = 0;
++ read(fd, buf, BUFFSIZE-1);
++ b = strstr(buf, "btime ");
++ if (b) {
++ sscanf(b, "btime %lu", &time_of_boot);
++ seconds_since_boot = time(0) - time_of_boot;
++ }
++ close(fd);
++ }
++
+ all_processes = 0;
+ bsd_c_option = 0;
+ bsd_e_option = 0;
+@@ -380,7 +397,6 @@
+ negate_selection = 0;
+ page_size = getpagesize();
+ running_only = 0;
+- seconds_since_boot = uptime(0,0);
+ selection_list = NULL;
+ simple_select = 0;
+ sort_list = NULL;
--- /dev/null
+--- procps-3.2.7/ps/ps.1.psman 2007-06-20 08:30:47.000000000 +0200
++++ procps-3.2.7/ps/ps.1 2007-06-20 08:50:10.000000000 +0200
+@@ -21,8 +21,8 @@
+ .\" See /usr/share/groff/current/tmac/an-old.tmac for what these do.
+ .\" Setting them to zero provides extra space, but only do that for
+ .\" plain text output. PostScript and such will remain indented.
+-.if n .nr IN 0n
+-.if n .nr an-prevailing-indent 0n
++.\" .if n .nr IN 0n
++.\" .if n .nr an-prevailing-indent 0n
+ .\"
+ .\"
+ .\" ColSize is used for the format spec table.
+@@ -838,6 +838,8 @@
+ .\" lB1 lB1 lBw(5.5i)
+ .\" lB1 l1 l.
+ .\"
++.RE
++
+ .TS
+ expand;
+ lB1 lB1 lBw(\n[ColSize]n)
--- /dev/null
+--- procps-3.2.7/ps/parser.c.selinux 2006-06-25 09:14:58.000000000 +0200
++++ procps-3.2.7/ps/parser.c 2006-07-10 10:14:22.000000000 +0200
+@@ -231,7 +231,7 @@
+ // In the meantime, please do not add to it. The list is
+ // intended to ONLY contain flags defined by the POSIX and UNIX
+ // standards published by The Open Group, IEEE, and ISO.
+- if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX
++ if(!strchr("aAdefgGlnoptuUZ", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX
+
+ switch(*flagptr){
+ case 'A':
+--- procps-3.2.7/ps/ps.1.selinux 2005-10-30 03:44:03.000000000 +0100
++++ procps-3.2.7/ps/ps.1 2006-07-10 10:08:12.000000000 +0200
+@@ -450,6 +450,9 @@
+ Do not show flags; show rss in place of addr.
+ This option can only be used with \fB\-l\fR.
+
++.opt \-Z
++display security context format (SELinux, etc.)
++
+ .opt \-\-format \ format
+ user\-defined format. Identical to \fB\-o\fR and \fBo\fR.
+
--- /dev/null
+diff -up procps-3.2.7/slabtop.c.once procps-3.2.7/slabtop.c
+--- procps-3.2.7/slabtop.c.once 2008-12-11 13:24:52.000000000 +0100
++++ procps-3.2.7/slabtop.c 2008-12-11 13:33:12.000000000 +0100
+@@ -268,9 +268,24 @@ static void parse_input(char c)
+ }
+ }
+
++/*printw or printf depending on the context*/
++static void printwf(int once, const char *s,...)
++{
++va_list va;
++
++va_start(va,s);
++if(once)
++ vprintf(s,va);
++else
++ vwprintw(stdscr, s, va);
++va_end(va);
++}
++
++
+ int main(int argc, char *argv[])
+ {
+ int o;
++ int once = 0;
+ unsigned short old_rows;
+ struct slab_info *slab_list = NULL;
+
+@@ -307,6 +322,7 @@ int main(int argc, char *argv[])
+ break;
+ case 'o':
+ delay = 0;
++ once = 1;
+ break;
+ case 'V':
+ display_version();
+@@ -322,12 +338,18 @@ int main(int argc, char *argv[])
+ if (tcgetattr(0, &saved_tty) == -1)
+ perror("tcgetattr");
+
+- initscr();
+- term_size(0);
+- old_rows = rows;
+- resizeterm(rows, cols);
+- signal(SIGWINCH, term_size);
+- signal(SIGINT, sigint_handler);
++ if(!once) {
++ initscr();
++ term_size(0);
++ old_rows = rows;
++ resizeterm(rows, cols);
++ signal(SIGWINCH, term_size);
++ signal(SIGINT, sigint_handler);
++ } else {
++ old_rows = rows;
++ rows = 80;
++ cols = 24;
++ }
+
+ do {
+ struct slab_info *curr;
+@@ -341,12 +363,12 @@ int main(int argc, char *argv[])
+ break;
+
+ if (old_rows != rows) {
+- resizeterm(rows, cols);
++ if(!once) resizeterm(rows, cols);
+ old_rows = rows;
+ }
+
+ move(0,0);
+- printw( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n"
++ printwf(once, " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n"
+@@ -360,15 +382,15 @@ int main(int argc, char *argv[])
+
+ slab_list = slabsort(slab_list);
+
+- attron(A_REVERSE);
+- printw( "%6s %6s %4s %8s %6s %8s %10s %-23s\n",
++ if(!once) attron(A_REVERSE);
++ printwf(once, "%6s %6s %4s %8s %6s %8s %10s %-23s\n",
+ "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS",
+ "OBJ/SLAB", "CACHE SIZE", "NAME");
+- attroff(A_REVERSE);
++ if(!once) attroff(A_REVERSE);
+
+ curr = slab_list;
+ for (i = 0; i < rows - 8 && curr->next; i++) {
+- printw("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
++ printwf(once, "%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
+ curr->nr_objs, curr->nr_active_objs, curr->use,
+ curr->obj_size / 1024.0, curr->nr_slabs,
+ curr->objs_per_slab, (unsigned)(curr->cache_size / 1024),
+@@ -376,7 +398,7 @@ int main(int argc, char *argv[])
+ curr = curr->next;
+ }
+
+- refresh();
++ if(!once) refresh();
+ put_slabinfo(slab_list);
+
+ FD_ZERO(&readfds);
+@@ -392,6 +414,6 @@ int main(int argc, char *argv[])
+
+ tcsetattr(0, TCSAFLUSH, &saved_tty);
+ free_slabinfo(slab_list);
+- endwin();
++ if(!once) endwin();
+ return 0;
+ }
--- /dev/null
+--- procps-3.2.7/sysctl.c.kzak 2007-04-03 01:26:01.000000000 +0200
++++ procps-3.2.7/sysctl.c 2007-04-03 01:26:13.000000000 +0200
+@@ -63,6 +63,44 @@
+ static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n";
+ static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n";
+
++/* Ignore deprecated sysctls
++ * -- we use this list when we scan (DisplayAll) /proc/sys only. We don't use it
++ * in case when user direcly uses deprecated key. It's better when user can read
++ * an error message from kernel.
++ */
++struct sysctl_ignore {
++ const char *prefix;
++ int prefix_len;
++ const char *option;
++ int option_len;
++};
++
++#define IGNORE_ENTRY(prefix, option) \
++ { prefix, sizeof(prefix)-1, option, sizeof(option)-1 }
++
++static struct sysctl_ignore Ignore[] =
++{
++ IGNORE_ENTRY( "net.ipv6.neigh", "base_reachable_time" ),
++ IGNORE_ENTRY( "net.ipv6.neigh", "retrans_time" )
++};
++
++static bool IsIgnored(const char *name)
++{
++ unsigned int i;
++ int sz = strlen(name);
++
++ for (i = 0; i < sizeof(Ignore)/sizeof(struct sysctl_ignore); i++) {
++ struct sysctl_ignore *p = &Ignore[i];
++
++ if (sz < (p->prefix_len + p->option_len))
++ continue;
++
++ if (strncmp(name, p->prefix, p->prefix_len) == 0 &&
++ strcmp(name + (sz - p->option_len), p->option) == 0)
++ return true;
++ }
++ return false;
++}
+
+ static void slashdot(char *restrict p, char old, char new){
+ p = strpbrk(p,"/.");
+@@ -122,7 +160,7 @@
+ * Read a sysctl setting
+ *
+ */
+-static int ReadSetting(const char *restrict const name) {
++static int ReadSetting(const char *restrict const name, bool useign) {
+ int rc = 0;
+ char *restrict tmpname;
+ char *restrict outname;
+@@ -145,6 +183,12 @@
+ outname = strdup(name);
+ slashdot(outname,'/','.'); /* change / to . */
+
++ if (useign && IsIgnored(outname)) {
++ free(outname);
++ free(tmpname);
++ return rc;
++ }
++
+ if (stat(tmpname, &st)==0) {
+ if (st.st_mode & (S_IRUSR|S_IROTH|S_IRGRP))
+ fp = fopen(tmpname, "r");
+@@ -257,7 +301,7 @@
+ strcat(tmpdir, "/");
+ DisplayAll(tmpdir);
+ } else {
+- rc |= ReadSetting(tmpdir+strlen(PROC_PATH));
++ rc |= ReadSetting(tmpdir+strlen(PROC_PATH), true);
+ }
+ }
+ free(tmpdir);
+@@ -519,7 +563,7 @@
+ if (WriteMode || index(*argv, '='))
+ ReturnCode = WriteSetting(*argv);
+ else
+- ReturnCode = ReadSetting(*argv);
++ ReturnCode = ReadSetting(*argv, false);
+ }
+ }
+
--- /dev/null
+diff -up procps-3.2.7/top.c.clrscr procps-3.2.7/top.c
+--- procps-3.2.7/top.c.clrscr 2008-04-08 08:49:48.000000000 +0200
++++ procps-3.2.7/top.c 2008-04-08 08:50:07.000000000 +0200
+@@ -2109,6 +2109,7 @@ static void fields_sort (void)
+ *p = x + 'A';
+ Curwin->rc.sortindx = x;
+ putp(Cap_curs_norm);
++ putp(Cap_clr_scr);
+ }
+
+
+@@ -2134,6 +2135,7 @@ static void fields_toggle (void)
+ *p = i + 'A';
+ }
+ putp(Cap_curs_norm);
++ putp(Cap_clr_scr);
+ }
+
+ /*###### Windows/Field Groups support #################################*/
--- /dev/null
+--- procps-3.2.7/top.c.orig 2007-09-07 21:28:41.000000000 +0100
++++ procps-3.2.7/top.c 2007-09-07 21:29:15.000000000 +0100
+@@ -959,8 +959,15 @@
+
+ // and just in case we're 2.2.xx compiled without SMP support...
+ if (Cpu_tot == 1) {
+- cpus[1].id = 0;
+- memcpy(cpus, &cpus[1], sizeof(CPU_t));
++ cpus[0].id = cpus[1].id = 0;
++ cpus[0].u = cpus[1].u;
++ cpus[0].n = cpus[1].n;
++ cpus[0].s = cpus[1].s;
++ cpus[0].i = cpus[1].i;
++ cpus[0].w = cpus[1].w;
++ cpus[0].x = cpus[1].x;
++ cpus[0].y = cpus[1].y;
++ cpus[0].z = cpus[1].z;
+ }
+
+ // now value each separate cpu's tics
--- /dev/null
+diff -up procps-3.2.7/top.c.cpuint procps-3.2.7/top.c
+--- procps-3.2.7/top.c.cpuint 2008-01-11 08:32:44.000000000 +0100
++++ procps-3.2.7/top.c 2008-01-11 08:32:52.000000000 +0100
+@@ -1824,7 +1824,7 @@ static void configs_read (void)
+ if(Rc.mode_irixps && smp_num_cpus>1){
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+- Fieldstab[P_CPU].fmts = " %4.0f";
++ Fieldstab[P_CPU].fmts = " %4.1f";
+ }
+
+ // lastly, establish the true runtime secure mode and delay time
--- /dev/null
+diff -up procps-3.2.7/top.1.cpudesc procps-3.2.7/top.1
+--- procps-3.2.7/top.1.cpudesc 2008-01-18 08:04:19.000000000 +0100
++++ procps-3.2.7/top.1 2008-01-18 08:15:49.000000000 +0100
+@@ -540,6 +540,20 @@ You\fI move\fR a field to the\fB left\fR
+ upper case\fR letter and to the\fB right\fR with the\fB lower case\fR
+ letter.
+
++.\" ......................................................................
++.SS 2c. SUMMARY Area Fields
++.\" ----------------------------------------------------------------------
++The summary area fields describing CPU statistics are abbreviated. They provide
++information about times spent in:
++ \fR us = user mode
++ \fR sy = system mode
++ \fR ni = low priority user mode (nice)
++ \fR id = idle task
++ \fR wa = I/O waiting
++ \fR hi = servicing IRQs
++ \fR si = servicing soft IRQs
++ \fR st = steal (time given to other DomU instances)
++
+
+ .\" ----------------------------------------------------------------------
+ .SH 3. INTERACTIVE Commands
--- /dev/null
+--- procps-3.2.7/top.c.remcpu 2006-07-10 10:41:11.000000000 +0200
++++ procps-3.2.7/top.c 2006-07-10 10:41:35.000000000 +0200
+@@ -912,6 +912,7 @@
+ static CPU_t *cpus_refresh (CPU_t *cpus)
+ {
+ static FILE *fp = NULL;
++ static int cpu_max;
+ int i;
+ int num;
+ // enough for a /proc/stat CPU line (not the intr line)
+@@ -926,24 +927,29 @@
+ can hold tics representing the /proc/stat cpu summary (the first
+ line read) -- that slot supports our View_CPUSUM toggle */
+ cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t));
++ cpu_max = Cpu_tot;
+ }
++ else if (cpu_max > Cpu_tot)
++ /* move saved CUPs summary to cpu_max possition */
++ memcpy(&cpus[cpu_max], &cpus[Cpu_tot], sizeof(CPU_t));
++
+ rewind(fp);
+ fflush(fp);
+
+ // first value the last slot with the cpu summary line
+ if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
+- cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number
+- cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number
+- cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number
++ cpus[cpu_max].x = 0; // FIXME: can't tell by kernel version number
++ cpus[cpu_max].y = 0; // FIXME: can't tell by kernel version number
++ cpus[cpu_max].z = 0; // FIXME: can't tell by kernel version number
+ num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
+- &cpus[Cpu_tot].u,
+- &cpus[Cpu_tot].n,
+- &cpus[Cpu_tot].s,
+- &cpus[Cpu_tot].i,
+- &cpus[Cpu_tot].w,
+- &cpus[Cpu_tot].x,
+- &cpus[Cpu_tot].y,
+- &cpus[Cpu_tot].z
++ &cpus[cpu_max].u,
++ &cpus[cpu_max].n,
++ &cpus[cpu_max].s,
++ &cpus[cpu_max].i,
++ &cpus[cpu_max].w,
++ &cpus[cpu_max].x,
++ &cpus[cpu_max].y,
++ &cpus[cpu_max].z
+ );
+ if (num < 4)
+ std_err("failed /proc/stat read");
+@@ -955,7 +961,7 @@
+ }
+
+ // now value each separate cpu's tics
+- for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) {
++ for (i = 0; ; i++) {
+ if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
+ cpus[i].x = 0; // FIXME: can't tell by kernel version number
+ cpus[i].y = 0; // FIXME: can't tell by kernel version number
+@@ -964,9 +970,35 @@
+ &cpus[i].id,
+ &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z
+ );
+- if (num < 4)
+- std_err("failed /proc/stat read");
++ if (num < 4) {
++ Cpu_tot = i;
++ break;
++ }
++ if (i == cpu_max - 1) {
++ // Bump cpu_max and extend cpus
++ cpu_max++;
++ cpus = realloc(cpus, (1 + cpu_max) * sizeof(CPU_t));
++ if (!cpus) std_err("realloc failed");
++ memcpy(&cpus[cpu_max], &cpus[cpu_max-1], sizeof(CPU_t));
++ }
++ }
++
++ if (cpu_max > Cpu_tot)
++ memcpy(&cpus[Cpu_tot], &cpus[cpu_max], sizeof(CPU_t));
++
++ // and just in case we're 2.2.xx compiled without SMP support...
++ if (Cpu_tot == 1) {
++ cpus[0].id = cpus[1].id = 0;
++ cpus[0].u = cpus[1].u;
++ cpus[0].n = cpus[1].n;
++ cpus[0].s = cpus[1].s;
++ cpus[0].i = cpus[1].i;
++ cpus[0].w = cpus[1].w;
++ cpus[0].x = cpus[1].x;
++ cpus[0].y = cpus[1].y;
++ cpus[0].z = cpus[1].z;
+ }
++
+ return cpus;
+ }
+
--- /dev/null
+--- procps-3.2.7/top.c.sorthigh 2007-02-05 09:06:34.000000000 +0100
++++ procps-3.2.7/top.c 2007-02-05 09:09:35.000000000 +0100
+@@ -3121,6 +3121,7 @@
+ unsigned w = Fieldstab[i].width;
+
+ int advance = (x==0) && !Rc.mode_altscr;
++ f += advance;
+
+ switch (i) {
+ case P_CMD:
+@@ -3179,7 +3180,7 @@
+ break;
+ case P_PRI:
+ if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) {
+- f = " RT";
++ f = advance ? " RT" : " RT";
+ MKCOL("");
+ } else
+ MKCOL((int)p->priority);
+@@ -3227,7 +3228,7 @@
+ break;
+ case P_WCH:
+ if (No_ksyms) {
+- f = " %08lx ";
++ f = advance ? "%08lx " : " %08lx ";
+ MKCOL((long)p->wchan);
+ } else {
+ MKCOL(lookup_wchan(p->wchan, p->XXXID));
+@@ -3236,7 +3237,7 @@
+
+ } /* end: switch 'procflag' */
+
+- rp = scat(rp, cbuf+advance);
++ rp = scat(rp, cbuf);
+ } /* end: for 'maxpflgs' */
+
+ PUFF(
--- /dev/null
+--- procps-3.2.7/vmstat.c.steal 2006-07-10 10:49:49.000000000 +0200
++++ procps-3.2.7/vmstat.c 2006-07-10 10:54:03.000000000 +0200
+@@ -150,15 +150,15 @@
+ ////////////////////////////////////////////////////////////////////////////
+
+ static void new_header(void){
+- printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n");
++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------\n");
+ printf(
+- "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s\n",
++ "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n",
+ "r","b",
+ "swpd", "free", a_option?"inact":"buff", a_option?"active":"cache",
+ "si","so",
+ "bi","bo",
+ "in","cs",
+- "us","sy","id","wa"
++ "us","sy","id","wa", "st"
+ );
+ }
+
+@@ -173,7 +173,7 @@
+ ////////////////////////////////////////////////////////////////////////////
+
+ static void new_format(void) {
+- const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n";
++ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\n";
+ unsigned int tog=0; /* toggle switch for cleaner code */
+ unsigned int i;
+ unsigned int hz = Hertz;
+@@ -217,8 +217,8 @@
+ (unsigned)( (100*duse + divo2) / Div ),
+ (unsigned)( (100*dsys + divo2) / Div ),
+ (unsigned)( (100*didl + divo2) / Div ),
+- (unsigned)( (100*diow + divo2) / Div ) /* ,
+- (unsigned)( (100*dstl + divo2) / Div ) */
++ (unsigned)( (100*diow + divo2) / Div ),
++ (unsigned)( (100*dstl + divo2) / Div )
+ );
+
+ for(i=1;i<num_updates;i++) { /* \\\\\\\\\\\\\\\\\\\\ main loop ////////////////// */
+@@ -266,8 +266,8 @@
+ (unsigned)( (100*duse+divo2)/Div ), /*us*/
+ (unsigned)( (100*dsys+divo2)/Div ), /*sy*/
+ (unsigned)( (100*didl+divo2)/Div ), /*id*/
+- (unsigned)( (100*diow+divo2)/Div )/*, //wa
+- (unsigned)( (100*dstl+divo2)/Div ) //st */
++ (unsigned)( (100*diow+divo2)/Div ), //wa
++ (unsigned)( (100*dstl+divo2)/Div ) //st
+ );
+ }
+ }
--- /dev/null
+diff -up procps-3.2.7/vmstat.c.jx procps-3.2.7/vmstat.c
+--- procps-3.2.7/vmstat.c.jx 2009-01-07 14:13:52.000000000 -0500
++++ procps-3.2.7/vmstat.c 2009-01-07 14:17:07.000000000 -0500
+@@ -150,7 +150,7 @@ static int format_1000(unsigned long lon
+ ////////////////////////////////////////////////////////////////////////////
+
+ static void new_header(void){
+- printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------\n");
++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n");
+ printf(
+ "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n",
+ "r","b",
--- /dev/null
+diff -up procps-3.2.7/proc/sysinfo.c.vmstat procps-3.2.7/proc/sysinfo.c
+--- procps-3.2.7/proc/sysinfo.c.vmstat 2008-12-04 15:25:50.000000000 +0100
++++ procps-3.2.7/proc/sysinfo.c 2008-12-04 16:54:29.000000000 +0100
+@@ -784,6 +784,18 @@ unsigned int getpartitions_num(struct di
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
++static int is_disk(char *dev)
++{
++ char syspath[PATH_MAX];
++ char *slash;
++
++ while ((slash = strchr(dev, '/')))
++ *slash = '!';
++ snprintf(syspath, sizeof(syspath), "/sys/block/%s", dev);
++ return !(access(syspath, F_OK));
++}
++
++/////////////////////////////////////////////////////////////////////////////
+
+ unsigned int getdiskstat(struct disk_stat **disks, struct partition_stat **partitions){
+ FILE* fd;
+@@ -791,6 +803,7 @@ unsigned int getdiskstat(struct disk_sta
+ int cPartition = 0;
+ int fields;
+ unsigned dummy;
++ char devname[PATH_MAX];
+
+ *disks = NULL;
+ *partitions = NULL;
+@@ -803,10 +816,11 @@ unsigned int getdiskstat(struct disk_sta
+ fclose(fd);
+ break;
+ }
+- fields = sscanf(buff, " %*d %*d %*s %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u", &dummy);
+- if (fields == 1){
++ fields = sscanf(buff, " %*d %*d %15s %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u",
++ &devname, &dummy);
++ if (fields == 2 && is_disk(devname)){
+ (*disks) = realloc(*disks, (cDisk+1)*sizeof(struct disk_stat));
+- sscanf(buff, " %*d %*d %15s %u %u %llu %u %u %u %llu %u %u %u %u",
++ sscanf(buff, " %*d %*d %15s %lu %lu %llu %lu %lu %lu %llu %lu %lu %lu %lu",
+ //&disk_major,
+ //&disk_minor,
+ (*disks)[cDisk].disk_name,
+@@ -827,7 +841,9 @@ unsigned int getdiskstat(struct disk_sta
+ }else{
+ (*partitions) = realloc(*partitions, (cPartition+1)*sizeof(struct partition_stat));
+ fflush(stdout);
+- sscanf(buff, " %*d %*d %15s %u %llu %u %u",
++ sscanf(buff, (fields == 2)
++ ? " %*d %*d %15s %u %*u %llu %*u %u %*u %llu %*u %*u %*u %*u"
++ : " %*d %*d %15s %u %llu %u %llu",
+ //&part_major,
+ //&part_minor,
+ (*partitions)[cPartition].partition_name,
+diff -up procps-3.2.7/proc/sysinfo.h.vmstat procps-3.2.7/proc/sysinfo.h
+--- procps-3.2.7/proc/sysinfo.h.vmstat 2008-12-04 15:25:41.000000000 +0100
++++ procps-3.2.7/proc/sysinfo.h 2008-12-04 16:54:32.000000000 +0100
+@@ -95,16 +95,16 @@ typedef struct disk_stat{
+ unsigned long long reads_sectors;
+ unsigned long long written_sectors;
+ char disk_name [16];
+- unsigned inprogress_IO;
+- unsigned merged_reads;
+- unsigned merged_writes;
+- unsigned milli_reading;
+- unsigned milli_spent_IO;
+- unsigned milli_writing;
+- unsigned partitions;
+- unsigned reads;
+- unsigned weighted_milli_spent_IO;
+- unsigned writes;
++ unsigned long inprogress_IO;
++ unsigned long merged_reads;
++ unsigned long merged_writes;
++ unsigned long milli_reading;
++ unsigned long milli_spent_IO;
++ unsigned long milli_writing;
++ unsigned long partitions;
++ unsigned long reads;
++ unsigned long weighted_milli_spent_IO;
++ unsigned long writes;
+ }disk_stat;
+
+ typedef struct partition_stat{
+@@ -113,7 +113,7 @@ typedef struct partition_stat{
+ unsigned parent_disk; // index into a struct disk_stat array
+ unsigned reads;
+ unsigned writes;
+- unsigned requested_writes;
++ unsigned long long requested_writes;
+ }partition_stat;
+
+ extern unsigned int getpartitions_num(struct disk_stat *disks, int ndisks);
--- /dev/null
+diff -up procps-3.2.7/vmstat.c.vmstat procps-3.2.7/vmstat.c
+--- procps-3.2.7/vmstat.c.vmstat 2009-02-23 13:44:27.000000000 +0100
++++ procps-3.2.7/vmstat.c 2009-02-23 13:45:03.000000000 +0100
+@@ -312,7 +312,7 @@ static int diskpartition_format(const ch
+ struct disk_stat *disks;
+ struct partition_stat *partitions, *current_partition=NULL;
+ unsigned long ndisks, j, k, npartitions;
+- const char format[] = "%20u %10llu %10u %10u\n";
++ const char format[] = "%20u %10llu %10u %10llu\n";
+
+ fDiskstat=fopen("/proc/diskstats","rb");
+ if(!fDiskstat){
--- /dev/null
+--- procps-3.2.7/proc/sysinfo.h.kzak 2006-06-25 08:41:48.000000000 +0200
++++ procps-3.2.7/proc/sysinfo.h 2006-08-04 11:19:09.000000000 +0200
+@@ -57,7 +57,7 @@
+ #define BUFFSIZE (64*1024)
+ typedef unsigned long long jiff;
+ extern void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz,
+- unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout,
++ unsigned long long *restrict pin, unsigned long long *restrict pout, unsigned long long *restrict s_in, unsigned long long *restrict sout,
+ unsigned *restrict intr, unsigned *restrict ctxt,
+ unsigned int *restrict running, unsigned int *restrict blocked,
+ unsigned int *restrict btime, unsigned int *restrict processes);
+@@ -72,10 +72,10 @@
+ extern unsigned long vm_nr_reverse_maps;
+ extern unsigned long vm_nr_mapped;
+ extern unsigned long vm_nr_slab;
+-extern unsigned long vm_pgpgin;
+-extern unsigned long vm_pgpgout;
+-extern unsigned long vm_pswpin;
+-extern unsigned long vm_pswpout;
++extern unsigned long long vm_pgpgin;
++extern unsigned long long vm_pgpgout;
++extern unsigned long long vm_pswpin;
++extern unsigned long long vm_pswpout;
+ extern unsigned long vm_pgalloc;
+ extern unsigned long vm_pgfree;
+ extern unsigned long vm_pgactivate;
+--- procps-3.2.7/proc/sysinfo.c.kzak 2006-06-25 08:41:48.000000000 +0200
++++ procps-3.2.7/proc/sysinfo.c 2006-08-04 12:04:06.000000000 +0200
+@@ -368,7 +368,7 @@
+ /***********************************************************************/
+
+ void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz,
+- unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout,
++ unsigned long long *restrict pin, unsigned long long *restrict pout, unsigned long long *restrict s_in, unsigned long long *restrict sout,
+ unsigned *restrict intr, unsigned *restrict ctxt,
+ unsigned int *restrict running, unsigned int *restrict blocked,
+ unsigned int *restrict btime, unsigned int *restrict processes) {
+@@ -396,11 +396,11 @@
+ if(b) sscanf(b, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", cuse, cice, csys, cide, ciow, cxxx, cyyy, czzz);
+
+ b = strstr(buff, "page ");
+- if(b) sscanf(b, "page %lu %lu", pin, pout);
++ if(b) sscanf(b, "page %llu %llu", pin, pout);
+ else need_vmstat_file = 1;
+
+ b = strstr(buff, "swap ");
+- if(b) sscanf(b, "swap %lu %lu", s_in, sout);
++ if(b) sscanf(b, "swap %llu %llu", s_in, sout);
+ else need_vmstat_file = 1;
+
+ b = strstr(buff, "intr ");
+@@ -611,9 +611,12 @@
+
+ typedef struct vm_table_struct {
+ const char *name; /* VM statistic name */
+- unsigned long *slot; /* slot in return struct */
++ void *slot; /* slot in return struct */
++ int sz; /* size of slot value */
+ } vm_table_struct;
+
++#define VM_TABENTRY(_name_) { # _name_, &vm_ ## _name_, sizeof(vm_ ## _name_) }
++
+ static int compare_vm_table_structs(const void *a, const void *b){
+ return strcmp(((const vm_table_struct*)a)->name,((const vm_table_struct*)b)->name);
+ }
+@@ -626,10 +629,10 @@
+ unsigned long vm_nr_reverse_maps; // includes PageDirect
+ unsigned long vm_nr_mapped; // mapped into pagetables
+ unsigned long vm_nr_slab; // in slab
+-unsigned long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line)
+-unsigned long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line)
+-unsigned long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line)
+-unsigned long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line)
++unsigned long long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line)
++unsigned long long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line)
++unsigned long long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line)
++unsigned long long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line)
+ unsigned long vm_pgalloc; // page allocations
+ unsigned long vm_pgfree; // page freeings
+ unsigned long vm_pgactivate; // pages moved inactive -> active
+@@ -668,54 +671,54 @@
+
+ void vminfo(void){
+ char namebuf[16]; /* big enough to hold any row name */
+- vm_table_struct findme = { namebuf, NULL};
++ vm_table_struct findme = { namebuf, NULL, 0};
+ vm_table_struct *found;
+ char *head;
+ char *tail;
+ static const vm_table_struct vm_table[] = {
+- {"allocstall", &vm_allocstall},
+- {"kswapd_inodesteal", &vm_kswapd_inodesteal},
+- {"kswapd_steal", &vm_kswapd_steal},
+- {"nr_dirty", &vm_nr_dirty}, // page version of meminfo Dirty
+- {"nr_mapped", &vm_nr_mapped}, // page version of meminfo Mapped
+- {"nr_page_table_pages", &vm_nr_page_table_pages},// same as meminfo PageTables
+- {"nr_pagecache", &vm_nr_pagecache}, // gone in 2.5.66+ kernels
+- {"nr_reverse_maps", &vm_nr_reverse_maps}, // page version of meminfo ReverseMaps GONE
+- {"nr_slab", &vm_nr_slab}, // page version of meminfo Slab
+- {"nr_unstable", &vm_nr_unstable},
+- {"nr_writeback", &vm_nr_writeback}, // page version of meminfo Writeback
+- {"pageoutrun", &vm_pageoutrun},
+- {"pgactivate", &vm_pgactivate},
+- {"pgalloc", &vm_pgalloc}, // GONE (now separate dma,high,normal)
+- {"pgalloc_dma", &vm_pgalloc_dma},
+- {"pgalloc_high", &vm_pgalloc_high},
+- {"pgalloc_normal", &vm_pgalloc_normal},
+- {"pgdeactivate", &vm_pgdeactivate},
+- {"pgfault", &vm_pgfault},
+- {"pgfree", &vm_pgfree},
+- {"pginodesteal", &vm_pginodesteal},
+- {"pgmajfault", &vm_pgmajfault},
+- {"pgpgin", &vm_pgpgin}, // important
+- {"pgpgout", &vm_pgpgout}, // important
+- {"pgrefill", &vm_pgrefill}, // GONE (now separate dma,high,normal)
+- {"pgrefill_dma", &vm_pgrefill_dma},
+- {"pgrefill_high", &vm_pgrefill_high},
+- {"pgrefill_normal", &vm_pgrefill_normal},
+- {"pgrotated", &vm_pgrotated},
+- {"pgscan", &vm_pgscan}, // GONE (now separate direct,kswapd and dma,high,normal)
+- {"pgscan_direct_dma", &vm_pgscan_direct_dma},
+- {"pgscan_direct_high", &vm_pgscan_direct_high},
+- {"pgscan_direct_normal",&vm_pgscan_direct_normal},
+- {"pgscan_kswapd_dma", &vm_pgscan_kswapd_dma},
+- {"pgscan_kswapd_high", &vm_pgscan_kswapd_high},
+- {"pgscan_kswapd_normal",&vm_pgscan_kswapd_normal},
+- {"pgsteal", &vm_pgsteal}, // GONE (now separate dma,high,normal)
+- {"pgsteal_dma", &vm_pgsteal_dma},
+- {"pgsteal_high", &vm_pgsteal_high},
+- {"pgsteal_normal", &vm_pgsteal_normal},
+- {"pswpin", &vm_pswpin}, // important
+- {"pswpout", &vm_pswpout}, // important
+- {"slabs_scanned", &vm_slabs_scanned},
++ VM_TABENTRY(allocstall),
++ VM_TABENTRY(kswapd_inodesteal),
++ VM_TABENTRY(kswapd_steal),
++ VM_TABENTRY(nr_dirty),
++ VM_TABENTRY(nr_mapped),
++ VM_TABENTRY(nr_page_table_pages),
++ VM_TABENTRY(nr_pagecache),
++ VM_TABENTRY(nr_reverse_maps),
++ VM_TABENTRY(nr_slab),
++ VM_TABENTRY(nr_unstable),
++ VM_TABENTRY(nr_writeback),
++ VM_TABENTRY(pageoutrun),
++ VM_TABENTRY(pgactivate),
++ VM_TABENTRY(pgalloc),
++ VM_TABENTRY(pgalloc_dma),
++ VM_TABENTRY(pgalloc_high),
++ VM_TABENTRY(pgalloc_normal),
++ VM_TABENTRY(pgdeactivate),
++ VM_TABENTRY(pgfault),
++ VM_TABENTRY(pgfree),
++ VM_TABENTRY(pginodesteal),
++ VM_TABENTRY(pgmajfault),
++ VM_TABENTRY(pgpgin),
++ VM_TABENTRY(pgpgout),
++ VM_TABENTRY(pgrefill),
++ VM_TABENTRY(pgrefill_dma),
++ VM_TABENTRY(pgrefill_high),
++ VM_TABENTRY(pgrefill_normal),
++ VM_TABENTRY(pgrotated),
++ VM_TABENTRY(pgscan),
++ VM_TABENTRY(pgscan_direct_dma),
++ VM_TABENTRY(pgscan_direct_high),
++ VM_TABENTRY(pgscan_direct_normal),
++ VM_TABENTRY(pgscan_kswapd_dma),
++ VM_TABENTRY(pgscan_kswapd_high),
++ VM_TABENTRY(pgscan_kswapd_normal),
++ VM_TABENTRY(pgsteal),
++ VM_TABENTRY(pgsteal_dma),
++ VM_TABENTRY(pgsteal_high),
++ VM_TABENTRY(pgsteal_normal),
++ VM_TABENTRY(pswpin),
++ VM_TABENTRY(pswpout),
++ VM_TABENTRY(slabs_scanned)
+ };
+ const int vm_table_count = sizeof(vm_table)/sizeof(vm_table_struct);
+
+@@ -741,7 +744,10 @@
+ );
+ head = tail+1;
+ if(!found) goto nextline;
+- *(found->slot) = strtoul(head,&tail,10);
++ if (found->sz == sizeof(unsigned long long))
++ *((unsigned long long *) found->slot) = strtoull(head,&tail,10);
++ else
++ *((unsigned long *) found->slot) = strtoul(head,&tail,10);
+ nextline:
+
+ //if(found) fprintf(stderr,"%s=%d\n",found->name,*(found->slot));
+--- procps-3.2.7/vmstat.c.kzak 2006-08-04 10:52:38.000000000 +0200
++++ procps-3.2.7/vmstat.c 2006-08-04 12:05:49.000000000 +0200
+@@ -180,7 +180,7 @@
+ unsigned int running,blocked,dummy_1,dummy_2;
+ jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2], cpu_xxx[2], cpu_yyy[2], cpu_zzz[2];
+ jiff duse, dsys, didl, diow, dstl, Div, divo2;
+- unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
++ unsigned long long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
+ unsigned int intr[2], ctxt[2];
+ unsigned int sleep_half;
+ unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul;
+@@ -509,7 +509,7 @@
+ static void sum_format(void) {
+ unsigned int running, blocked, btime, processes;
+ jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
+- unsigned long pgpgin, pgpgout, pswpin, pswpout;
++ unsigned long long pgpgin, pgpgout, pswpin, pswpout;
+ unsigned int intr, ctxt;
+
+ meminfo();
+@@ -539,10 +539,10 @@
+ printf("%13Lu IRQ cpu ticks\n", cpu_xxx);
+ printf("%13Lu softirq cpu ticks\n", cpu_yyy);
+ printf("%13Lu stolen cpu ticks\n", cpu_zzz);
+- printf("%13lu pages paged in\n", pgpgin);
+- printf("%13lu pages paged out\n", pgpgout);
+- printf("%13lu pages swapped in\n", pswpin);
+- printf("%13lu pages swapped out\n", pswpout);
++ printf("%13llu pages paged in\n", pgpgin);
++ printf("%13llu pages paged out\n", pgpgout);
++ printf("%13llu pages swapped in\n", pswpin);
++ printf("%13llu pages swapped out\n", pswpout);
+ printf("%13u interrupts\n", intr);
+ printf("%13u CPU context switches\n", ctxt);
+ printf("%13u boot time\n", btime);
+@@ -554,7 +554,7 @@
+ static void fork_format(void) {
+ unsigned int running, blocked, btime, processes;
+ jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
+- unsigned long pgpgin, pgpgout, pswpin, pswpout;
++ unsigned long long pgpgin, pgpgout, pswpin, pswpout;
+ unsigned int intr, ctxt;
+
+ getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
--- /dev/null
+diff -up procps-3.2.7/vmstat.8.timestamp procps-3.2.7/vmstat.8
+--- procps-3.2.7/vmstat.8.timestamp 2009-02-03 12:32:27.000000000 +0100
++++ procps-3.2.7/vmstat.8 2009-02-03 12:34:37.000000000 +0100
+@@ -8,6 +8,7 @@ vmstat \- Report virtual memory statisti
+ .B vmstat
+ .RB [ "\-a" ]
+ .RB [ "\-n" ]
++.RB [ "\-t" ]
+ .RI [ delay " [ " count ]]
+ .br
+ .B vmstat
+@@ -43,7 +44,9 @@ equivalent to the total number of tasks
+ is represented by one or more tasks, depending on thread usage.
+ This display does not repeat.
+ .PP
+-The \fB-m\fP displays slabinfo.
++The \fB-t\fP switch adds timestamp to the output.
++.PP
++The \fB-m\fP switch displays slabinfo.
+ .PP
+ The \fB-n\fP switch causes the header to be displayed only once rather than periodically.
+ .PP
--- /dev/null
+--- procps-3.2.7/vmstat.c 2008-12-11 22:11:30.042532106 -0500
++++ procps-3.2.7/vmstat.c.timestamp 2008-12-11 22:08:11.553529883 -0500
+@@ -25,6 +25,7 @@
+ #include <sys/ioctl.h>
+ #include <sys/dir.h>
+ #include <dirent.h>
++#include <time.h>
+
+ #include "proc/sysinfo.h"
+ #include "proc/version.h"
+@@ -56,6 +57,7 @@
+
+ static unsigned int height; // window height
+ static unsigned int moreheaders=TRUE;
++static unsigned int showtimestamp=FALSE;
+
+
+ /////////////////////////////////////////////////////////////////////////
+@@ -71,6 +73,7 @@
+ fprintf(stderr," -p prints disk partition statistics\n");
+ fprintf(stderr," -s prints vm table\n");
+ fprintf(stderr," -m prints slabinfo\n");
++ fprintf(stderr," -t add timestamp to output\n");
+ fprintf(stderr," -S unit size\n");
+ fprintf(stderr," delay is the delay between updates in seconds. \n");
+ fprintf(stderr," unit size k:1000 K:1024 m:1000000 M:1048576 (default is K)\n");
+@@ -150,7 +153,11 @@
+ ////////////////////////////////////////////////////////////////////////////
+
+ static void new_header(void){
+- printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n");
++ if(!showtimestamp){
++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----\n");
++ } else {
++ printf("procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------ ---timestamp---\n");
++ }
+ printf(
+ "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n",
+ "r","b",
+@@ -173,7 +180,7 @@
+ ////////////////////////////////////////////////////////////////////////////
+
+ static void new_format(void) {
+- const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\n";
++ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\t%s\n";
+ unsigned int tog=0; /* toggle switch for cleaner code */
+ unsigned int i;
+ unsigned int hz = Hertz;
+@@ -185,6 +192,10 @@
+ unsigned int sleep_half;
+ unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul;
+ int debt = 0; // handle idle ticks running backwards
++ struct tm *tm_ptr;
++ time_t the_time;
++ char timebuf[32];
++ timebuf[0] = '\0';
+
+ sleep_half=(sleep_time/2);
+ new_header();
+@@ -196,6 +207,13 @@
+ &running,&blocked,
+ &dummy_1, &dummy_2);
+
++ if (showtimestamp)
++ {
++ (void) time( &the_time );
++ tm_ptr = localtime( &the_time );
++ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S %Z", tm_ptr);
++ }
++
+ duse= *cpu_use + *cpu_nic;
+ dsys= *cpu_sys + *cpu_xxx + *cpu_yyy;
+ didl= *cpu_idl;
+@@ -218,7 +236,8 @@
+ (unsigned)( (100*dsys + divo2) / Div ),
+ (unsigned)( (100*didl + divo2) / Div ),
+ (unsigned)( (100*diow + divo2) / Div ),
+- (unsigned)( (100*dstl + divo2) / Div )
++ (unsigned)( (100*dstl + divo2) / Div ),
++ timebuf
+ );
+
+ for(i=1;i<num_updates;i++) { /* \\\\\\\\\\\\\\\\\\\\ main loop ////////////////// */
+@@ -234,6 +253,13 @@
+ &running,&blocked,
+ &dummy_1,&dummy_2);
+
++ if (showtimestamp)
++ {
++ (void) time( &the_time );
++ tm_ptr = localtime( &the_time );
++ strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S %Z", tm_ptr);
++ }
++
+ duse= cpu_use[tog]-cpu_use[!tog] + cpu_nic[tog]-cpu_nic[!tog];
+ dsys= cpu_sys[tog]-cpu_sys[!tog] + cpu_xxx[tog]-cpu_xxx[!tog] + cpu_yyy[tog]-cpu_yyy[!tog];
+ didl= cpu_idl[tog]-cpu_idl[!tog];
+@@ -267,7 +293,8 @@
+ (unsigned)( (100*dsys+divo2)/Div ), /*sy*/
+ (unsigned)( (100*didl+divo2)/Div ), /*id*/
+ (unsigned)( (100*diow+divo2)/Div ), //wa
+- (unsigned)( (100*dstl+divo2)/Div ) //st
++ (unsigned)( (100*dstl+divo2)/Div ), //st
++ timebuf /* timestamp */
+ );
+ }
+ }
+@@ -641,6 +668,9 @@
+ case 's':
+ statMode |= VMSUMSTAT;
+ break;
++ case 't':
++ showtimestamp=TRUE;
++ break;
+ default:
+ /* no other aguments defined yet. */
+ usage();
--- /dev/null
+--- procps-3.2.7/w.c.kzak 2007-04-02 23:58:30.000000000 +0200
++++ procps-3.2.7/w.c 2007-04-03 00:09:15.000000000 +0200
+@@ -149,11 +149,11 @@
+ const proc_t *restrict const tmp = *pptr;
+ if(unlikely(tmp->tgid == u->ut_pid)) {
+ *found_utpid = 1;
+- best = tmp;
++ if (!best)
++ best = tmp;
+ }
+ if(tmp->tty != line) continue;
+ (*jcpu) += tmp->utime + tmp->stime;
+- secondbest = tmp;
+ /* same time-logic here as for "best" below */
+ if(! (secondbest && tmp->start_time <= secondbest->start_time) ){
+ secondbest = tmp;
--- /dev/null
+diff -u procps-3.2.7/Makefile procps/Makefile
+--- procps-3.2.7/Makefile 2007-01-16 17:24:49.000000000 +0100
++++ procps/Makefile 2007-01-16 17:29:27.000000000 +0100
+@@ -67,7 +67,7 @@
+ # plus the top-level Makefile to make it work stand-alone.
+ _TARFILES := Makefile
+
+-CURSES := -lncurses
++CURSES := -lncursesw
+
+ # This seems about right for the dynamic library stuff.
+ # Something like this is probably needed to make the SE Linux
+diff -u procps-3.2.7/watch.c procps/watch.c
+--- procps-3.2.7/watch.c 2007-01-16 17:24:49.000000000 +0100
++++ procps/watch.c 2007-01-16 18:06:57.000000000 +0100
+@@ -15,7 +15,7 @@
+ #include <ctype.h>
+ #include <getopt.h>
+ #include <signal.h>
+-#include <ncurses.h>
++#include <ncursesw/ncurses.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -28,6 +28,8 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
++#include <wchar.h>
++#include <wctype.h>
+
+ #ifdef FORCE_8BIT
+ #undef isprint
+@@ -137,6 +139,27 @@
+ }
+ }
+
++static wint_t
++readwc(FILE *stream, mbstate_t *mbs)
++{
++ for (;;) {
++ int chr;
++ char c;
++ wchar_t wc;
++ size_t len;
++
++ chr = getc(stream);
++ if (chr == EOF)
++ return WEOF;
++ c = chr;
++ len = mbrtowc(&wc, &c, 1, mbs);
++ if (len == (size_t)-1)
++ memset(mbs, 0, sizeof(*mbs));
++ else if (len != (size_t)-2)
++ return wc;
++ }
++}
++
+ int
+ main(int argc, char *argv[])
+ {
+@@ -243,6 +266,7 @@
+ FILE *p;
+ int x, y;
+ int oldeolseen = 1;
++ mbstate_t mbs;
+
+ if (screen_size_changed) {
+ get_terminal_size();
+@@ -276,49 +300,63 @@
+ do_exit(2);
+ }
+
++ memset(&mbs, 0, sizeof(mbs));
+ for (y = show_title; y < height; y++) {
+ int eolseen = 0, tabpending = 0;
+ for (x = 0; x < width; x++) {
+- int c = ' ';
+- int attr = 0;
++ wint_t c = L' ';
++ int attr = 0, c_width;
++ cchar_t cc;
++ wchar_t wstr[2];
+
+ if (!eolseen) {
+ /* if there is a tab pending, just spit spaces until the
+ next stop instead of reading characters */
+ if (!tabpending)
+ do
+- c = getc(p);
+- while (c != EOF && !isprint(c)
+- && c != '\n'
+- && c != '\t');
+- if (c == '\n')
++ c = readwc(p, &mbs);
++ while (c != WEOF && !iswprint(c)
++ && c != L'\n'
++ && c != L'\t');
++ if (c == L'\n')
+ if (!oldeolseen && x == 0) {
+ x = -1;
+ continue;
+ } else
+ eolseen = 1;
+- else if (c == '\t')
++ else if (c == L'\t')
+ tabpending = 1;
+- if (c == EOF || c == '\n' || c == '\t')
+- c = ' ';
++ if (c == WEOF || c == L'\n' || c == L'\t')
++ c = L' ';
+ if (tabpending && (((x + 1) % 8) == 0))
+ tabpending = 0;
+ }
++ wstr[0] = c;
++ wstr[1] = 0;
++ setcchar (&cc, wstr, 0, 0, NULL);
+ move(y, x);
+ if (option_differences) {
+- chtype oldch = inch();
+- char oldc = oldch & A_CHARTEXT;
++ cchar_t oldc;
++ wchar_t oldwstr[2];
++ attr_t attrs;
++ short colors;
++
++ in_wch(&oldc);
++ getcchar(&oldc, oldwstr, &attrs, &colors, NULL);
+ attr = !first_screen
+- && ((char)c != oldc
++ && (wstr[0] != oldwstr[0]
+ ||
+ (option_differences_cumulative
+- && (oldch & A_ATTRIBUTES)));
++ && attrs));
+ }
+ if (attr)
+ standout();
+- addch(c);
++ add_wch(&cc);
+ if (attr)
+ standend();
++ c_width = wcwidth(c);
++ if (c_width > 1)
++ x += c_width - 1;
+ }
+ oldeolseen = eolseen;
+ }
--- /dev/null
+--- procps-3.2.7/ps/display.c.enable-core 2009-02-27 11:57:21.000000000 +0000
++++ procps-3.2.7/ps/display.c 2009-02-27 11:57:48.000000000 +0000
+@@ -562,6 +562,8 @@
+ default:
+ sigaction(i,&sa,NULL);
+ case 0:
++ case SIGABRT:
++ case SIGSEGV:
+ case SIGINT: /* ^C */
+ case SIGTSTP: /* ^Z */
+ case SIGTTOU: /* see stty(1) man page */
--- /dev/null
+* Wed Jul 28 2010 Ameya Palande <ameya.palande@nokia.com> 3.2.8
+- Fix for "Unknown HZ value! (??) Assume 100." problem
+
+* Mon Dec 14 2009 Yi Yang <yi.y.yang@intel.com> 3.2.7
+- Fix Makefile error when LD_AS_NEEDED is set
+
+* Mon Dec 14 2009 Yi Yang <yi.y.yang@intel.com> 3.2.7
+- Remove "unset LD_AS_NEEDED"
+
+* Thu May 7 2009 Arjan van de Ven <arjan@linux.intel.com> 3.2.7
+- not ready for LD_AS_NEEDED yet
+
+* Wed Jul 23 2008 Zhang Xin <xing.z.zhang@intel.com>
+- Mark man file as %doc
+
+* Tue Feb 12 2008 Tomas Smetana <tsmetana@redhat.com> 3.2.7-20
+- rebuild (gcc-4.3)
+
+* Thu Jan 24 2008 Tomas Smetana <tsmetana@redhat.com> 3.2.7-19.2
+- install slabtop again: kernel was fixed
+
+* Fri Jan 18 2008 Tomas Smetana <tsmetana@redhat.com> 3.2.7-19.1
+- rebuild because of errors on x86_64
+
+* Fri Jan 18 2008 Tomas Smetana <tsmetana@redhat.com> 3.2.7-19
+- fix #296471 - update top man page
+- fix #226319 - merge review
+
+* Fri Jan 11 2008 Tomas Smetana <tsmetana@redhat.com> 3.2.7-18
+- fix displaying the CPU column as integer (related #354001)
+- don't install slabtop -- there's no /proc/slabinfo
+
+* Wed Sep 12 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-17
+- fix #185994 - top "Cpu0" line never updates when using
+ "Single Cpu = Off" option on single processor machine
+
+* Mon Aug 27 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-16.1
+- bump release
+
+* Mon Aug 27 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-16
+- fix #255441 - ps requires libselinux-devel to display security contexts
+
+* Thu Aug 23 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-15.1
+- rebuild
+
+* Mon Aug 20 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-15
+- fix #244960 - ps manpage formatted incorrectly
+- update license tag
+
+* Mon Jun 18 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-14
+- fix #244152 ps truncates eip and esp to 32-bit values on 64-bit systems
+
+* Tue May 22 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-13
+- fix #208217 - ps does not accept '+' in sort specifier
+
+* Wed Apr 25 2007 Tomas Smetana <tsmetana@redhat.com> 3.2.7-12
+- fix #183029 - watch ignores multibyte characters
+- fix #222251 - STIME column can jitter
+- fix array overflow in sysctl
+
+* Tue Apr 3 2007 Karel Zak <kzak@redhat.com> 3.2.7-11
+- fix #234546 - 'w' doesn't give correct information about what's being run.
+- fix #228870 - process `sysctl' is using deprecated sysctl
+- cleanup spec file
+
+* Mon Feb 5 2007 Karel Zak <kzak@redhat.com> 3.2.7-10
+- fix #212637 - sysctl using deprecated syscall
+- fix #140975 - top corrupts screen when sorting on first column
+
+* Tue Jan 30 2007 Karel Zak <kzak@redhat.com> 3.2.7-9
+- fix procps_version in FAQ patch (thanks to Ian Kent)
+
+* Wed Sep 27 2006 Karel Zak <kzak@redhat.com> 3.2.7-8
+- remove zombie patch (needs more investigation)
+- fix #208100 - top command with '-c' option become not to display list of tasks
+- fix #199174 - top returns with exit code 1 even if no error occurs
+
+* Tue Sep 19 2006 Karel Zak <kzak@redhat.com> 3.2.7-7
+- fix #206551 - top fails to convert to cpu single mode when hit '1'
+
+* Tue Sep 5 2006 Karel Zak <kzak@redhat.com> 3.2.7-6
+- fix minor bug in procps-3.2.6-top-env-cpuloop.patch
+
+* Fri Aug 7 2006 Karel Zak <kzak@redhat.com> - 3.2.7-5
+- fix #189349 - 32bit vmstat on 64bit kernel
+
+* Thu Aug 3 2006 Karel Zak <kzak@redhat.com> - 3.2.7-4
+- fix #139827 - ps(1) outputs a multi-threads process as a defunct process.
+
+* Wed Jul 19 2006 Karel Zak <kzak@redhat.com> - 3.2.7-3
+- spec file cleanup
+
+* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - 3.2.7-2.1
+- rebuild
+
+* Mon Jul 10 2006 Karel Zak <kzak@redhat.com> 3.2.7-2
+- fix #134516 - ps ignores /proc/#/cmdline if contents 2047 bytes
+
+* Mon Jul 10 2006 Karel Zak <kzak@redhat.com> 3.2.7-1
+- upgrade to 3.2.7 (and sync patches)
+
+* Thu Jun 1 2006 Karel Zak <kzak@redhat.com> 3.2.6-4
+- fix #191493 - watch -n doesn't handle large integers properly
+- fix #186017 - top "Cpu0" line never updates on single processor machine
+ (bugfix added to the 'remcpu' patch)
+- fix #168444 - memory usage conflicts with /proc/meminfo
+- fix #174619 - top reports wrong values for CPU(s) in batch mode
+- fix #185299 - cpu steal time support
+
+* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> - 3.2.6-3.2
+- bump again for double-long bug on ppc(64)
+
+* Tue Feb 07 2006 Jesse Keating <jkeating@redhat.com> - 3.2.6-3.1
+- rebuilt for new gcc4.1 snapshot and glibc changes
+
+* Tue Dec 13 2005 Dan Walsh <dwalsh@redhat.com> 3.2.6-3
+- Translate context
+
+* Fri Dec 09 2005 Jesse Keating <jkeating@redhat.com>
+- rebuilt
+
+* Tue Nov 8 2005 Karel Zak <kzak@redhat.com> 3.2.6-2
+- fix #157725 - sysctl -A returns an error
+
+* Mon Oct 31 2005 Karel Zak <kzak@redhat.com> 3.2.6-1
+- update to new upstream release
+
+* Wed Oct 12 2005 Karel Zak <kzak@redhat.com> 3.2.5-8
+- fix #170083 - Top showing bad cpu usage numbers
+
+* Tue Sep 6 2005 Karel Zak <kzak@redhat.com> 3.2.5-7
+- improve procps-3.2.5-sysctl-writeonly.patch
+- fix #161449 - "top" ignores user and system toprc
+- fix #161559 - top segfaults when resizing console
+- fix #160796 - vmstat crashes when accessing LVM partition
+- fix #161303 - 'top' failed when remove cpus
+
+* Tue May 10 2005 Karel Zak <kzak@redhat.com> 3.2.5-6
+- fix permissions in the spec install section
+
+* Tue May 10 2005 Karel Zak <kzak@redhat.com> 3.2.5-5
+- fix debuginfo
+
+* Tue Apr 26 2005 Karel Zak <kzak@redhat.com> 3.2.5-4
+- fix #144459 - sysctl reports error: unknown error <...> reading key '<key>'
+ (now sysctl doesn't read data from write-only /proc/sys files)
+
+* Thu Mar 17 2005 Karel Zak <kzak@redhat.com> 3.2.5-3
+- fix top crashes when terminal window is resized (#149319)
+
+* Sat Mar 5 2005 Karel Zak <kzak@redhat.com> 3.2.5-2
+- rebuilt
+
+* Tue Feb 1 2005 Karel Zak <kzak@redhat.com> 3.2.5-1
+- update to new upstream 3.2.5
+- recreate selinux patch
+- remove errno, slabinfo21 and fullpath patches -- all fixed by upstream
+
+* Mon Jan 25 2005 Karel Zak <kzak@redhat.com> 3.2.4-4
+- pmap truncates filenames of mappings (#142751)
+
+* Mon Jan 24 2005 Karel Zak <kzak@redhat.com> 3.2.4-3
+- add support for /proc/slabinfo 2.1 (#145369)
+
+* Fri Jan 7 2005 Karel Zak <kzak@redhat.com> 3.2.4-2
+- fix sysctl errno usage (#144459)
+
+* Wed Dec 1 2004 Karel Zak <kzak@redhat.com> 3.2.4-1
+- update to new upstream release
+
+* Mon Nov 1 2004 Karel Zak <kzak@redhat.com> 3.2.3-6
+- update FAQ
+- update spec description
+- fix text in .noproc patch
+- fix segv fault if cpu number exceeding 38 (#137159)
+
+* Tue Sep 28 2004 Dan Walsh <dwalsh@redhat.com> 3.2.3-5
+- Fix terminal handling when /proc is not available.
+- Patch provided by Karel Zak
+
+* Thu Sep 16 2004 Dan Walsh <dwalsh@redhat.com> 3.2.3-4
+- Fix spec file to use makefile
+
+* Mon Aug 30 2004 Dan Walsh <dwalsh@redhat.com> 3.2.3-3
+- Fix batch mode to use dumb terminal
+
+* Tue Aug 17 2004 Florian La Roche <Florian.LaRoche@redhat.de>
+- fix building as non-root, patch from Steve G <linux_4ever@yahoo.com>
+
+* Tue Aug 10 2004 Dan Walsh <dwalsh@redhat.com> 3.2.3-1
+- Latest from Upstream
+
+* Tue Jul 20 2004 Dan Walsh <dwalsh@redhat.com> 3.2.2-2
+- Reformat ps man page
+
+* Mon Jul 19 2004 Dan Walsh <dwalsh@redhat.com> 3.2.2-1
+- Update to upstream version.
+
+* Sat Jun 26 2004 Dan Walsh <dwalsh@redhat.com> 3.2.1-7
+- Add patch to display vm_size when STATSIZE env set
+
+* Tue Jun 15 2004 Alan Cox <alan@redhat.com> 3.2.1-6
+- Removed broken SELinux patch to w
+- Added a better alternative whereby we get less data but don't fall for
+ vanished processes when SELinux is running
+
+* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Mon Jun 14 2004 Dan Walsh <dwalsh@redhat.com> 3.2.1-5
+- Fix FAQ Line
+
+* Mon Apr 09 2004 Colin Walters <walters@redhat.com> 3.2.1-4
+- Add little patch to make w/who work when getattr access
+ to /proc/<pid> for the user's login process is denied
+
+* Sun Mar 28 2004 Dan Walsh <dwalsh@redhat.com> 3.2.1-3
+- bump for rhel3
+
+* Sun Mar 28 2004 Dan Walsh <dwalsh@redhat.com> 3.2.1-2
+- Removed addtask patch, very buggy,
+- Added FAQ to docdir
+
+* Sun Mar 28 2004 Dan Walsh <dwalsh@redhat.com> 3.2.1-1
+- Update to latest from upstream
+
+* Thu Mar 25 2004 Dan Walsh <dwalsh@redhat.com> 3.2.0-3
+- Add addtask patch to total all threads times.
+
+* Wed Mar 17 2004 Dan Walsh <dwalsh@redhat.com>
+- Clean up spec file.
+
+* Tue Mar 02 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Tue Feb 24 2004 Dan Walsh <dwalsh@redhat.com> 3.2.0-1
+- New version from upstream
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Thu Jan 22 2004 Dan Walsh <dwalsh@redhat.com> 3.1.15-3
+- Match -Z to --context
+
+* Wed Jan 21 2004 Dan Walsh <dwalsh@redhat.com> 3.1.15-2
+- Add back in -Z support
+
+* Wed Jan 21 2004 Alexander Larsson <alexl@redhat.com> 3.1.15-1
+- upgrade to procps3
+- Some regressions, see bug #114012
+
+* Tue Jan 20 2004 Dan Walsh <dwalsh@redhat.com> 2.0.17-7
+- Remove LIBCURSES from skill and sysctl
+
+* Wed Dec 10 2003 Dan Walsh <dwalsh@redhat.com> 2.0.17-6
+- Turn on SELinux
+
+* Mon Dec 8 2003 Alexander Larsson <alexl@redhat.com> 2.0.17-5
+- Fix top total percentages (#109484)
+
+* Wed Oct 15 2003 Dan Walsh <dwalsh@redhat.com> 2.0.17-4
+- Turn off selinux
+
+* Wed Oct 15 2003 Dan Walsh <dwalsh@redhat.com> 2.0.17-3.sel
+- Fix help message
+
+* Thu Oct 9 2003 Dan Walsh <dwalsh@redhat.com> 2.0.17-2.sel
+- Turn on selinux
+
+* Fri Oct 3 2003 Alexander Larsson <alexl@redhat.com> 2.0.17-1
+- Update to 2.0.17, drop upstream patches, forward port remaining patches
+
+* Fri Sep 5 2003 Dan Walsh <dwalsh@redhat.com> 2.0.13-11
+- Turn off selinux
+
+* Thu Aug 28 2003 Dan Walsh <dwalsh@redhat.com> 2.0.13-10.sel
+- Add -Z switch for SELinux
+
+* Sun Aug 17 2003 Doug Ledford <dledford@redhat.com> 2.0.13-9E
+- Add patch to recognize irq and softirq time accounting in kernels that
+ support this feature
+
+* Mon Aug 11 2003 Alexander Larsson <alexl@redhat.com> 2.0.13-8
+- rebuild
+
+* Mon Aug 11 2003 Alexander Larsson <alexl@redhat.com> 2.0.13-7E
+- Add swapped patch from rik van riel
+
+* Wed Aug 6 2003 Alexander Larsson <alexl@redhat.com> 2.0.13-6
+- rebuild
+
+* Wed Aug 6 2003 Alexander Larsson <alexl@redhat.com> 2.0.13-5E
+- Update iowait patch (#101657)
+- Add wchan 64bit patch from Mark DeWandel
+
+* Mon Jul 28 2003 Dan Walsh <dwalsh@redhat.com> 2.0.13-4E
+- Add SELinux patch
+
+* Wed Jul 16 2003 Matt Wilson <msw@redhat.com> 2.0.13-3E
+- display iowait with procps-2.0.13-iowait.patch (#99061)
+
+* Fri Jul 11 2003 Alexander Larsson <alexl@redhat.com> 2.0.13-2E
+- Disable linuxthreads thread hack
+
+* Mon Jul 7 2003 Alexander Larsson <alexl@redhat.com> 2.0.13-1E
+- rebuild
+
+* Fri Jul 4 2003 Alexander Larsson <alexl@redhat.com> 2.0.13-1
+- update to 2.0.13
+- Re-merged ntpl patch
+- Add hertz fix from Ernie Petrides <petrides@redhat.com>
+
+* Wed Jun 04 2003 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Fri May 23 2003 Alexander Larsson <alexl@redhat.com> 2.0.12-1
+- Update to 2.0.12
+- Add patch to fix segfault on ps axl (#91453)
+
+* Fri Mar 14 2003 Alexander Larsson <alexl@redhat.com> 2.0.11-7
+- Add patch that fixes negative priorities in top.
+
+* Thu Feb 20 2003 Alexander Larsson <alexl@redhat.com> 2.0.11-6
+- New NPTL patch:
+- Added skipthreads optimization to top
+- Don't read threads in 'w'
+
+* Thu Feb 20 2003 Alexander Larsson <alexl@redhat.com> 2.0.11-5
+- Update the NPTL patch since the kernel /proc was fixed
+- For kernels >= 2.4.20-2.50
+
+* Mon Feb 17 2003 Alexander Larsson <alexl@redhat.com> 2.0.11-4
+- Update nptl patch to new /proc layout.
+
+* Wed Jan 22 2003 Tim Powers <timp@redhat.com> 2.0.11-3
+- rebuilt
+
+* Wed Jan 22 2003 Alexander Larsson <alexl@redhat.com> 2.0.11-2
+- Created nptl patch after discussion with ingo and arjan
+
+* Tue Jan 21 2003 Alexander Larsson <alexl@redhat.com> 2.0.11-1
+- Update to 2.0.11
+
+* Mon Dec 16 2002 Elliot Lee <sopwith@redhat.com> 2.0.10-4
+- Fix %%install in changelog
+
+* Tue Nov 19 2002 Jakub Jelinek <jakub@redhat.com> 2.0.10-3
+- Fix for Hammer
+
+* Wed Oct 23 2002 Alexander Larsson <alexl@redhat.com> 2.0.10-2
+- Remove uninstalled files in %%install. Add pmap to %%files
+
+* Tue Oct 8 2002 Alexander Larsson <alexl@redhat.com> 2.0.10-1
+- Update to 2.0.10
+- Removed applied patches.
+
+* Mon Aug 12 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-25
+- Add patch to protect against idle ticks going backwards. Fixes #71237
+
+* Thu Aug 8 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-24
+- Fix saving of sort, fixes #32757
+- Fix printing size, fixes #48224
+- Fix float decimal point input #58163
+
+* Thu Aug 8 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-23
+- Fix unsigned/signed bug. Closes #60998.
+- Update threadbadhack to correctly propagate process time to the main thread.
+
+* Wed Aug 7 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-22
+- Don't strip binaries
+
+* Fri Jul 12 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-21
+- Remove the X11 subpackage
+
+* Mon Jul 1 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-19
+- Added patch that fixes #35174
+
+* Wed Jun 26 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-18
+- New thread badhack patch. Fixes a segfault.
+
+* Mon Jun 24 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-16
+- New thread badhack. Now enabled by default.
+
+* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Thu Jun 20 2002 Alexander Larsson <alexl@redhat.com> 2.0.7-14
+- Added badhack to support hiding threads
+
+* Thu May 23 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Apr 15 2002 Bill Nottingham <notting@redhat.com> 2.0.7-12
+- add ldconfig in postun section
+
+* Mon Aug 27 2001 Trond Eivind Glomsrod <teg@redhat.com> 2.0.7-11
+- Add ncurses-devel as a build dependency (#49562)
+
+* Sat Jul 21 2001 Tim Powers <timp@redhat.com>
+- removed applnk entry, one of the things that's cluttering our menus
+
+* Sun Jun 24 2001 Elliot Lee <sopwith@redhat.com>
+- Bump release + rebuild.
+
+* Thu Apr 5 2001 Jakub Jelinek <jakub@redhat.com>
+- fix AIX style user defined formats (#34833)
+
+* Thu Mar 22 2001 Bill Nottingham <notting@redhat.com>
+- add a '-e' to sysctl to ignore errors (#31852)
+
+* Mon Mar 5 2001 Preston Brown <pbrown@redhat.com>
+- bigger buffer for reading /proc/stat fixes segfault (#27840)
+
+* Thu Feb 1 2001 Preston Brown <pbrown@redhat.com>
+- make sysctl return a value when errors occur (#18820).
+- support big UIDs (#22683)
+
+* Mon Jan 22 2001 Helge Deller <hdeller@redhat.com>
+- work-around for negative CPU output (Bug #18380)
+
+* Thu Aug 17 2000 Than Ngo <than@redhat.com>
+- fix failing in RPM post script (Bug #16226)
+
+* Wed Jul 26 2000 Michael K. Johnson <johnsonm@redhat.com>
+- Added Jakub's locale patch
+
+* Fri Jul 14 2000 Michael K. Johnson <johnsonm@redhat.com>
+- procps-2.0.7
+- integrated all patches except for signames patch, which is broken
+ and unnecessary
+- See NEWS for changes between 2.0.6 and 2.0.7
+- Added patch to correctly install desktop file. Oops.
+
+* Wed Jul 12 2000 Prospector <bugzilla@redhat.com>
+- automatic rebuild
+
+* Tue Jul 03 2000 Preston Brown <pbrown@redhat.com>
+- larger buffers for reading /proc/stat
+
+* Tue Jun 13 2000 Preston Brown <pbrown@redhat.com>
+- FHS paths
+
+* Tue May 30 2000 Preston Brown <pbrown@redhat.com>
+- add smp, signal name patches from VA Linux. Thanks guys.
+
+* Mon May 22 2000 Harald Hoyer <harald@redhat.com>
+- added sysctl.conf (5) man page
+
+* Wed May 10 2000 Bill Nottingham <notting@redhat.com>
+- fix PAGE_SIZE mismatch on ia64
+
+* Sun May 7 2000 Bill Nottingham <notting@redhat.com>
+- rebuild with different optimizations for ia64
+
+* Fri Mar 24 2000 Bernhard Rosenkraenzer <bero@redhat.com>
+- rebuild with current ncurses
+
+* Tue Mar 7 2000 Bill Nottingham <notting@redhat.com>
+- fix end-of-file behavior in sysctl
+
+* Mon Feb 07 2000 Preston Brown <pbrown@redhat.com>
+- wmconfig -> desktop
+
+* Mon Feb 7 2000 Jakub Jelinek <jakub@redhat.com>
+- don't try to load System.map (and spit error messages if it does not
+ exist) if ps or top are not going to use it, both to speed things up
+ and remove the ugly messages when they don't make sense.
+- in top, print the possible error messages using standard top SHOWMESSAGE
+ (because it will be now printed out when already in terminal mode).
+
+* Thu Feb 3 2000 Matt Wilson <msw@redhat.com>
+- added patch to prevent divide by zero on UltraSparc
+- gzip man pages
+
--- /dev/null
+#specfile originally created for Fedora, modified for Moblin Linux
+Summary: System and process monitoring utilities
+Name: procps
+Version: 3.2.8
+Release: 1
+License: GPLv2+ and LGPLv2+
+Group: Applications/System
+URL: http://procps.sourceforge.net
+Source: http://procps.sourceforge.net/procps-%{version}.tar.gz
+Source1: FAQ
+Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+Patch1: procps-3.2.7-selinux.patch
+Patch2: procps-3.1.15-misc.patch
+Patch3: procps-3.2.3-FAQ.patch
+Patch4: procps-3.2.1-selinux-workaround.patch
+Patch6: procps-3.2.3-noproc.patch
+Patch7: procps-3.2.3-pseudo.patch
+Patch8: procps-3.2.4-0x9b.patch
+# 157725 - sysctl -A returns an error
+Patch9: procps-3.2.5-sysctl-writeonly.patch
+# 161449 - "top" ignores user and system toprc
+Patch10: procps-3.2.5-top-rc.patch
+# 161303 - 'top' failed when remove cpus
+# 186017 - Top "Cpu0" line never updates on single processor machine
+Patch11: procps-3.2.7-top-remcpu.patch
+# Selinux
+Patch12: procps-3.2.6-libselinux.patch
+# 177453 - for VIRT use proc->vm_size rather then proc->size (workaround)
+Patch13: procps-3.2.6-top-env-vmsize.patch
+# 174619 - workaround for reliable Cpu(s) data in the first loop
+Patch14: procps-3.2.6-top-env-cpuloop.patch
+# 185299 - cpu steal time support
+Patch15: procps-3.2.7-vmstat-cpusteal.patch
+# 134516 - ps ignores /proc/#/cmdline if contents 2047 bytes
+Patch16: procps-3.2.7-longcmd.patch
+# 189349 - 32bit vmstat on 64bit kernel
+Patch17: procps-3.2.7-vmstat-pgpg.patch
+# 212637 - sysctl using deprecated syscall
+# 228870 - process `sysctl' is using deprecated sysctl ...
+Patch18: procps-3.2.7-sysctl-ignore.patch
+# 140975 - top corrupts screen when sorting on first column
+Patch19: procps-3.2.7-top-sorthigh.path
+# 234546 - 'w' doesn't give correct information about what's being run.
+Patch20: procps-3.2.7-w-best.patch
+# 183029 - watch ignores unicode characters
+#Patch21: procps-3.2.7-watch-unicode.patch
+# 222251 - STIME can jitter by one second
+Patch22: procps-3.2.7-ps-stime.patch
+#244152 - ps truncates eip and esp to 32-bit values on 64-bit systems
+Patch23: procps-3.2.7-ps-eip64.patch
+#244960 - ps manpage formatted incorrectly
+Patch24: procps-3.2.7-psman.patch
+#255441 - ldopen libselinux.so.1 instead of libselinux.so
+Patch25: procps-3.2.7-ps-libselinux.patch
+#185994 - error when using "Single Cpu = Off" option
+Patch26: procps-3.2.7-top-cpu0.patch
+#354001 - CPU value in top is reported as an integer
+Patch27: procps-3.2.7-top-cpuint.patch
+#296471 - update top man page
+Patch28: procps-3.2.7-top-manpage.patch
+#440694 - strange text after selecting few field
+Patch30: procps-3.2.7-top-clrscr.patch
+#435453 - errors with man -t formatting of ps man page
+Patch31: procps-3.2.7-ps-man-fmt.patch
+#472783 - 'vmstat -p <partition name>',
+# the detailed statistics of the partition name is not output.
+Patch32: procps-3.2.7-vmstat-partstats-long.patch
+# Fix vmstat header to be 80 chars not 81
+Patch33: procps-3.2.7-vmstat-header.patch
+# rhel bug #475963: slabtop -o should display the info once
+Patch34: procps-3.2.7-slabtop-once.patch
+#476134 - added timestamp to vmstat with new option -t
+Patch35: procps-3.2.7-vmstat-timestamp.patch
+#manual page updated to document the -t functionality
+Patch36: procps-3.2.7-vmstat-timestamp-manpage.patch
+# 'requested writes' display in partition statistics
+Patch37: procps-3.2.7-vmstat-partstats-reqwrites.patch
+# '-l' option of 'free' documented
+Patch38: procps-3.2.7-free-hlmem.patch
+# enable core dump generation
+Patch39: procps-enable-core.patch
+# fix "Unknown HZ value! (??) Assume 100." issue
+Patch40: unknown-hz-value-fix.patch
+
+BuildRequires: ncurses-devel
+
+%description
+The procps package contains a set of system utilities that provide
+system information. Procps includes ps, free, skill, pkill, pgrep,
+snice, tload, top, uptime, vmstat, w, watch and pdwx. The ps command
+displays a snapshot of running processes. The top command provides
+a repetitive update of the statuses of running processes. The free
+command displays the amounts of free and used memory on your
+system. The skill command sends a terminate command (or another
+specified signal) to a specified set of processes. The snice
+command is used to change the scheduling priority of specified
+processes. The tload command prints a graph of the current system
+load average to a specified tty. The uptime command displays the
+current time, how long the system has been running, how many users
+are logged on, and system load averages for the past one, five,
+and fifteen minutes. The w command displays a list of the users
+who are currently logged on and what they are running. The watch
+program watches a running program. The vmstat command displays
+virtual memory statistics about processes, memory, paging, block
+I/O, traps, and CPU activity. The pwdx command reports the current
+working directory of a process or processes.
+
+%prep
+%setup -q
+
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
+%patch15 -p1
+%patch16 -p1
+%patch17 -p1
+%patch18 -p1
+%patch19 -p1
+%patch20 -p1
+#%patch21 -p1
+%patch22 -p1
+%patch23 -p1
+%patch24 -p1
+%patch25 -p1
+%patch26 -p1
+%patch27 -p1
+%patch28 -p1
+%patch30 -p1
+%patch31 -p1
+%patch32 -p1
+%patch33 -p1
+%patch34 -p1
+%patch35 -p1
+%patch36 -p1
+%patch37 -p1
+%patch38 -p1
+%patch39 -p1
+%patch40 -p1
+
+cp %SOURCE1 .
+
+%build
+make SHARED=1 CFLAGS="$RPM_OPT_FLAGS" W_SHOWFROM=-DW_SHOWFROM lib64=%{_lib}
+
+%install
+rm -rf %{buildroot}
+make ldconfig=true DESTDIR=%{buildroot} lib64=%{_lib} install="install -D" \
+ SKIP="/bin/kill /usr/share/man/man1/kill.1" install
+mkdir -p %{buildroot}/%{_docdir}/procps-%{version}
+# keep 'rpm' happy...
+chmod -R u+w %{buildroot}/sbin
+chmod -R u+w %{buildroot}/bin
+chmod -R u+w %{buildroot}/usr/bin
+chmod -R u+w %{buildroot}/lib*
+
+%clean
+rm -rf %{buildroot}
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%defattr(0644,root,root,755)
+%doc NEWS BUGS TODO FAQ
+%attr(755,root,root) /%{_lib}/*
+%attr(755,root,root) /bin/ps
+%attr(755,root,root) /sbin/sysctl
+%attr(755,root,root) /usr/bin/*
+
+%doc %attr(0644,root,root) %{_mandir}/man1/*
+%doc %attr(0644,root,root) %{_mandir}/man8/*
+%doc %attr(0644,root,root) %{_mandir}/man5/*
+
--- /dev/null
+From 8e8a824533643de4a4a221358a8c5ae7d4e9e474 Mon Sep 17 00:00:00 2001
+From: Ameya Palande <ameya.palande@nokia.com>
+Date: Wed, 28 Jul 2010 14:41:24 +0300
+Subject: [PATCH] unknown hz value fix
+
+Fixes linux version detection. This relied on a side-effect of the elf loader
+and previously hoped that constructors would be called in a certain order.
+That behavior was undefined and hence unreliable. For newer kernels, an elf
+note is now checked to extract HZ tick count.
+
+Original author: <david.sugar@canonical.com>
+Backported to meego by: Ameya Palande <ameya.palande@nokia.com>
+---
+ proc/sysinfo.c | 1 +
+ proc/version.c | 5 ++---
+ proc/version.h | 1 +
+ 3 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/proc/sysinfo.c b/proc/sysinfo.c
+index 0ad7fba..793829f 100644
+--- a/proc/sysinfo.c
++++ b/proc/sysinfo.c
+@@ -212,6 +212,7 @@ static int check_for_privs(void){
+ static void init_libproc(void) __attribute__((constructor));
+ static void init_libproc(void){
+ have_privs = check_for_privs();
++ init_Linux_version(); // make sure we have version before continuing...
+ // ought to count CPUs in /proc/stat instead of relying
+ // on glibc, which foolishly tries to parse /proc/cpuinfo
+ //
+diff --git a/proc/version.c b/proc/version.c
+index 391b9bb..2049e15 100644
+--- a/proc/version.c
++++ b/proc/version.c
+@@ -35,11 +35,10 @@ void display_version(void) {
+
+ int linux_version_code;
+
+-static void init_Linux_version(void) __attribute__((constructor));
+-static void init_Linux_version(void) {
++void init_Linux_version(void) {
+ static struct utsname uts;
+ int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */
+-
++
+ if (uname(&uts) == -1) /* failure implies impending death */
+ exit(1);
+ if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3)
+diff --git a/proc/version.h b/proc/version.h
+index 492d251..28c9b49 100644
+--- a/proc/version.h
++++ b/proc/version.h
+@@ -14,6 +14,7 @@
+
+ EXTERN_C_BEGIN
+
++extern void init_Linux_version(void); /* initialize linux version */
+ extern void display_version(void); /* display suite version */
+ extern const char procps_version[]; /* global buf for suite version */
+ extern const char procps_number_version[]; /* global buf for suite number version */
+--
+1.7.0.4
+
--- /dev/null
+.\" Manual page for pgrep / pkill.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Copyright 2000 Kjetil Torgrim Homme
+.\"
+.TH PGREP 1 "June 25, 2000" "Linux" "Linux User's Manual"
+.SH NAME
+pgrep, pkill \- look up or signal processes based on name and other attributes
+
+.SH SYNOPSIS
+pgrep [\-flvx] [\-d \fIdelimiter\fP] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
+.br
+ [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
+.br
+ [\-t \fIterm\fP,...] [\fIpattern\fP]
+
+pkill [\-\fIsignal\fP] [\-fvx] [\-n|\-o] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
+.br
+ [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
+.br
+ [\-t \fIterm\fP,...] [\fIpattern\fP]
+
+.SH DESCRIPTION
+\fBpgrep\fP looks through the currently running processes and lists the
+process IDs which matches the selection criteria to stdout. All
+the criteria have to match. For example,
+
+pgrep -u root sshd
+
+will only list the processes called \fBsshd\fP AND owned by \fBroot\fP.
+On the other hand,
+
+pgrep -u root,daemon
+
+will list the processes owned by \fBroot\fP OR \fBdaemon\fP.
+
+\fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP)
+to each process instead of listing them on stdout.
+
+.SH OPTIONS
+.TP
+\-d \fIdelimiter\fP
+Sets the string used to delimit each process ID in the output (by
+default a newline). (\fBpgrep\fP only.)
+.TP
+\-f
+The \fIpattern\fP is normally only matched against the process name.
+When \-f is set, the full command line is used.
+.TP
+\-g \fIpgrp\fP,...
+Only match processes in the process group IDs listed. Process group 0
+is translated into \fBpgrep\fP's or \fBpkill\fP's own process group.
+.TP
+\-G \fIgid\fP,...
+Only match processes whose real group ID is listed. Either the
+numerical or symbolical value may be used.
+.TP
+\-l
+List the process name as well as the process ID. (\fBpgrep\fP only.)
+.TP
+\-n
+Select only the newest (most recently started) of the matching
+processes.
+.TP
+\-o
+Select only the oldest (least recently started) of the matching
+processes.
+.TP
+\-P \fIppid\fP,...
+Only match processes whose parent process ID is listed.
+.TP
+\-s \fIsid\fP,...
+Only match processes whose process session ID is listed. Session ID 0
+is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID.
+.TP
+\-t \fIterm\fP,...
+Only match processes whose controlling terminal is listed. The
+terminal name should be specified without the "/dev/" prefix.
+.TP
+\-u \fIeuid\fP,...
+Only match processes whose effective user ID is listed. Either the
+numerical or symbolical value may be used.
+.TP
+\-U \fIuid\fP,...
+Only match processes whose real user ID is listed. Either the
+numerical or symbolical value may be used.
+.TP
+\-v
+Negates the matching.
+.TP
+\-x
+Only match processes whose name (or command line if \-f is specified)
+\fBexactly\fP match the \fIpattern\fP.
+.TP
+\-\fIsignal\fP
+Defines the signal to send to each matched process. Either the
+numeric or the symbolic signal name can be used. (\fBpkill\fP only.)
+
+.SH OPERANDS
+.TP
+\fIpattern\fP
+Specifies an Extended Regular Expression for matching against the
+process names or command lines.
+
+.SH EXAMPLES
+Example 1: Find the process ID of the \fBnamed\fP daemon:
+
+unix$ pgrep \-u root named
+
+Example 2: Make \fBsyslog\fP reread its configuration file:
+
+unix$ pkill \-HUP syslogd
+
+Example 3: Give detailed information on all \fBxterm\fP processes:
+
+unix$ ps \-fp $(pgrep \-d, \-x xterm)
+
+Example 4: Make all \fBnetscape\fP processes run nicer:
+
+unix$ renice +4 `pgrep netscape`
+
+.SH "EXIT STATUS"
+.TP
+.I "0"
+One or more processes matched the criteria.
+.TP
+.I "1"
+No processes matched.
+.TP
+.I "2"
+Syntax error in the command line.
+.TP
+.I "3"
+Fatal error: out of memory etc.
+
+.SH NOTES
+The process name used for matching is limited to the 15 characters
+present in the output of /proc/\fIpid\fP/stat. Use the \-f option to
+match against the complete command line, /proc/\fIpid\fP/cmdline.
+
+The running \fBpgrep\fP or \fBpkill\fP process will never report
+itself as a match.
+
+.SH BUGS
+The options \-n and \-o and \-v can not be combined. Let me know if
+you need to do this.
+
+Defunct processes are reported.
+
+.SH "SEE ALSO"
+ps(1) regex(7) signal(7) killall(1) skill(1) kill(1) kill(2)
+
+.SH STANDARDS
+\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7. This
+implementation is fully compatible.
+
+.SH AUTHOR
+Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+
+Albert Cahalan <albert@users.sf.net> is the current maintainer of
+the procps package.
+
+Please send bug reports to <procps-feedback@lists.sf.net>
--- /dev/null
+/* emacs settings: -*- c-basic-offset: 8 tab-width: 8 -*-
+ *
+ * pgrep/pkill -- utilities to filter the process table
+ *
+ * Copyright 2000 Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+ *
+ * May be distributed under the conditions of the
+ * GNU General Public License; a copy is in COPYING
+ *
+ * Changes by Albert Cahalan, 2002,2006.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+#include <errno.h>
+
+#include "proc/readproc.h"
+#include "proc/sig.h"
+#include "proc/devname.h"
+#include "proc/sysinfo.h"
+#include "proc/version.h" /* procps_version */
+
+// EXIT_SUCCESS is 0
+// EXIT_FAILURE is 1
+#define EXIT_USAGE 2
+#define EXIT_FATAL 3
+
+static int i_am_pkill = 0;
+static const char *progname = "pgrep";
+
+union el {
+ long num;
+ char * str;
+};
+
+/* User supplied arguments */
+
+static int opt_full = 0;
+static int opt_long = 0;
+static int opt_oldest = 0;
+static int opt_newest = 0;
+static int opt_negate = 0;
+static int opt_exact = 0;
+static int opt_signal = SIGTERM;
+static int opt_lock = 0;
+static int opt_case = 0;
+
+static const char *opt_delim = "\n";
+static union el *opt_pgrp = NULL;
+static union el *opt_rgid = NULL;
+static union el *opt_pid = NULL;
+static union el *opt_ppid = NULL;
+static union el *opt_sid = NULL;
+static union el *opt_term = NULL;
+static union el *opt_euid = NULL;
+static union el *opt_ruid = NULL;
+static char *opt_pattern = NULL;
+static char *opt_pidfile = NULL;
+
+static int usage (int opt) NORETURN;
+static int usage (int opt)
+{
+ int err = (opt=='?'); /* getopt() uses '?' to mark an error */
+ FILE *fp = err ? stderr : stdout;
+
+ if (i_am_pkill)
+ fprintf (fp, "Usage: pkill [-SIGNAL] [-fvx] ");
+ else
+ fprintf (fp, "Usage: pgrep [-flvx] [-d DELIM] ");
+ fprintf (fp, "[-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n"
+ "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] "
+ "[PATTERN]\n");
+
+ exit(err ? EXIT_USAGE : EXIT_SUCCESS);
+}
+
+
+static union el *split_list (const char *restrict str, int (*convert)(const char *, union el *))
+{
+ char *copy = strdup (str);
+ char *ptr = copy;
+ char *sep_pos;
+ int i = 0;
+ int size = 0;
+ union el *list = NULL;
+
+ do {
+ if (i == size) {
+ size = size * 5 / 4 + 4;
+ // add 1 because slot zero is a count
+ list = realloc (list, 1 + size * sizeof *list);
+ if (list == NULL)
+ exit (EXIT_FATAL);
+ }
+ sep_pos = strchr (ptr, ',');
+ if (sep_pos)
+ *sep_pos = 0;
+ // Use ++i instead of i++ because slot zero is a count
+ if (!convert (ptr, &list[++i]))
+ exit (EXIT_USAGE);
+ if (sep_pos)
+ ptr = sep_pos + 1;
+ } while (sep_pos);
+
+ free (copy);
+ if (!i) {
+ free (list);
+ list = NULL;
+ } else {
+ list[0].num = i;
+ }
+ return list;
+}
+
+// strict_atol returns a Boolean: TRUE if the input string
+// contains a plain number, FALSE if there are any non-digits.
+static int strict_atol (const char *restrict str, long *restrict value)
+{
+ int res = 0;
+ int sign = 1;
+
+ if (*str == '+')
+ ++str;
+ else if (*str == '-') {
+ ++str;
+ sign = -1;
+ }
+
+ for ( ; *str; ++str) {
+ if (! isdigit (*str))
+ return (0);
+ res *= 10;
+ res += *str - '0';
+ }
+ *value = sign * res;
+ return 1;
+}
+
+#include <sys/file.h>
+
+// Seen non-BSD code do this:
+//
+//if (fcntl_lock(pid_fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1)
+// return -1;
+int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len)
+{
+ struct flock lock[1];
+
+ lock->l_type = type;
+ lock->l_whence = whence;
+ lock->l_start = start;
+ lock->l_len = len;
+
+ return fcntl(fd, cmd, lock);
+}
+
+
+// We try a read lock. The daemon should have a write lock.
+// Seen using flock: FreeBSD code
+static int has_flock(int fd)
+{
+ return flock(fd, LOCK_SH|LOCK_NB)==-1 && errno==EWOULDBLOCK;
+}
+
+// We try a read lock. The daemon should have a write lock.
+// Seen using fcntl: libslack
+static int has_fcntl(int fd)
+{
+ struct flock f; // seriously, struct flock is for a fnctl lock!
+ f.l_type = F_RDLCK;
+ f.l_whence = SEEK_SET;
+ f.l_start = 0;
+ f.l_len = 0;
+ return fcntl(fd,F_SETLK,&f)==-1 && (errno==EACCES || errno==EAGAIN);
+}
+
+static union el *read_pidfile(void)
+{
+ char buf[12];
+ int fd;
+ struct stat sbuf;
+ char *endp;
+ int pid;
+ union el *list = NULL;
+
+ fd = open(opt_pidfile, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if(fd<0)
+ goto out;
+ if(fstat(fd,&sbuf) || !S_ISREG(sbuf.st_mode) || sbuf.st_size<1)
+ goto out;
+ // type of lock, if any, is not standardized on Linux
+ if(opt_lock && !has_flock(fd) && !has_fcntl(fd))
+ goto out;
+ memset(buf,'\0',sizeof buf);
+ buf[read(fd,buf+1,sizeof buf-2)] = '\0';
+ pid = strtoul(buf+1,&endp,10);
+ if(endp<=buf+1 || pid<1 || pid>0x7fffffff)
+ goto out;
+ if(*endp && !isspace(*endp))
+ goto out;
+ list = malloc(2 * sizeof *list);
+ list[0].num = 1;
+ list[1].num = pid;
+out:
+ close(fd);
+ return list;
+}
+
+static int conv_uid (const char *restrict name, union el *restrict e)
+{
+ struct passwd *pwd;
+
+ if (strict_atol (name, &e->num))
+ return (1);
+
+ pwd = getpwnam (name);
+ if (pwd == NULL) {
+ fprintf (stderr, "%s: invalid user name: %s\n",
+ progname, name);
+ return 0;
+ }
+ e->num = pwd->pw_uid;
+ return 1;
+}
+
+
+static int conv_gid (const char *restrict name, union el *restrict e)
+{
+ struct group *grp;
+
+ if (strict_atol (name, &e->num))
+ return 1;
+
+ grp = getgrnam (name);
+ if (grp == NULL) {
+ fprintf (stderr, "%s: invalid group name: %s\n",
+ progname, name);
+ return 0;
+ }
+ e->num = grp->gr_gid;
+ return 1;
+}
+
+
+static int conv_pgrp (const char *restrict name, union el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ fprintf (stderr, "%s: invalid process group: %s\n",
+ progname, name);
+ return 0;
+ }
+ if (e->num == 0)
+ e->num = getpgrp ();
+ return 1;
+}
+
+
+static int conv_sid (const char *restrict name, union el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ fprintf (stderr, "%s: invalid session id: %s\n",
+ progname, name);
+ return 0;
+ }
+ if (e->num == 0)
+ e->num = getsid (0);
+ return 1;
+}
+
+
+static int conv_num (const char *restrict name, union el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ fprintf (stderr, "%s: not a number: %s\n",
+ progname, name);
+ return 0;
+ }
+ return 1;
+}
+
+
+static int conv_str (const char *restrict name, union el *restrict e)
+{
+ e->str = strdup (name);
+ return 1;
+}
+
+
+static int match_numlist (long value, const union el *restrict list)
+{
+ int found = 0;
+ if (list == NULL)
+ found = 0;
+ else {
+ int i;
+ for (i = list[0].num; i > 0; i--) {
+ if (list[i].num == value)
+ found = 1;
+ }
+ }
+ return found;
+}
+
+static int match_strlist (const char *restrict value, const union el *restrict list)
+{
+ int found = 0;
+ if (list == NULL)
+ found = 0;
+ else {
+ int i;
+ for (i = list[0].num; i > 0; i--) {
+ if (! strcmp (list[i].str, value))
+ found = 1;
+ }
+ }
+ return found;
+}
+
+static void output_numlist (const union el *restrict list, int num)
+{
+ int i;
+ const char *delim = opt_delim;
+ for (i = 0; i < num; i++) {
+ if(i+1==num)
+ delim = "\n";
+ printf ("%ld%s", list[i].num, delim);
+ }
+}
+
+static void output_strlist (const union el *restrict list, int num)
+{
+// FIXME: escape codes
+ int i;
+ const char *delim = opt_delim;
+ for (i = 0; i < num; i++) {
+ if(i+1==num)
+ delim = "\n";
+ printf ("%s%s", list[i].str, delim);
+ }
+}
+
+static PROCTAB *do_openproc (void)
+{
+ PROCTAB *ptp;
+ int flags = 0;
+
+ if (opt_pattern || opt_full)
+ flags |= PROC_FILLCOM;
+ if (opt_ruid || opt_rgid)
+ flags |= PROC_FILLSTATUS;
+ if (opt_oldest || opt_newest || opt_pgrp || opt_sid || opt_term)
+ flags |= PROC_FILLSTAT;
+ if (!(flags & PROC_FILLSTAT))
+ flags |= PROC_FILLSTATUS; // FIXME: need one, and PROC_FILLANY broken
+ if (opt_euid && !opt_negate) {
+ int num = opt_euid[0].num;
+ int i = num;
+ uid_t *uids = malloc (num * sizeof (uid_t));
+ if (uids == NULL)
+ exit (EXIT_FATAL);
+ while (i-- > 0) {
+ uids[i] = opt_euid[i+1].num;
+ }
+ flags |= PROC_UID;
+ ptp = openproc (flags, uids, num);
+ } else {
+ ptp = openproc (flags);
+ }
+ return ptp;
+}
+
+static regex_t * do_regcomp (void)
+{
+ regex_t *preg = NULL;
+
+ if (opt_pattern) {
+ char *re;
+ char errbuf[256];
+ int re_err;
+
+ preg = malloc (sizeof (regex_t));
+ if (preg == NULL)
+ exit (EXIT_FATAL);
+ if (opt_exact) {
+ re = malloc (strlen (opt_pattern) + 5);
+ if (re == NULL)
+ exit (EXIT_FATAL);
+ sprintf (re, "^(%s)$", opt_pattern);
+ } else {
+ re = opt_pattern;
+ }
+
+ re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB | opt_case);
+ if (re_err) {
+ regerror (re_err, preg, errbuf, sizeof(errbuf));
+ fputs(errbuf,stderr);
+ exit (EXIT_USAGE);
+ }
+ }
+ return preg;
+}
+
+static union el * select_procs (int *num)
+{
+ PROCTAB *ptp;
+ proc_t task;
+ unsigned long long saved_start_time; // for new/old support
+ pid_t saved_pid = 0; // for new/old support
+ int matches = 0;
+ int size = 0;
+ regex_t *preg;
+ pid_t myself = getpid();
+ union el *list = NULL;
+ char cmd[4096];
+
+ ptp = do_openproc();
+ preg = do_regcomp();
+
+ if (opt_newest) saved_start_time = 0ULL;
+ if (opt_oldest) saved_start_time = ~0ULL;
+ if (opt_newest) saved_pid = 0;
+ if (opt_oldest) saved_pid = INT_MAX;
+
+ memset(&task, 0, sizeof (task));
+ while(readproc(ptp, &task)) {
+ int match = 1;
+
+ if (task.XXXID == myself)
+ continue;
+ else if (opt_newest && task.start_time < saved_start_time)
+ match = 0;
+ else if (opt_oldest && task.start_time > saved_start_time)
+ match = 0;
+ else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid))
+ match = 0;
+ else if (opt_pid && ! match_numlist (task.tgid, opt_pid))
+ match = 0;
+ else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp))
+ match = 0;
+ else if (opt_euid && ! match_numlist (task.euid, opt_euid))
+ match = 0;
+ else if (opt_ruid && ! match_numlist (task.ruid, opt_ruid))
+ match = 0;
+ else if (opt_rgid && ! match_numlist (task.rgid, opt_rgid))
+ match = 0;
+ else if (opt_sid && ! match_numlist (task.session, opt_sid))
+ match = 0;
+ else if (opt_term) {
+ if (task.tty == 0) {
+ match = 0;
+ } else {
+ char tty[256];
+ dev_to_tty (tty, sizeof(tty) - 1,
+ task.tty, task.XXXID, ABBREV_DEV);
+ match = match_strlist (tty, opt_term);
+ }
+ }
+ if (opt_long || (match && opt_pattern)) {
+ if (opt_full && task.cmdline) {
+ int i = 0;
+ int bytes = sizeof (cmd) - 1;
+
+ /* make sure it is always NUL-terminated */
+ cmd[bytes] = 0;
+ /* make room for SPC in loop below */
+ --bytes;
+
+ strncpy (cmd, task.cmdline[i], bytes);
+ bytes -= strlen (task.cmdline[i++]);
+ while (task.cmdline[i] && bytes > 0) {
+ strncat (cmd, " ", bytes);
+ strncat (cmd, task.cmdline[i], bytes);
+ bytes -= strlen (task.cmdline[i++]) + 1;
+ }
+ } else {
+ strcpy (cmd, task.cmd);
+ }
+ }
+
+ if (match && opt_pattern) {
+ if (regexec (preg, cmd, 0, NULL, 0) != 0)
+ match = 0;
+ }
+
+ if (match ^ opt_negate) { /* Exclusive OR is neat */
+ if (opt_newest) {
+ if (saved_start_time == task.start_time &&
+ saved_pid > task.XXXID)
+ continue;
+ saved_start_time = task.start_time;
+ saved_pid = task.XXXID;
+ matches = 0;
+ }
+ if (opt_oldest) {
+ if (saved_start_time == task.start_time &&
+ saved_pid < task.XXXID)
+ continue;
+ saved_start_time = task.start_time;
+ saved_pid = task.XXXID;
+ matches = 0;
+ }
+ if (matches == size) {
+ size = size * 5 / 4 + 4;
+ list = realloc(list, size * sizeof *list);
+ if (list == NULL)
+ exit (EXIT_FATAL);
+ }
+ if (opt_long) {
+ char buff[5096]; // FIXME
+ sprintf (buff, "%d %s", task.XXXID, cmd);
+ list[matches++].str = strdup (buff);
+ } else {
+ list[matches++].num = task.XXXID;
+ }
+ }
+
+ memset (&task, 0, sizeof (task));
+ }
+ closeproc (ptp);
+
+ *num = matches;
+ return list;
+}
+
+
+static void parse_opts (int argc, char **argv)
+{
+ char opts[32] = "";
+ int opt;
+ int criteria_count = 0;
+
+ if (strstr (argv[0], "pkill")) {
+ i_am_pkill = 1;
+ progname = "pkill";
+ /* Look for a signal name or number as first argument */
+ if (argc > 1 && argv[1][0] == '-') {
+ int sig;
+ sig = signal_name_to_number (argv[1] + 1);
+ if (sig == -1 && isdigit (argv[1][1]))
+ sig = atoi (argv[1] + 1);
+ if (sig != -1) {
+ int i;
+ for (i = 2; i < argc; i++)
+ argv[i-1] = argv[i];
+ --argc;
+ opt_signal = sig;
+ }
+ }
+ } else {
+ /* These options are for pgrep only */
+ strcat (opts, "ld:");
+ }
+
+ strcat (opts, "LF:fnovxP:g:s:u:U:G:t:?V");
+
+ while ((opt = getopt (argc, argv, opts)) != -1) {
+ switch (opt) {
+// case 'D': // FreeBSD: print info about non-matches for debugging
+// break;
+ case 'F': // FreeBSD: the arg is a file containing a PID to match
+ opt_pidfile = strdup (optarg);
+ ++criteria_count;
+ break;
+ case 'G': // Solaris: match rgid/rgroup
+ opt_rgid = split_list (optarg, conv_gid);
+ if (opt_rgid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+// case 'I': // FreeBSD: require confirmation before killing
+// break;
+// case 'J': // Solaris: match by project ID (name or number)
+// break;
+ case 'L': // FreeBSD: fail if pidfile (see -F) not locked
+ opt_lock++;
+ break;
+// case 'M': // FreeBSD: specify core (OS crash dump) file
+// break;
+// case 'N': // FreeBSD: specify alternate namelist file (for us, System.map -- but we don't need it)
+// break;
+ case 'P': // Solaris: match by PPID
+ opt_ppid = split_list (optarg, conv_num);
+ if (opt_ppid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+// case 'S': // FreeBSD: don't ignore the built-in kernel tasks
+// break;
+// case 'T': // Solaris: match by "task ID" (probably not a Linux task)
+// break;
+ case 'U': // Solaris: match by ruid/rgroup
+ opt_ruid = split_list (optarg, conv_uid);
+ if (opt_ruid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 'V':
+ fprintf(stdout, "%s (%s)\n", progname, procps_version);
+ exit(EXIT_SUCCESS);
+// case 'c': // Solaris: match by contract ID
+// break;
+ case 'd': // Solaris: change the delimiter
+ opt_delim = strdup (optarg);
+ break;
+ case 'f': // Solaris: match full process name (as in "ps -f")
+ opt_full = 1;
+ break;
+ case 'g': // Solaris: match pgrp
+ opt_pgrp = split_list (optarg, conv_pgrp);
+ if (opt_pgrp == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+// case 'i': // FreeBSD: ignore case. OpenBSD: withdrawn. See -I. This sucks.
+// if (opt_case)
+// usage (opt);
+// opt_case = REG_ICASE;
+// break;
+// case 'j': // FreeBSD: restricted to the given jail ID
+// break;
+ case 'l': // Solaris: long output format (pgrep only) Should require -f for beyond argv[0] maybe?
+ opt_long = 1;
+ break;
+ case 'n': // Solaris: match only the newest
+ if (opt_oldest|opt_negate|opt_newest)
+ usage (opt);
+ opt_newest = 1;
+ ++criteria_count;
+ break;
+ case 'o': // Solaris: match only the oldest
+ if (opt_oldest|opt_negate|opt_newest)
+ usage (opt);
+ opt_oldest = 1;
+ ++criteria_count;
+ break;
+ case 's': // Solaris: match by session ID -- zero means self
+ opt_sid = split_list (optarg, conv_sid);
+ if (opt_sid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 't': // Solaris: match by tty
+ opt_term = split_list (optarg, conv_str);
+ if (opt_term == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 'u': // Solaris: match by euid/egroup
+ opt_euid = split_list (optarg, conv_uid);
+ if (opt_euid == NULL)
+ usage (opt);
+ ++criteria_count;
+ break;
+ case 'v': // Solaris: as in grep, invert the matching (uh... applied after selection I think)
+ if (opt_oldest|opt_negate|opt_newest)
+ usage (opt);
+ opt_negate = 1;
+ break;
+ // OpenBSD -x, being broken, does a plain string
+ case 'x': // Solaris: use ^(regexp)$ in place of regexp (FreeBSD too)
+ opt_exact = 1;
+ break;
+// case 'z': // Solaris: match by zone ID
+// break;
+ case '?':
+ usage (opt);
+ break;
+ }
+ }
+
+ if(opt_lock && !opt_pidfile){
+ fprintf(stderr, "%s: -L without -F makes no sense\n",progname);
+ usage(0);
+ }
+
+ if(opt_pidfile){
+ opt_pid = read_pidfile();
+ if(!opt_pid){
+ fprintf(stderr, "%s: pidfile not valid\n",progname);
+ usage(0);
+ }
+ }
+
+ if (argc - optind == 1)
+ opt_pattern = argv[optind];
+ else if (argc - optind > 1)
+ usage (0);
+ else if (criteria_count == 0) {
+ fprintf (stderr, "%s: No matching criteria specified\n",
+ progname);
+ usage (0);
+ }
+}
+
+
+int main (int argc, char *argv[])
+{
+ union el *procs;
+ int num;
+
+ parse_opts (argc, argv);
+
+ procs = select_procs (&num);
+ if (i_am_pkill) {
+ int i;
+ for (i = 0; i < num; i++) {
+ if (kill (procs[i].num, opt_signal) != -1) continue;
+ if (errno==ESRCH) continue; // gone now, which is OK
+ fprintf (stderr, "pkill: %ld - %s\n",
+ procs[i].num, strerror (errno));
+ }
+ } else {
+ if (opt_long)
+ output_strlist(procs,num);
+ else
+ output_numlist(procs,num);
+ }
+ return !num; // exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE)
+}
--- /dev/null
+.so man1/pgrep.1
--- /dev/null
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for pmap.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan.
+.\"
+.TH PMAP 1 "October 26, 2002" "Linux" "Linux User's Manual"
+.SH NAME
+pmap \- report memory map of a process
+
+.SH SYNOPSIS
+.nf
+pmap [ -x | -d ] [ -q ] pids...
+pmap -V
+.fi
+
+.SH DESCRIPTION
+The pmap command reports the memory map of a process or processes.
+
+.SH "GENERAL OPTIONS"
+.TS
+l l l.
+-x extended Show the extended format.
+-d device Show the device format.
+-q quiet Do not display some header/footer lines.
+-V show version Displays version of program.
+.TE
+
+.SH "SEE ALSO"
+ps(1) pgrep(1)
+
+.SH STANDARDS
+No standards apply, but pmap looks an awful lot like a SunOS command.
+
+.SH AUTHOR
+Albert Cahalan <albert@users.sf.net> wrote pmap in 2002, and is the current
+maintainer of the procps collection. Please send bug reports
+to <procps-feedback@lists.sf.net>.
--- /dev/null
+/*
+ * Copyright 2002 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include "proc/readproc.h"
+#include "proc/version.h"
+#include "proc/escape.h"
+
+static void usage(void) NORETURN;
+static void usage(void){
+ fprintf(stderr,
+ "Usage: pmap [-x | -d] [-q] [-A low,high] pid...\n"
+ "-x show details\n"
+ "-d show offset and device number\n"
+ "-q quiet; less header/footer info\n"
+ "-V show the version number\n"
+ "-A limit results to the given range\n"
+ );
+ exit(1);
+}
+
+
+static unsigned KLONG range_low;
+static unsigned KLONG range_high = ~0ull;
+
+static int V_option;
+static int r_option; // ignored -- for SunOS compatibility
+static int x_option;
+static int d_option;
+static int q_option;
+
+static unsigned shm_minor = ~0u;
+
+static void discover_shm_minor(void){
+ void *addr;
+ int shmid;
+ char mapbuf[256];
+
+ if(!freopen("/proc/self/maps", "r", stdin)) return;
+
+ // create
+ shmid = shmget(IPC_PRIVATE, 42, IPC_CREAT | 0666);
+ if(shmid==-1) return; // failed; oh well
+ // attach
+ addr = shmat(shmid, NULL, SHM_RDONLY);
+ if(addr==(void*)-1) goto out_destroy;
+
+ while(fgets(mapbuf, sizeof mapbuf, stdin)){
+ char flags[32];
+ char *tmp; // to clean up unprintables
+ unsigned KLONG start, end;
+ unsigned long long file_offset, inode;
+ unsigned dev_major, dev_minor;
+ sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
+ tmp = strchr(mapbuf,'\n');
+ if(tmp) *tmp='\0';
+ tmp = mapbuf;
+ while(*tmp){
+ if(!isprint(*tmp)) *tmp='?';
+ tmp++;
+ }
+ if(start > (unsigned long)addr) continue;
+ if(dev_major) continue;
+ if(flags[3] != 's') continue;
+ if(strstr(mapbuf,"/SYSV")){
+ shm_minor = dev_minor;
+ break;
+ }
+ }
+
+ if(shmdt(addr)) perror("shmdt");
+
+out_destroy:
+ if(shmctl(shmid, IPC_RMID, NULL)) perror("IPC_RMID");
+
+ return;
+}
+
+
+static const char *mapping_name(proc_t *p, unsigned KLONG addr, unsigned KLONG len, const char *mapbuf, unsigned showpath, unsigned dev_major, unsigned dev_minor, unsigned long long inode){
+ const char *cp;
+
+ if(!dev_major && dev_minor==shm_minor && strstr(mapbuf,"/SYSV")){
+ static char shmbuf[64];
+ snprintf(shmbuf, sizeof shmbuf, " [ shmid=0x%Lx ]", inode);
+ return shmbuf;
+ }
+
+ cp = strrchr(mapbuf,'/');
+ if(cp){
+ if(showpath) return strchr(mapbuf,'/');
+ return cp[1] ? cp+1 : cp;
+ }
+
+ cp = strchr(mapbuf,'/');
+ if(cp){
+ if(showpath) return cp;
+ return strrchr(cp,'/') + 1; // it WILL succeed
+ }
+
+ cp = " [ anon ]";
+ if( (p->start_stack >= addr) && (p->start_stack <= addr+len) ) cp = " [ stack ]";
+ return cp;
+}
+
+static int one_proc(proc_t *p){
+ char buf[32];
+ char mapbuf[9600];
+ char cmdbuf[512];
+ unsigned long total_shared = 0ul;
+ unsigned long total_private_readonly = 0ul;
+ unsigned long total_private_writeable = 0ul;
+
+ // Overkill, but who knows what is proper? The "w" prog
+ // uses the tty width to determine this.
+ int maxcmd = 0xfffff;
+
+ sprintf(buf,"/proc/%u/maps",p->tgid);
+ if(!freopen(buf, "r", stdin)) return 1;
+
+ escape_command(cmdbuf, p, sizeof cmdbuf, &maxcmd, ESC_ARGS|ESC_BRACKETS);
+ printf("%u: %s\n", p->tgid, cmdbuf);
+
+ if(!q_option && (x_option|d_option)){
+ if(x_option){
+ if(sizeof(KLONG)==4) printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
+ else printf("Address Kbytes RSS Anon Locked Mode Mapping\n");
+ }
+ if(d_option){
+ if(sizeof(KLONG)==4) printf("Address Kbytes Mode Offset Device Mapping\n");
+ else printf("Address Kbytes Mode Offset Device Mapping\n");
+ }
+ }
+
+ while(fgets(mapbuf,sizeof mapbuf,stdin)){
+ char flags[32];
+ char *tmp; // to clean up unprintables
+ unsigned KLONG start, end, diff;
+ unsigned long long file_offset, inode;
+ unsigned dev_major, dev_minor;
+ sscanf(mapbuf,"%"KLF"x-%"KLF"x %31s %Lx %x:%x %Lu", &start, &end, flags, &file_offset, &dev_major, &dev_minor, &inode);
+
+ if(start > range_high)
+ break;
+ if(end < range_low)
+ continue;
+
+ tmp = strchr(mapbuf,'\n');
+ if(tmp) *tmp='\0';
+ tmp = mapbuf;
+ while(*tmp){
+ if(!isprint(*tmp)) *tmp='?';
+ tmp++;
+ }
+
+ diff = end-start;
+ if(flags[3]=='s') total_shared += diff;
+ if(flags[3]=='p'){
+ flags[3] = '-';
+ if(flags[1]=='w') total_private_writeable += diff;
+ else total_private_readonly += diff;
+ }
+
+ // format used by Solaris 9 and procps-3.2.0+
+ // an 'R' if swap not reserved (MAP_NORESERVE, SysV ISM shared mem, etc.)
+ flags[4] = '-';
+ flags[5] = '\0';
+
+ if(x_option){
+ const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
+ printf(
+ (sizeof(KLONG)==8)
+ ? "%016"KLF"x %7lu - - - %s %s\n"
+ : "%08lx %7lu - - - %s %s\n",
+ start,
+ (unsigned long)(diff>>10),
+ flags,
+ cp
+ );
+ }
+ if(d_option){
+ const char *cp = mapping_name(p, start, diff, mapbuf, 0, dev_major, dev_minor, inode);
+ printf(
+ (sizeof(KLONG)==8)
+ ? "%016"KLF"x %7lu %s %016Lx %03x:%05x %s\n"
+ : "%08lx %7lu %s %016Lx %03x:%05x %s\n",
+ start,
+ (unsigned long)(diff>>10),
+ flags,
+ file_offset,
+ dev_major, dev_minor,
+ cp
+ );
+ }
+ if(!x_option && !d_option){
+ const char *cp = mapping_name(p, start, diff, mapbuf, 1, dev_major, dev_minor, inode);
+ printf(
+ (sizeof(KLONG)==8)
+ ? "%016"KLF"x %6luK %s %s\n"
+ : "%08lx %6luK %s %s\n",
+ start,
+ (unsigned long)(diff>>10),
+ flags,
+ cp
+ );
+ }
+
+ }
+
+
+
+
+ if(!q_option){
+ if(x_option){
+ if(sizeof(KLONG)==8){
+ printf("---------------- ------ ------ ------ ------\n");
+ printf(
+ "total kB %15ld - - -\n",
+ (total_shared + total_private_writeable + total_private_readonly) >> 10
+ );
+ }else{
+ printf("-------- ------- ------- ------- -------\n");
+ printf(
+ "total kB %7ld - - -\n",
+ (total_shared + total_private_writeable + total_private_readonly) >> 10
+ );
+ }
+ }
+ if(d_option){
+ printf(
+ "mapped: %ldK writeable/private: %ldK shared: %ldK\n",
+ (total_shared + total_private_writeable + total_private_readonly) >> 10,
+ total_private_writeable >> 10,
+ total_shared >> 10
+ );
+ }
+ if(!x_option && !d_option){
+ if(sizeof(KLONG)==8) printf(" total %16ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
+ else printf(" total %8ldK\n", (total_shared + total_private_writeable + total_private_readonly) >> 10);
+ }
+ }
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[]){
+ unsigned *pidlist;
+ unsigned count = 0;
+ PROCTAB* PT;
+ proc_t p;
+ int ret = 0;
+
+ if(argc<2) usage();
+ pidlist = malloc(sizeof(unsigned)*argc); // a bit more than needed perhaps
+
+ while(*++argv){
+ if(!strcmp("--version",*argv)){
+ V_option++;
+ continue;
+ }
+ if(**argv=='-'){
+ char *walk = *argv;
+ if(!walk[1]) usage();
+ while(*++walk){
+ switch(*walk){
+ case 'V':
+ V_option++;
+ break;
+ case 'x':
+ x_option++;
+ break;
+ case 'r':
+ r_option++;
+ break;
+ case 'd':
+ d_option++;
+ break;
+ case 'q':
+ q_option++;
+ break;
+ case 'A':{
+ char *arg1;
+ if(walk[1]){
+ arg1 = walk+1;
+ walk += strlen(walk)-1;
+ }else{
+ arg1 = *++argv;
+ if(!arg1)
+ usage();
+ }
+ char *arg2 = strchr(arg1,',');
+ if(arg2)
+ *arg2 = '\0';
+ arg2 = arg2 ? arg2++ : arg1;
+
+ if(*arg1)
+ range_low = STRTOUKL(arg1,&arg1,16);
+ if(*arg2)
+ range_high = STRTOUKL(arg2,&arg2,16);
+ if(*arg1 || *arg2)
+ usage();
+ }
+ break;
+ case 'a': // Sun prints anon/swap reservations
+ case 'F': // Sun forces hostile ptrace-like grab
+ case 'l': // Sun shows unresolved dynamic names
+ case 'L': // Sun shows lgroup info
+ case 's': // Sun shows page sizes
+ case 'S': // Sun shows swap reservations
+ default:
+ usage();
+ }
+ }
+ }else{
+ char *walk = *argv;
+ char *endp;
+ unsigned long pid;
+ if(!strncmp("/proc/",walk,6)){
+ walk += 6;
+ // user allowed to do: pmap /proc/*
+ if(*walk<'0' || *walk>'9') continue;
+ }
+ if(*walk<'0' || *walk>'9') usage();
+ pid = strtoul(walk, &endp, 0);
+ if(pid<1ul || pid>0x7ffffffful || *endp) usage();
+ pidlist[count++] = pid;
+ }
+ }
+
+ if( (x_option|V_option|r_option|d_option|q_option) >> 1 ) usage(); // dupes
+ if(V_option){
+ if(count|x_option|r_option|d_option|q_option) usage();
+ fprintf(stdout, "pmap (%s)\n", procps_version);
+ return 0;
+ }
+ if(count<1) usage(); // no processes
+ if(d_option && x_option) usage();
+
+ discover_shm_minor();
+
+ pidlist[count] = 0; // old libproc interface is zero-terminated
+ PT = openproc(PROC_FILLSTAT|PROC_FILLARG|PROC_PID, pidlist);
+ while(readproc(PT, &p)){
+ ret |= one_proc(&p);
+ if(p.cmdline) free((void*)*p.cmdline);
+ count--;
+ }
+ closeproc(PT);
+
+ if(count) ret |= 42; // didn't find all processes asked for
+ return ret;
+}
--- /dev/null
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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
+\f
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com
+// Copyright 2002 Albert Cahalan
+//
+// This file is placed under the conditions of the GNU Library
+// General Public License, version 2, or any later version.
+// See file COPYING for information on distribution conditions.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "alloc.h"
+
+void *xcalloc(void *pointer, int size) {
+ void * ret;
+ if (pointer)
+ free(pointer);
+ if (!(ret = calloc(1, size))) {
+ fprintf(stderr, "xcalloc: allocation error, size = %d\n", size);
+ exit(1);
+ }
+ return ret;
+}
+
+void *xmalloc(unsigned int size) {
+ void *p;
+
+ if (size == 0)
+ ++size;
+ p = malloc(size);
+ if (!p) {
+ fprintf(stderr, "xmalloc: malloc(%d) failed", size);
+ perror(NULL);
+ exit(1);
+ }
+ return(p);
+}
+
+void *xrealloc(void *oldp, unsigned int size) {
+ void *p;
+
+ if (size == 0)
+ ++size;
+ p = realloc(oldp, size);
+ if (!p) {
+ fprintf(stderr, "xrealloc: realloc(%d) failed", size);
+ perror(NULL);
+ exit(1);
+ }
+ return(p);
+}
--- /dev/null
+#ifndef PROCPS_PROC_ALLOC_H
+#define PROCPS_PROC_ALLOC_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+extern void *xrealloc(void *oldp, unsigned int size) MALLOC;
+extern void *xmalloc(unsigned int size) MALLOC;
+extern void *xcalloc(void *pointer, int size) MALLOC;
+
+EXTERN_C_END
+
+#endif
--- /dev/null
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "version.h"
+#include "devname.h"
+
+// This is the buffer size for a tty name. Any path is legal,
+// which makes PAGE_SIZE appropriate (see kernel source), but
+// that is only 99% portable and utmp only holds 32 anyway.
+// We need at least 20 for guess_name().
+#define TTY_NAME_SIZE 128
+
+/* Who uses what:
+ *
+ * tty_to_dev w (there is a fancy version in ps)
+ * dev_to_tty top, ps
+ */
+
+#if 0
+#include <sys/sysmacros.h>
+#define MAJOR_OF(d) ((unsigned)major(d))
+#define MINOR_OF(d) ((unsigned)minor(d))
+#else
+#define MAJOR_OF(d) ( ((unsigned)(d)>>8u) & 0xfffu )
+#define MINOR_OF(d) ( ((unsigned)(d)&0xffu) | (((unsigned)(d)&0xfff00000u)>>12u) )
+#undef major
+#undef minor
+#define major <-- do not use -->
+#define minor <-- do not use -->
+#endif
+
+typedef struct tty_map_node {
+ struct tty_map_node *next;
+ unsigned short devfs_type; // bool
+ unsigned short major_number;
+ unsigned minor_first;
+ unsigned minor_last;
+ char name[16];
+} tty_map_node;
+
+static tty_map_node *tty_map = NULL;
+
+/* Load /proc/tty/drivers for device name mapping use. */
+static void load_drivers(void){
+ char buf[10000];
+ char *p;
+ int fd;
+ int bytes;
+ fd = open("/proc/tty/drivers",O_RDONLY);
+ if(fd == -1) goto fail;
+ bytes = read(fd, buf, sizeof(buf) - 1);
+ if(bytes == -1) goto fail;
+ buf[bytes] = '\0';
+ p = buf;
+ while(( p = strstr(p, " /dev/") )){ // " /dev/" is the second column
+ tty_map_node *tmn;
+ int len;
+ char *end;
+ p += 6;
+ end = strchr(p, ' ');
+ if(!end) continue;
+ len = end - p;
+ tmn = calloc(1, sizeof(tty_map_node));
+ tmn->next = tty_map;
+ tty_map = tmn;
+ /* if we have a devfs type name such as /dev/tts/%d then strip the %d but
+ keep a flag. */
+ if(len >= 3 && !strncmp(end - 2, "%d", 2)){
+ len -= 2;
+ tmn->devfs_type = 1;
+ }
+ if(len >= sizeof tmn->name)
+ len = sizeof tmn->name - 1; // mangle it to avoid overflow
+ memcpy(tmn->name, p, len);
+ p = end; /* set p to point past the %d as well if there is one */
+ while(*p == ' ') p++;
+ tmn->major_number = atoi(p);
+ p += strspn(p, "0123456789");
+ while(*p == ' ') p++;
+ switch(sscanf(p, "%u-%u", &tmn->minor_first, &tmn->minor_last)){
+ default:
+ /* Can't finish parsing this line so we remove it from the list */
+ tty_map = tty_map->next;
+ free(tmn);
+ break;
+ case 1:
+ tmn->minor_last = tmn->minor_first;
+ break;
+ case 2:
+ break;
+ }
+ }
+fail:
+ if(fd != -1) close(fd);
+ if(!tty_map) tty_map = (tty_map_node *)-1;
+}
+
+/* Try to guess the device name from /proc/tty/drivers info. */
+static int driver_name(char *restrict const buf, unsigned maj, unsigned min){
+ struct stat sbuf;
+ tty_map_node *tmn;
+ if(!tty_map) load_drivers();
+ if(tty_map == (tty_map_node *)-1) return 0;
+ tmn = tty_map;
+ for(;;){
+ if(!tmn) return 0;
+ if(tmn->major_number == maj && tmn->minor_first <= min && tmn->minor_last >= min) break;
+ tmn = tmn->next;
+ }
+ sprintf(buf, "/dev/%s%d", tmn->name, min); /* like "/dev/ttyZZ255" */
+ if(stat(buf, &sbuf) < 0){
+ if(tmn->devfs_type) return 0;
+ sprintf(buf, "/dev/%s", tmn->name); /* like "/dev/ttyZZ255" */
+ if(stat(buf, &sbuf) < 0) return 0;
+ }
+ if(min != MINOR_OF(sbuf.st_rdev)) return 0;
+ if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
+ return 1;
+}
+
+// major 204 is a mess -- "Low-density serial ports"
+static const char low_density_names[][6] = {
+"LU0", "LU1", "LU2", "LU3",
+"FB0",
+"SA0", "SA1", "SA2",
+"SC0", "SC1", "SC2", "SC3",
+"FW0", "FW1", "FW2", "FW3",
+"AM0", "AM1", "AM2", "AM3", "AM4", "AM5", "AM6", "AM7",
+"AM8", "AM9", "AM10", "AM11", "AM12", "AM13", "AM14", "AM15",
+"DB0", "DB1", "DB2", "DB3", "DB4", "DB5", "DB6", "DB7",
+"SG0",
+"SMX0", "SMX1", "SMX2",
+"MM0", "MM1",
+"CPM0", "CPM1", "CPM2", "CPM3", /* "CPM4", "CPM5", */ // bad allocation?
+"IOC0", "IOC1", "IOC2", "IOC3", "IOC4", "IOC5", "IOC6", "IOC7",
+"IOC8", "IOC9", "IOC10", "IOC11", "IOC12", "IOC13", "IOC14", "IOC15",
+"IOC16", "IOC17", "IOC18", "IOC19", "IOC20", "IOC21", "IOC22", "IOC23",
+"IOC24", "IOC25", "IOC26", "IOC27", "IOC28", "IOC29", "IOC30", "IOC31",
+"VR0", "VR1",
+"IOC84", "IOC85", "IOC86", "IOC87", "IOC88", "IOC89", "IOC90", "IOC91",
+"IOC92", "IOC93", "IOC94", "IOC95", "IOC96", "IOC97", "IOC98", "IOC99",
+"IOC100", "IOC101", "IOC102", "IOC103", "IOC104", "IOC105", "IOC106", "IOC107",
+"IOC108", "IOC109", "IOC110", "IOC111", "IOC112", "IOC113", "IOC114", "IOC115",
+"SIOC0", "SIOC1", "SIOC2", "SIOC3", "SIOC4", "SIOC5", "SIOC6", "SIOC7",
+"SIOC8", "SIOC9", "SIOC10", "SIOC11", "SIOC12", "SIOC13", "SIOC14", "SIOC15",
+"SIOC16", "SIOC17", "SIOC18", "SIOC19", "SIOC20", "SIOC21", "SIOC22", "SIOC23",
+"SIOC24", "SIOC25", "SIOC26", "SIOC27", "SIOC28", "SIOC29", "SIOC30", "SIOC31",
+"PSC0", "PSC1", "PSC2", "PSC3", "PSC4", "PSC5",
+"AT0", "AT1", "AT2", "AT3", "AT4", "AT5", "AT6", "AT7",
+"AT8", "AT9", "AT10", "AT11", "AT12", "AT13", "AT14", "AT15",
+"NX0", "NX1", "NX2", "NX3", "NX4", "NX5", "NX6", "NX7",
+"NX8", "NX9", "NX10", "NX11", "NX12", "NX13", "NX14", "NX15",
+"J0", // minor is 186
+"UL0","UL1","UL2","UL3",
+"xvc0", // FAIL -- "/dev/xvc0" lacks "tty" prefix
+"PZ0","PZ1","PZ2","PZ3",
+"TX0","TX1","TX2","TX3","TX4","TX5","TX6","TX7",
+"SC0","SC1","SC2","SC3",
+"MAX0","MAX1","MAX2","MAX3",
+};
+
+#if 0
+// test code
+#include <stdio.h>
+#define AS(x) (sizeof(x)/sizeof((x)[0]))
+int main(int argc, char *argv[]){
+ int i = 0;
+ while(i<AS(low_density_names)){
+ printf("%3d = /dev/tty%.*s\n",i,sizeof low_density_names[i],low_density_names[i]);
+ i++;
+ }
+ return 0;
+}
+#endif
+
+/* Try to guess the device name (useful until /proc/PID/tty is added) */
+static int guess_name(char *restrict const buf, unsigned maj, unsigned min){
+ struct stat sbuf;
+ int t0, t1;
+ unsigned tmpmin = min;
+
+ switch(maj){
+ case 3: /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
+ if(tmpmin > 255) return 0; // should never happen; array index protection
+ t0 = "pqrstuvwxyzabcde"[tmpmin>>4];
+ t1 = "0123456789abcdef"[tmpmin&0x0f];
+ sprintf(buf, "/dev/tty%c%c", t0, t1);
+ break;
+ case 4:
+ if(min<64){
+ sprintf(buf, "/dev/tty%d", min);
+ break;
+ }
+ sprintf(buf, "/dev/ttyS%d", min-64);
+ break;
+ case 11: sprintf(buf, "/dev/ttyB%d", min); break;
+ case 17: sprintf(buf, "/dev/ttyH%d", min); break;
+ case 19: sprintf(buf, "/dev/ttyC%d", min); break;
+ case 22: sprintf(buf, "/dev/ttyD%d", min); break; /* devices.txt */
+ case 23: sprintf(buf, "/dev/ttyD%d", min); break; /* driver code */
+ case 24: sprintf(buf, "/dev/ttyE%d", min); break;
+ case 32: sprintf(buf, "/dev/ttyX%d", min); break;
+ case 43: sprintf(buf, "/dev/ttyI%d", min); break;
+ case 46: sprintf(buf, "/dev/ttyR%d", min); break;
+ case 48: sprintf(buf, "/dev/ttyL%d", min); break;
+ case 57: sprintf(buf, "/dev/ttyP%d", min); break;
+ case 71: sprintf(buf, "/dev/ttyF%d", min); break;
+ case 75: sprintf(buf, "/dev/ttyW%d", min); break;
+ case 78: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
+ case 105: sprintf(buf, "/dev/ttyV%d", min); break;
+ case 112: sprintf(buf, "/dev/ttyM%d", min); break; /* conflict */
+ /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
+ case 136 ... 143: sprintf(buf, "/dev/pts/%d", min+(maj-136)*256); break;
+ case 148: sprintf(buf, "/dev/ttyT%d", min); break;
+ case 154: sprintf(buf, "/dev/ttySR%d", min); break;
+ case 156: sprintf(buf, "/dev/ttySR%d", min+256); break;
+ case 164: sprintf(buf, "/dev/ttyCH%d", min); break;
+ case 166: sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */
+ case 172: sprintf(buf, "/dev/ttyMX%d", min); break;
+ case 174: sprintf(buf, "/dev/ttySI%d", min); break;
+ case 188: sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */
+ case 204:
+ if(min >= sizeof low_density_names / sizeof low_density_names[0]) return 0;
+ memcpy(buf,"/dev/tty",8);
+ memcpy(buf+8, low_density_names[min], sizeof low_density_names[0]);
+ buf[8 + sizeof low_density_names[0]] = '\0';
+// snprintf(buf, 9 + sizeof low_density_names[0], "/dev/tty%.*s", sizeof low_density_names[0], low_density_names[min]);
+ break;
+ case 208: sprintf(buf, "/dev/ttyU%d", min); break;
+ case 216: sprintf(buf, "/dev/ttyUB%d", min); break; // "/dev/rfcomm%d" now?
+ case 224: sprintf(buf, "/dev/ttyY%d", min); break;
+ case 227: sprintf(buf, "/dev/3270/tty%d", min); break; /* bummer, HUGE */
+ case 229: sprintf(buf, "/dev/iseries/vtty%d", min); break; /* bummer, HUGE */
+ case 256: sprintf(buf, "/dev/ttyEQ%d", min); break;
+ default: return 0;
+ }
+ if(stat(buf, &sbuf) < 0) return 0;
+ if(min != MINOR_OF(sbuf.st_rdev)) return 0;
+ if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
+ return 1;
+}
+
+/* Linux 2.2 can give us filenames that might be correct.
+ * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
+ * and in /proc/PID/fd/255 (used by bash to remember the tty).
+ */
+static int link_name(char *restrict const buf, unsigned maj, unsigned min, int pid, const char *restrict name){
+ struct stat sbuf;
+ char path[32];
+ int count;
+ sprintf(path, "/proc/%d/%s", pid, name); /* often permission denied */
+ count = readlink(path,buf,TTY_NAME_SIZE-1);
+ if(count == -1) return 0;
+ buf[count] = '\0';
+ if(stat(buf, &sbuf) < 0) return 0;
+ if(min != MINOR_OF(sbuf.st_rdev)) return 0;
+ if(maj != MAJOR_OF(sbuf.st_rdev)) return 0;
+ return 1;
+}
+
+/* number --> name */
+unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) {
+ static char buf[TTY_NAME_SIZE];
+ char *restrict tmp = buf;
+ unsigned dev = dev_t_dev;
+ unsigned i = 0;
+ int c;
+ if(dev == 0u) goto no_tty;
+ if(linux_version_code > LINUX_VERSION(2, 7, 0)){ // not likely to make 2.6.xx
+ if(link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "tty" )) goto abbrev;
+ }
+ if(driver_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
+ if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/2" )) goto abbrev;
+ if( guess_name(tmp, MAJOR_OF(dev), MINOR_OF(dev) )) goto abbrev;
+ if( link_name(tmp, MAJOR_OF(dev), MINOR_OF(dev), pid, "fd/255")) goto abbrev;
+ // fall through if unable to find a device file
+no_tty:
+ strcpy(ret, "?");
+ return 1;
+abbrev:
+ if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5;
+ if((flags&ABBREV_TTY) && !strncmp(tmp,"tty", 3) && tmp[3]) tmp += 3;
+ if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4;
+ /* gotta check before we chop or we may chop someone else's memory */
+ if(chop + (unsigned long)(tmp-buf) <= sizeof buf)
+ tmp[chop] = '\0';
+ /* replace non-ASCII characters with '?' and return the number of chars */
+ for(;;){
+ c = *tmp;
+ tmp++;
+ if(!c) break;
+ i++;
+ if(c<=' ') c = '?';
+ if(c>126) c = '?';
+ *ret = c;
+ ret++;
+ }
+ *ret = '\0';
+ return i;
+}
+
+/* name --> number */
+int tty_to_dev(const char *restrict const name) {
+ struct stat sbuf;
+ static char buf[32];
+ if(name[0]=='/' && stat(name, &sbuf) >= 0) return sbuf.st_rdev;
+ snprintf(buf,32,"/dev/%s",name);
+ if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+ snprintf(buf,32,"/dev/tty%s",name);
+ if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+ snprintf(buf,32,"/dev/pts/%s",name);
+ if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+ return -1;
+}
--- /dev/null
+#ifndef PROC_DEVNAME_H
+#define PROC_DEVNAME_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+#define ABBREV_DEV 1 /* remove /dev/ */
+#define ABBREV_TTY 2 /* remove tty */
+#define ABBREV_PTS 4 /* remove pts/ */
+
+extern unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags);
+
+extern int tty_to_dev(const char *restrict const name);
+
+EXTERN_C_END
+#endif
--- /dev/null
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include "procps.h"
+#include "escape.h"
+#include "readproc.h"
+
+#if (__GNU_LIBRARY__ >= 6)
+# include <wchar.h>
+# include <wctype.h>
+# include <stdlib.h> /* MB_CUR_MAX */
+# include <ctype.h>
+# include <langinfo.h>
+#endif
+
+#if (__GNU_LIBRARY__ >= 6)
+static int escape_str_utf8(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){
+ int my_cells = 0;
+ int my_bytes = 0;
+ mbstate_t s;
+
+ memset(&s, 0, sizeof (s));
+
+ for(;;) {
+ wchar_t wc;
+ int len = 0;
+
+ if(my_cells >= *maxcells || my_bytes+1 >= bufsize)
+ break;
+
+ if (!(len = mbrtowc (&wc, src, MB_CUR_MAX, &s)))
+ /* 'str' contains \0 */
+ break;
+
+ if (len < 0) {
+ /* invalid multibyte sequence -- zeroize state */
+ memset (&s, 0, sizeof (s));
+ *(dst++) = '?';
+ src++;
+ my_cells++;
+ my_bytes++;
+
+ } else if (len==1) {
+ /* non-multibyte */
+ *(dst++) = isprint(*src) ? *src : '?';
+ src++;
+ my_cells++;
+ my_bytes++;
+
+ } else if (!iswprint(wc)) {
+ /* multibyte - no printable */
+ *(dst++) = '?';
+ src+=len;
+ my_cells++;
+ my_bytes++;
+
+ } else {
+ /* multibyte - printable */
+ int wlen = wcwidth(wc);
+
+ if (wlen==0) {
+ // invisible multibyte -- we don't ignore it, because some terminal
+ // interpret it wrong and more safe is replace it with '?'
+ *(dst++) = '?';
+ src+=len;
+ my_cells++;
+ my_bytes++;
+ } else {
+ // multibyte - printable
+ // Got space?
+ if (my_cells+wlen > *maxcells || my_bytes+1+len >= bufsize) break;
+ // 0x9b is control byte for some terminals
+ if (memchr(src, 0x9B, len)) {
+ // unsafe multibyte
+ *(dst++) = '?';
+ src+=len;
+ my_cells++;
+ my_bytes++;
+ } else {
+ // safe multibyte
+ memcpy(dst, src, len);
+ my_cells += wlen;
+ dst += len;
+ my_bytes += len;
+ src += len;
+ }
+ }
+ }
+ //fprintf(stdout, "cells: %d\n", my_cells);
+ }
+ *(dst++) = '\0';
+
+ // fprintf(stderr, "maxcells: %d, my_cells; %d\n", *maxcells, my_cells);
+
+ *maxcells -= my_cells;
+ return my_bytes; // bytes of text, excluding the NUL
+}
+
+#endif /* __GNU_LIBRARY__ */
+
+/* sanitize a string via one-way mangle */
+int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells){
+ unsigned char c;
+ int my_cells = 0;
+ int my_bytes = 0;
+ const char codes[] =
+ "Z-------------------------------"
+ "********************************"
+ "********************************"
+ "*******************************-"
+ "--------------------------------"
+ "********************************"
+ "********************************"
+ "********************************";
+
+#if (__GNU_LIBRARY__ >= 6)
+ static int utf_init=0;
+
+ if(utf_init==0){
+ /* first call -- check if UTF stuff is usable */
+ char *enc = nl_langinfo(CODESET);
+ utf_init = enc && strcasecmp(enc, "UTF-8")==0 ? 1 : -1;
+ }
+ if (utf_init==1)
+ /* UTF8 locales */
+ return escape_str_utf8(dst, src, bufsize, maxcells);
+#endif
+
+ if(bufsize > *maxcells+1) bufsize=*maxcells+1; // FIXME: assumes 8-bit locale
+
+ for(;;){
+ if(my_cells >= *maxcells || my_bytes+1 >= bufsize)
+ break;
+ c = (unsigned char) *(src++);
+ if(!c) break;
+ if(codes[c]=='-') c='?';
+ my_cells++;
+ my_bytes++;
+ *(dst++) = c;
+ }
+ *(dst++) = '\0';
+
+ *maxcells -= my_cells;
+ return my_bytes; // bytes of text, excluding the NUL
+}
+
+/////////////////////////////////////////////////
+
+// escape an argv or environment string array
+//
+// bytes arg means sizeof(buf)
+int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t bytes, int *cells){
+ size_t i = 0;
+
+ for(;;){
+ i += escape_str(dst+i, *src, bytes-i, cells);
+ if(bytes-i < 3) break; // need room for space, a character, and the NUL
+ src++;
+ if(!*src) break; // need something to print
+ if (*cells<=1) break; // need room for printed size of text
+ dst[i++] = ' ';
+ --*cells;
+ }
+ return i; // bytes, excluding the NUL
+}
+
+///////////////////////////////////////////////////
+
+int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags){
+ int overhead = 0;
+ int end = 0;
+
+ if(flags & ESC_ARGS){
+ const char **lc = (const char**)pp->cmdline;
+ if(lc && *lc) return escape_strlist(outbuf, lc, bytes, cells);
+ }
+ if(flags & ESC_BRACKETS){
+ overhead += 2;
+ }
+ if(flags & ESC_DEFUNCT){
+ if(pp->state=='Z') overhead += 10; // chars in " <defunct>"
+ else flags &= ~ESC_DEFUNCT;
+ }
+ if(overhead + 1 >= *cells){ // if no room for even one byte of the command name
+ // you'd damn well better have _some_ space
+// outbuf[0] = '-'; // Oct23
+ outbuf[1] = '\0';
+ return 1;
+ }
+ if(flags & ESC_BRACKETS){
+ outbuf[end++] = '[';
+ }
+ *cells -= overhead;
+ end += escape_str(outbuf+end, pp->cmd, bytes-overhead, cells);
+
+ // Hmmm, do we want "[foo] <defunct>" or "[foo <defunct>]"?
+ if(flags & ESC_BRACKETS){
+ outbuf[end++] = ']';
+ }
+ if(flags & ESC_DEFUNCT){
+ memcpy(outbuf+end, " <defunct>", 10);
+ end += 10;
+ }
+ outbuf[end] = '\0';
+ return end; // bytes, not including the NUL
+}
--- /dev/null
+#ifndef PROCPS_PROC_ESCAPE_H
+#define PROCPS_PROC_ESCAPE_H
+
+//#include <stdio.h>
+#include <sys/types.h>
+#include "procps.h"
+#include "readproc.h"
+
+EXTERN_C_BEGIN
+
+#define ESC_STRETCH 1 // since we mangle to '?' this is 1 (would be 4 for octal escapes)
+
+#define ESC_ARGS 0x1 // try to use cmdline instead of cmd
+#define ESC_BRACKETS 0x2 // if using cmd, put '[' and ']' around it
+#define ESC_DEFUNCT 0x4 // mark zombies with " <defunct>"
+
+extern int escape_strlist(char *restrict dst, const char *restrict const *restrict src, size_t n, int *cells);
+extern int escape_str(char *restrict dst, const char *restrict src, int bufsize, int *maxcells);
+extern int escape_command(char *restrict const outbuf, const proc_t *restrict const pp, int bytes, int *cells, unsigned flags);
+
+EXTERN_C_END
+#endif
--- /dev/null
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include "procps.h"
+#include "version.h"
+#include "sysinfo.h" /* smp_num_cpus */
+#include "wchan.h" // to verify prototypes
+
+#define KSYMS_FILENAME "/proc/ksyms"
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/would/be/nice/to/have/this/file"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked"
+#define linux_version_code 131598 /* ? */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.12"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12"
+#define linux_version_code 131852 /* 2.3.12 */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS"
+#define linux_version_code 131858 /* 2.3.18ac8 */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS"
+#define linux_version_code 131858 /* 2.3.18ac8 */
+#define smp_num_cpus 2
+#endif
+
+/* These are the symbol types, with relative popularity:
+ * ? w machine type junk for Alpha -- odd syntax
+ * ? S not for i386
+ * 4 W not for i386
+ * 60 R
+ * 100 A
+ * 125 r
+ * 363 s not for i386
+ * 858 B
+ * 905 g generated by modutils?
+ * 929 G generated by modutils?
+ * 1301 b
+ * 2750 D
+ * 4481 d
+ * 11417 ?
+ * 13666 t
+ * 15442 T
+ *
+ * For i386, that is: "RArBbDd?tT"
+ */
+
+#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS"
+
+/*
+ * '?' is a symbol type
+ * '.' is part of a name (versioning?)
+ * "\t[]" are for the module name in /proc/ksyms
+ */
+#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+/* System.map lines look like:
+ * hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n
+ *
+ * Alpha systems can start with a few lines that have the address replaced
+ * by space padding and a 'w' for the type. For those lines, the last space
+ * is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv
+ * (just one of those, always with a "_mv", then the newline)
+ *
+ * The /proc/ksyms lines are like System.map lines w/o the symbol type char.
+ * When odd features are used, the name part contains:
+ * "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}"
+ * It is likely that more crap will be added...
+ */
+
+typedef struct symb {
+ unsigned KLONG addr;
+ const char *name;
+} symb;
+
+/* These mostly rely on POSIX to make them zero. */
+
+static symb hashtable[256];
+
+static char *sysmap_data;
+static unsigned sysmap_room;
+static symb *sysmap_index;
+static unsigned sysmap_count;
+
+static char *ksyms_data;
+static unsigned ksyms_room = 4096;
+static symb *ksyms_index;
+static unsigned ksyms_count;
+static unsigned idx_room;
+
+/*********************************/
+
+/* Kill this: _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$
+ * We kill: (_R[^A-Z]*[0-9a-f]{8,})+$
+ *
+ * The loop should almost never be taken, but it has to be there.
+ * It gets rid of anything that _looks_ like a version code, even
+ * if a real version code has already been found. This is because
+ * the inability to perfectly recognize a version code may lead to
+ * symbol mangling, which in turn leads to mismatches between the
+ * /proc/ksyms and System.map data files.
+ */
+#if 0
+static char *chop_version(char *arg){
+ char *cp;
+ cp = strchr(arg,'\t');
+ if(cp) *cp = '\0'; /* kill trailing module name first */
+ for(;;){
+ char *p;
+ int len = 0;
+ cp = strrchr(arg, 'R');
+ if(!cp || cp<=arg+1 || cp[-1]!='_') break;
+ for(p=cp; *++p; ){
+ switch(*p){
+ default:
+ goto out;
+ case '0' ... '9':
+ case 'a' ... 'f':
+ len++;
+ continue;
+ case 'g' ... 'z':
+ case '_':
+ len=0;
+ continue;
+ }
+ }
+ if(len<8) break;
+ cp[-1] = '\0';
+ }
+out:
+ if(*arg=='G'){
+ int len = strlen(arg);
+ while( len>8 && !memcmp(arg,"GPLONLY_",8) ){
+ arg += 8;
+ len -= 8;
+ }
+ }
+ return arg;
+}
+#endif
+static char *chop_version(char *arg){
+ char *cp;
+ cp = strchr(arg,'\t');
+ if(cp) *cp = '\0'; /* kill trailing module name first */
+ for(;;){
+ int len;
+ cp = strrchr(arg, 'R');
+ if(!cp || cp<=arg+1 || cp[-1]!='_') break;
+ len=strlen(cp);
+ if(len<9) break;
+ if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break;
+ if(strspn(cp+len-8,"0123456789abcdef")!=8) break;
+ cp[-1] = '\0';
+ }
+ if(*arg=='G'){
+ int len = strlen(arg);
+ while( len>8 && !memcmp(arg,"GPLONLY_",8) ){
+ arg += 8;
+ len -= 8;
+ }
+ }
+ return arg;
+}
+
+/***********************************/
+
+static const symb *search(unsigned KLONG address, symb *idx, unsigned count){
+ unsigned left;
+ unsigned mid;
+ unsigned right;
+ if(!idx) return NULL; /* maybe not allocated */
+ if(address < idx[0].addr) return NULL;
+ if(address >= idx[count-1].addr) return idx+count-1;
+ left = 0;
+ right = count-1;
+ for(;;){
+ mid = (left + right) / 2;
+ if(address >= idx[mid].addr) left = mid;
+ if(address <= idx[mid].addr) right = mid;
+ if(right-left <= 1) break;
+ }
+ if(address == idx[right].addr) return idx+right;
+ return idx+left;
+}
+
+/*********************************/
+
+/* allocate if needed, read, and return buffer size */
+static void read_file(const char *restrict filename, char **bufp, unsigned *restrict roomp) {
+ int fd = 0;
+ ssize_t done;
+ char *buf = *bufp;
+ ssize_t total = 0;
+ unsigned room = *roomp;
+
+ if(!room) goto hell; /* failed before */
+ if(!buf) buf = malloc(room);
+ if(!buf) goto hell;
+open_again:
+ fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if(fd<0){
+ switch(errno){
+ case EINTR: goto open_again;
+ default: _exit(101);
+ case EACCES: /* somebody screwing around? */
+ /* FIXME: set a flag to disable symbol lookup? */
+ case ENOENT:; /* no module support */
+ }
+ goto hell;
+ }
+ for(;;){
+ done = read(fd, buf+total, room-total-1);
+ if(done==0) break; /* nothing left */
+ if(done==-1){
+ if(errno==EINTR) continue; /* try again */
+ perror("");
+ goto hell;
+ }
+ if(done==(ssize_t)room-total-1){
+ char *tmp;
+ total += done;
+ /* more to go, but no room in buffer */
+ room *= 2;
+ tmp = realloc(buf, room);
+ if(!tmp) goto hell;
+ buf = tmp;
+ continue;
+ }
+ if(done>0 && done<(ssize_t)room-total-1){
+ total += done;
+ continue; /* OK, we read some. Go do more. */
+ }
+ fprintf(stderr,"%ld can't happen\n", (long)done);
+ /* FIXME: memory leak */
+ _exit(42);
+ }
+ buf[total] = '\0'; // parse_ksyms() expects NUL-terminated file
+ *bufp = buf;
+ *roomp = room;
+ close(fd);
+ return;
+hell:
+ if(buf) free(buf);
+ *bufp = NULL;
+ *roomp = 0; /* this function will never work again */
+ total = 0;
+ if(fd>0) close(fd);
+ return;
+}
+
+/*********************************/
+
+static int parse_ksyms(void) {
+ char *endp;
+ if(!ksyms_room || !ksyms_data) goto quiet_goodbye;
+ endp = ksyms_data;
+ ksyms_count = 0;
+ if(idx_room) goto bypass; /* some space already allocated */
+ idx_room = 512;
+ for(;;){
+ void *vp;
+ idx_room *= 2;
+ vp = realloc(ksyms_index, sizeof(symb)*idx_room);
+ if(!vp) goto bad_alloc;
+ ksyms_index = vp;
+bypass:
+ for(;;){
+ char *saved;
+ if(!*endp) return 1;
+ saved = endp;
+ ksyms_index[ksyms_count].addr = STRTOUKL(endp, &endp, 16);
+ if(endp==saved || *endp != ' ') goto bad_parse;
+ endp++;
+ saved = endp;
+ endp = strchr(endp,'\n');
+ if(!endp) goto bad_parse; /* no newline */
+ *endp = '\0';
+ ksyms_index[ksyms_count].name = chop_version(saved);
+ ++endp;
+ if(++ksyms_count >= idx_room) break; /* need more space */
+ }
+ }
+
+ if(0){
+bad_alloc:
+ fprintf(stderr, "Warning: not enough memory available\n");
+ }
+ if(0){
+bad_parse:
+ fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n");
+ }
+quiet_goodbye:
+ idx_room = 0;
+ if(ksyms_data) free(ksyms_data) , ksyms_data = NULL;
+ ksyms_room = 0;
+ if(ksyms_index) free(ksyms_index) , ksyms_index = NULL;
+ ksyms_count = 0;
+ return 0;
+}
+
+/*********************************/
+
+#define VCNT 16
+
+static int sysmap_mmap(const char *restrict const filename, message_fn message) {
+ struct stat sbuf;
+ char *endp;
+ int fd;
+ char Version[32];
+ fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if(fd<0) return 0;
+ if(fstat(fd, &sbuf) < 0) goto bad_open;
+ if(!S_ISREG(sbuf.st_mode)) goto bad_open;
+ if(sbuf.st_size < 5000) goto bad_open; /* if way too small */
+ /* Would be shared read-only, but we want '\0' after each name. */
+ endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ sysmap_data = endp;
+ while(*endp==' '){ /* damn Alpha machine types */
+ if(strncmp(endp," w ", 19)) goto bad_parse;
+ endp += 19;
+ endp = strchr(endp,'\n');
+ if(!endp) goto bad_parse; /* no newline */
+ if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse;
+ endp++;
+ }
+ if(sysmap_data == (caddr_t) -1) goto bad_open;
+ close(fd);
+ fd = -1;
+ sprintf(Version, "Version_%d", linux_version_code);
+ sysmap_room = 512;
+ for(;;){
+ void *vp;
+ sysmap_room *= 2;
+ vp = realloc(sysmap_index, sizeof(symb)*sysmap_room);
+ if(!vp) goto bad_alloc;
+ sysmap_index = vp;
+ for(;;){
+ char *vstart;
+ if(endp - sysmap_data >= sbuf.st_size){ /* if we reached the end */
+ int i = VCNT; /* check VCNT times to verify this file */
+ if(*Version) goto bad_version;
+ if(!ksyms_index) return 1; /* if can not verify, assume success */
+ while(i--){
+#if 1
+ const symb *findme;
+ const symb *map_symb;
+ /* Choose VCNT entries from /proc/ksyms to test */
+ findme = ksyms_index + (ksyms_count*i/VCNT);
+ /* Search for them in the System.map */
+ map_symb = search(findme->addr, sysmap_index, sysmap_count);
+ if(map_symb){
+ if(map_symb->addr != findme->addr) continue;
+ /* backup to first matching address */
+ while (map_symb != sysmap_index){
+ if (map_symb->addr != (map_symb-1)->addr) break;
+ map_symb--;
+ }
+ /* search for name in symbols with same address */
+ while (map_symb != (sysmap_index+sysmap_count)){
+ if (map_symb->addr != findme->addr) break;
+ if (!strcmp(map_symb->name,findme->name)) goto good_match;
+ map_symb++;
+ }
+ map_symb--; /* backup to last symbol with matching address */
+ message("{%s} {%s}\n",map_symb->name,findme->name);
+ goto bad_match;
+ }
+good_match:;
+#endif
+ }
+ return 1; /* success */
+ }
+ sysmap_index[sysmap_count].addr = STRTOUKL(endp, &endp, 16);
+ if(*endp != ' ') goto bad_parse;
+ endp++;
+ if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse;
+ endp++;
+ if(*endp != ' ') goto bad_parse;
+ endp++;
+ vstart = endp;
+ endp = strchr(endp,'\n');
+ if(!endp) goto bad_parse; /* no newline */
+ *endp = '\0';
+ ++endp;
+ vstart = chop_version(vstart);
+ sysmap_index[sysmap_count].name = vstart;
+ if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0';
+ if(++sysmap_count >= sysmap_room) break; /* need more space */
+ }
+ }
+
+ if(0){
+bad_match:
+ message("Warning: %s does not match kernel data.\n", filename);
+ }
+ if(0){
+bad_version:
+ message("Warning: %s has an incorrect kernel version.\n", filename);
+ }
+ if(0){
+bad_alloc:
+ message("Warning: not enough memory available\n");
+ }
+ if(0){
+bad_parse:
+ message("Warning: %s not parseable as a System.map\n", filename);
+ }
+ if(0){
+bad_open:
+ message("Warning: %s could not be opened as a System.map\n", filename);
+ }
+
+ sysmap_room=0;
+ sysmap_count=0;
+ if(sysmap_index) free(sysmap_index);
+ sysmap_index = NULL;
+ if(fd>=0) close(fd);
+ if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1);
+ sysmap_data = NULL;
+ return 0;
+}
+
+/*********************************/
+
+static void read_and_parse(void){
+ static time_t stamp; /* after data gets old, load /proc/ksyms again */
+ if(time(NULL) != stamp){
+ read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room);
+ parse_ksyms();
+ memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */
+ stamp = time(NULL);
+ }
+}
+
+/*********************************/
+
+static void default_message(const char *restrict format, ...) __attribute__((format(printf,1,2)));
+static void default_message(const char *restrict format, ...) {
+ va_list arg;
+
+ va_start (arg, format);
+ vfprintf (stderr, format, arg);
+ va_end (arg);
+}
+
+/*********************************/
+
+static int use_wchan_file;
+
+int open_psdb_message(const char *restrict override, message_fn message) {
+ static const char *sysmap_paths[] = {
+ "/boot/System.map-%s",
+ "/boot/System.map",
+ "/lib/modules/%s/System.map",
+ "/usr/src/linux/System.map",
+ "/System.map",
+ NULL
+ };
+ struct stat sbuf;
+ struct utsname uts;
+ char path[128];
+ const char **fmt = sysmap_paths;
+ const char *sm;
+
+#ifdef SYSMAP_FILENAME /* debug feature */
+ override = SYSMAP_FILENAME;
+#endif
+
+ // first allow for a user-selected System.map file
+ if(
+ (sm=override)
+ ||
+ (sm=getenv("PS_SYSMAP"))
+ ||
+ (sm=getenv("PS_SYSTEM_MAP"))
+ ){
+ if(!have_privs){
+ read_and_parse();
+ if(sysmap_mmap(sm, message)) return 0;
+ }
+ /* failure is better than ignoring the user & using bad data */
+ return -1; /* ought to return "Namelist not found." */
+ }
+
+ // next try the Linux 2.5.xx method
+ if(!stat("/proc/self/wchan", &sbuf)){
+ use_wchan_file = 1; // hack
+ return 0;
+ }
+
+ // finally, search for the System.map file
+ uname(&uts);
+ path[sizeof path - 1] = '\0';
+ do{
+ int did_ksyms = 0;
+ snprintf(path, sizeof path - 1, *fmt, uts.release);
+ if(!stat(path, &sbuf)){
+ if (did_ksyms++) read_and_parse();
+ if (sysmap_mmap(path, message)) return 0;
+ }
+ }while(*++fmt);
+ /* TODO: Without System.map, no need to keep ksyms loaded. */
+ return -1;
+}
+
+/***************************************/
+
+int open_psdb(const char *restrict override) {
+ return open_psdb_message(override, default_message);
+}
+
+/***************************************/
+
+static const char * read_wchan_file(unsigned pid){
+ static char buf[64];
+ const char *ret = buf;
+ ssize_t num;
+ int fd;
+
+ snprintf(buf, sizeof buf, "/proc/%d/wchan", pid);
+ fd = open(buf, O_RDONLY);
+ if(fd==-1) return "?";
+ num = read(fd, buf, sizeof buf - 1);
+ close(fd);
+ if(num<1) return "?"; // allow for "0"
+ buf[num] = '\0';
+
+ if(buf[0]=='0' && buf[1]=='\0') return "-";
+
+ // would skip over numbers if they existed -- but no
+
+ // lame ppc64 has a '.' in front of every name
+ if(*ret=='.') ret++;
+ switch(*ret){
+ case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break;
+ case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break;
+ case '_': while(*ret=='_') ret++; break;
+ }
+ return ret;
+}
+
+/***************************************/
+
+static const symb fail = { .name = "?" };
+static const char dash[] = "-";
+static const char star[] = "*";
+
+#define MAX_OFFSET (0x1000*sizeof(long)) /* past this is generally junk */
+
+/* return pointer to temporary static buffer with function name */
+const char * lookup_wchan(unsigned KLONG address, unsigned pid) {
+ const symb *mod_symb;
+ const symb *map_symb;
+ const symb *good_symb;
+ const char *ret;
+ unsigned hash;
+
+ // can't cache it due to a race condition :-(
+ if(use_wchan_file) return read_wchan_file(pid);
+
+ if(!address) return dash;
+ if(!~address) return star;
+
+ read_and_parse();
+ hash = (address >> 4) & 0xff; /* got 56/63 hits & 7/63 misses */
+ if(hashtable[hash].addr == address) return hashtable[hash].name;
+ mod_symb = search(address, ksyms_index, ksyms_count);
+ if(!mod_symb) mod_symb = &fail;
+ map_symb = search(address, sysmap_index, sysmap_count);
+ if(!map_symb) map_symb = &fail;
+
+ /* which result is closest? */
+ good_symb = (mod_symb->addr > map_symb->addr)
+ ? mod_symb
+ : map_symb
+ ;
+ if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail;
+
+ /* good_symb->name has the data, but needs to be trimmed */
+ ret = good_symb->name;
+ // lame ppc64 has a '.' in front of every name
+ if(*ret=='.') ret++;
+ switch(*ret){
+ case 's': if(!strncmp(ret, "sys_", 4)) ret += 4; break;
+ case 'd': if(!strncmp(ret, "do_", 3)) ret += 3; break;
+ case '_': while(*ret=='_') ret++; break;
+ }
+ /* if(!*ret) ret = fail.name; */ /* not likely (name was "sys_", etc.) */
+
+ /* cache name after abbreviation */
+ hashtable[hash].addr = address;
+ hashtable[hash].name = ret;
+
+ return ret;
+}
--- /dev/null
+# for --version-script
+# WTF is the syntax for this file?
+# Give me a BNF, man!
+_3_2_5 {
+global:
+ __cyg_profile_func_enter; __cyg_profile_func_exit; main;
+
+ readproc; readtask; readproctab; readproctab2; look_up_our_self; escape_command;
+ escape_str; escape_strlist;
+ openproc; closeproc;
+ tty_to_dev; dev_to_tty; open_psdb_message; open_psdb; lookup_wchan;
+ display_version; procps_version; linux_version_code;
+ Hertz; smp_num_cpus; have_privs;
+ sprint_uptime; uptime; user_from_uid; print_uptime; loadavg;
+ pretty_print_signals; print_given_signals; unix_print_signals; signal_name_to_number; signal_number_to_name;
+ meminfo; vminfo; getstat; getdiskstat; getpartitions_num; getslabinfo; get_pid_digits;
+ kb_active; kb_inactive; kb_main_buffers; kb_main_cached;
+ kb_main_free; kb_main_total; kb_main_used; kb_swap_free;
+ kb_swap_total; kb_swap_used; kb_main_shared;
+ kb_low_total; kb_low_free; kb_high_total; kb_high_free;
+ vm_pgpgin; vm_pgpgout; vm_pswpin; vm_pswpout;
+ free_slabinfo; put_slabinfo; get_slabinfo; get_proc_stats;
+local: *;
+};
--- /dev/null
+# This file gets included into the main Makefile, in the top directory.
+
+# Ideally, we want something like this:
+#
+# /lib/libproc.so.w ELF soname ('w' is a digit, starting from 1)
+# /lib/procps-x.y.z.so file itself (x.y.z is the procps version)
+# /lib/libproc.so for linking, UNSUPPORTED
+# /usr/lib/libproc.a for linking, UNSUPPORTED
+# proc/libproc.so.w as above, if testing with LD_LIBRARY_PATH
+# proc/whatever if testing with LD_PRELOAD
+# proc/libproc.a for static build
+#
+# Without a stable ABI, there's no point in having any of that.
+# Without a stable API, there's no point in having the *.a file.
+#
+# A new ELF soname is required for every big ABI change. To conserve
+# numbers for future use, the ELF soname can be set equal to the
+# file name until some future date when a stable ABI is declared.
+
+SHARED := 1
+
+# for lib$(NAME).so and /usr/include/($NAME) and such
+NAME := proc
+
+LIBVERSION := $(VERSION).$(SUBVERSION).$(MINORVERSION)
+ABIVERSION := 0
+
+SOFILE := lib$(NAME)-$(LIBVERSION).so
+ifneq ($(ABIVERSION),0)
+SOLINK := lib$(NAME).so
+SONAME := lib$(NAME).so.$(ABIVERSION)
+else
+SONAME := $(SOFILE)
+SOLINK := $(SOFILE)
+endif
+
+ANAME := lib$(NAME).a
+
+############
+
+FPIC := -fpic
+
+ifeq ($(SHARED),1)
+ALL += proc/$(SONAME)
+INSTALL += ldconfig
+LIBFLAGS := -DSHARED=1 $(FPIC)
+# This is in gcc 3.5, but exported functions must be marked.
+#LIBFLAGS += $(call check_gcc,-fvisibility=hidden,)
+LIBPROC := proc/$(SONAME)
+else
+ALL += proc/$(ANAME)
+#INSTALL += $(usr/lib)$(ANAME)
+LIBFLAGS := -DSHARED=0
+LIBPROC := proc/$(ANAME)
+endif
+
+LIBSRC := $(wildcard proc/*.c)
+LIBHDR := $(wildcard proc/*.h)
+LIBOBJ := $(LIBSRC:.c=.o)
+
+# Separate rule for this directory, to use -fpic or -fPIC
+$(filter-out proc/version.o,$(LIBOBJ)): proc/%.o: proc/%.c
+ $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) $< -o $@
+
+LIB_X := COPYING module.mk library.map
+TARFILES += $(LIBSRC) $(LIBHDR) $(addprefix proc/,$(LIB_X))
+
+
+# Clean away all output files, .depend, and symlinks.
+# Use wildcards in case the version has changed.
+CLEAN += proc/.depend proc/lib*.so* proc/lib*.a $(LIBOBJ)
+DIRS += proc/
+
+proc/$(ANAME): $(LIBOBJ)
+ $(AR) rcs $@ $^
+
+#proc/$(SONAME): proc/library.map
+proc/$(SONAME): $(LIBOBJ)
+ $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -shared -Wl,-soname,$(SONAME) -Wl,--version-script=proc/library.map -o $@ $^ -lc
+
+
+# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT
+proc/.depend: $(LIBSRC) $(LIBHDR)
+ $(strip $(CC) $(ALL_CPPFLAGS) $(LIB_CFLAGS) -MM -MG $(LIBSRC) > $@)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),tar)
+ifneq ($(MAKECMDGOALS),extratar)
+ifneq ($(MAKECMDGOALS),beta)
+-include proc/.depend
+endif
+endif
+endif
+endif
+
+#################### install rules ###########################
+
+$(lib)$(SOFILE) : proc/$(SONAME)
+ $(install) --mode a=rx $< $@
+
+ifneq ($(SOLINK),$(SOFILE))
+.PHONY: $(lib)$(SOLINK)
+$(lib)$(SOLINK) : $(lib)$(SOFILE)
+ cd $(lib) && $(ln_sf) $(SOFILE) $(SOLINK)
+endif
+
+ifneq ($(SONAME),$(SOFILE))
+.PHONY: $(lib)$(SONAME)
+$(lib)$(SONAME) : $(lib)$(SOFILE)
+ cd $(lib) && $(ln_sf) $(SOFILE) $(SONAME)
+endif
+
+.PHONY: ldconfig
+ldconfig : $(lib)$(SONAME) $(lib)$(SOLINK)
+ $(ldconfig)
+
+$(usr/lib)$(ANAME) : proc/$(ANAME)
+ $(install) --mode a=r $< $@
+
+# Junk anyway... supposed to go in /usr/include/$(NAME)
+#INSTALL += $(addprefix $(include),$(HDRFILES))
+#
+#$(addprefix $(include),$(HDRFILES)): $(include)% : proc/%
+#$(include)% : proc/%
+# $(install) --mode a=r $< $@
+
+##################################################################
+
+proc/version.o: proc/version.c proc/version.h
+ $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(LIBFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c -o $@ $<
--- /dev/null
+#ifndef PROCPS_PROC_PROCPS_H
+#define PROCPS_PROC_PROCPS_H
+
+#ifdef __cplusplus
+#define EXTERN_C_BEGIN extern "C" {
+#define EXTERN_C_END }
+#else
+#define EXTERN_C_BEGIN
+#define EXTERN_C_END
+#endif
+
+// Some ports make the mistake of running a 32-bit userspace
+// on a 64-bit kernel. Shame on them. It's not at all OK to
+// make everything "long long", since that causes unneeded
+// slowness on 32-bit hardware.
+//
+// SPARC: The 32-bit kernel was looking like an ex-penguin,
+// but it lives! ("I'm not dead yet.") So, 64-bit users will
+// just have to compile for 64-bit. Aw, the suffering.
+//
+// MIPS: Used 32-bit for embedded systems and obsolete hardware.
+// The 64-bit systems use an n32 format executable, defining
+// _ABIN32 to indicate this. Since n32 doesn't currently run on
+// any 32-bit system, nobody get hurt if it's bloated. Not that
+// this is sane of course, but it won't hurt the 32-bit users.
+// __mips_eabi means eabi, which comes in both sizes, but isn't used.
+//
+// PowerPC: Big ugly problem! 32-bit Macs are still popular. :-/
+//
+// x86-64: So far, nobody has been dumb enough to go 32-bit.
+//
+// Unknown: PA-RISC and zSeries
+//
+#if defined(k64test) || (defined(_ABIN32) && _MIPS_SIM == _ABIN32)
+#define KLONG long long // not typedef; want "unsigned KLONG" to work
+#define KLF "L"
+#define STRTOUKL strtoull
+#else
+#define KLONG long
+#define KLF "l"
+#define STRTOUKL strtoul
+#endif
+
+// since gcc-2.5
+#define NORETURN __attribute__((__noreturn__))
+#define FUNCTION __attribute__((__const__)) // no access to global mem, even via ptr, and no side effect
+
+#if !defined(restrict) && __STDC_VERSION__ < 199901
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 92
+#define restrict __restrict__
+#else
+#warning No restrict keyword?
+#define restrict
+#endif
+#endif
+
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 96
+// won't alias anything, and aligned enough for anything
+#define MALLOC __attribute__ ((__malloc__))
+// no side effect, may read globals
+#define PURE __attribute__ ((__pure__))
+// tell gcc what to expect: if(unlikely(err)) die(err);
+#define likely(x) __builtin_expect(!!(x),1)
+#define unlikely(x) __builtin_expect(!!(x),0)
+#define expected(x,y) __builtin_expect((x),(y))
+#else
+#define MALLOC
+#define PURE
+#define likely(x) (x)
+#define unlikely(x) (x)
+#define expected(x,y) (x)
+#endif
+
+#if SHARED==1 && (__GNUC__ > 2 || __GNUC_MINOR__ >= 96)
+#define LABEL_OFFSET
+#endif
+
+#define STRINGIFY_ARG(a) #a
+#define STRINGIFY(a) STRINGIFY_ARG(a)
+
+// marks old junk, to warn non-procps library users
+#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 0 ) || __GNUC__ > 3
+#define OBSOLETE __attribute__((deprecated))
+#else
+#define OBSOLETE
+#endif
+
+#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 1 ) || __GNUC__ > 3
+// Tells gcc that function is library-internal;
+// so no need to do dynamic linking at run-time.
+// This might work with slightly older compilers too.
+#define HIDDEN __attribute__((visibility("hidden")))
+// The opposite, in case -fvisibility=hidden used
+#define EXPORT __attribute__((visibility("default")))
+// Tell g++ that a function won't throw exceptions.
+#define NOTHROW __attribute__((__nothrow__))
+#else
+#define HIDDEN
+#define EXPORT
+#define NOTHROW
+#endif
+
+// Like HIDDEN, but for an alias that gets created.
+// In gcc-3.2 there is an alias+hidden conflict.
+// Many will have patched this bug, but oh well.
+#if ( __GNUC__ == 3 && __GNUC_MINOR__ > 2 ) || __GNUC__ > 3
+#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x),visibility("hidden")))
+#else
+#define HIDDEN_ALIAS(x) extern __typeof(x) x##_direct __attribute__((alias(#x)))
+#endif
+
+#endif
--- /dev/null
+// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com
+// Note: most likely none of his code remains
+//
+// Copyright 2002, Albert Cahalan
+//
+// This file is placed under the conditions of the GNU Library
+// General Public License, version 2, or any later version.
+// See file COPYING for information on distribution conditions.
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include "alloc.h"
+#include "pwcache.h"
+#include <grp.h>
+
+// might as well fill cache lines... else we waste memory anyway
+
+#define HASHSIZE 64 /* power of 2 */
+#define HASH(x) ((x) & (HASHSIZE - 1))
+
+static struct pwbuf {
+ struct pwbuf *next;
+ uid_t uid;
+ char name[P_G_SZ];
+} *pwhash[HASHSIZE];
+
+char *user_from_uid(uid_t uid) {
+ struct pwbuf **p;
+ struct passwd *pw;
+
+ p = &pwhash[HASH(uid)];
+ while (*p) {
+ if ((*p)->uid == uid)
+ return((*p)->name);
+ p = &(*p)->next;
+ }
+ *p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf));
+ (*p)->uid = uid;
+ pw = getpwuid(uid);
+ if(!pw || strlen(pw->pw_name) >= P_G_SZ)
+ sprintf((*p)->name, "%u", uid);
+ else
+ strcpy((*p)->name, pw->pw_name);
+
+ (*p)->next = NULL;
+ return((*p)->name);
+}
+
+static struct grpbuf {
+ struct grpbuf *next;
+ gid_t gid;
+ char name[P_G_SZ];
+} *grphash[HASHSIZE];
+
+char *group_from_gid(gid_t gid) {
+ struct grpbuf **g;
+ struct group *gr;
+
+ g = &grphash[HASH(gid)];
+ while (*g) {
+ if ((*g)->gid == gid)
+ return((*g)->name);
+ g = &(*g)->next;
+ }
+ *g = (struct grpbuf *) malloc(sizeof(struct grpbuf));
+ (*g)->gid = gid;
+ gr = getgrgid(gid);
+ if (!gr || strlen(gr->gr_name) >= P_G_SZ)
+ sprintf((*g)->name, "%u", gid);
+ else
+ strcpy((*g)->name, gr->gr_name);
+ (*g)->next = NULL;
+ return((*g)->name);
+}
--- /dev/null
+#ifndef PROCPS_PROC_PWCACHE_H
+#define PROCPS_PROC_PWCACHE_H
+
+#include <sys/types.h>
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+// used in pwcache and in readproc to set size of username or groupname
+#define P_G_SZ 20
+
+extern char *user_from_uid(uid_t uid);
+extern char *group_from_gid(gid_t gid);
+
+EXTERN_C_END
+
+#endif
--- /dev/null
+/*
+ * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+ * Copyright (C) 1996 Charles L. Blake.
+ * Copyright (C) 1998 Michael K. Johnson
+ * Copyright 1998-2003 Albert Cahalan
+ * May be distributed under the conditions of the
+ * GNU Library General Public License; a copy is in COPYING
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "version.h"
+#include "readproc.h"
+#include "alloc.h"
+#include "pwcache.h"
+#include "devname.h"
+#include "procps.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/dir.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// sometimes it's easier to do this manually, w/o gcc helping
+#ifdef PROF
+extern void __cyg_profile_func_enter(void*,void*);
+#define ENTER(x) __cyg_profile_func_enter((void*)x,(void*)x)
+#define LEAVE(x) __cyg_profile_func_exit((void*)x,(void*)x)
+#else
+#define ENTER(x)
+#define LEAVE(x)
+#endif
+
+// convert hex string to unsigned long long
+static unsigned long long unhex(const char *restrict cp){
+ unsigned long long ull = 0;
+ for(;;){
+ char c = *cp++;
+ if(unlikely(c<0x30)) break;
+ ull = (ull<<4) | (c - (c>0x57) ? 0x57 : 0x30) ;
+ }
+ return ull;
+}
+
+static int task_dir_missing;
+
+///////////////////////////////////////////////////////////////////////////
+
+typedef struct status_table_struct {
+ unsigned char name[7]; // /proc/*/status field name
+ unsigned char len; // name length
+#ifdef LABEL_OFFSET
+ long offset; // jump address offset
+#else
+ void *addr;
+#endif
+} status_table_struct;
+
+#ifdef LABEL_OFFSET
+#define F(x) {#x, sizeof(#x)-1, (long)(&&case_##x-&&base)},
+#else
+#define F(x) {#x, sizeof(#x)-1, &&case_##x},
+#endif
+#define NUL {"", 0, 0},
+
+// Derived from:
+// gperf -7 --language=ANSI-C --key-positions=1,3,4 -C -n -c sml.gperf
+//
+// Suggested method:
+// Grep this file for "case_", then strip those down to the name.
+// (leave the colon and newline) So "Pid:\n" and "Threads:\n"
+// would be lines in the file. (no quote, no escape, etc.)
+//
+// Watch out for name size in the status_table_struct (grrr, expanding)
+// and the number of entries (we mask with 63 for now). The table
+// must be padded out to 64 entries, maybe 128 in the future.
+
+static void status2proc(char *S, proc_t *restrict P, int is_proc){
+ long Threads = 0;
+ long Tgid = 0;
+ long Pid = 0;
+
+ static const unsigned char asso[] =
+ {
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 15, 61,
+ 61, 61, 61, 61, 61, 61, 30, 3, 5, 5,
+ 61, 5, 61, 8, 61, 61, 3, 61, 10, 61,
+ 6, 61, 13, 0, 30, 25, 0, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 3, 61, 13,
+ 0, 0, 61, 30, 61, 25, 61, 61, 61, 0,
+ 61, 61, 61, 61, 5, 61, 0, 61, 61, 61,
+ 0, 61, 61, 61, 61, 61, 61, 61
+ };
+
+ static const status_table_struct table[] = {
+ F(VmStk)
+ NUL NUL
+ F(State)
+ NUL
+ F(VmExe)
+ F(ShdPnd)
+ NUL
+ F(VmData)
+ NUL
+ F(Name)
+ NUL NUL
+ F(VmRSS)
+ NUL NUL
+ F(VmLck)
+ NUL NUL NUL
+ F(Gid)
+ F(Pid)
+ NUL NUL NUL
+ F(VmSize)
+ NUL NUL
+ F(VmLib)
+ NUL NUL
+ F(PPid)
+ NUL
+ F(SigCgt)
+ NUL
+ F(Threads)
+ F(SigPnd)
+ NUL
+ F(SigIgn)
+ NUL
+ F(Uid)
+ NUL NUL NUL NUL NUL NUL NUL NUL NUL
+ NUL NUL NUL NUL NUL
+ F(Tgid)
+ NUL NUL NUL NUL
+ F(SigBlk)
+ NUL NUL NUL
+ };
+
+#undef F
+#undef NUL
+
+ENTER(0x220);
+
+ P->vm_size = 0;
+ P->vm_lock = 0;
+ P->vm_rss = 0;
+ P->vm_data = 0;
+ P->vm_stack= 0;
+ P->vm_exe = 0;
+ P->vm_lib = 0;
+ P->nlwp = 0;
+ P->signal[0] = '\0'; // so we can detect it as missing for very old kernels
+
+ goto base;
+
+ for(;;){
+ char *colon;
+ status_table_struct entry;
+
+ // advance to next line
+ S = strchr(S, '\n');
+ if(unlikely(!S)) break; // if no newline
+ S++;
+
+ // examine a field name (hash and compare)
+ base:
+ if(unlikely(!*S)) break;
+ entry = table[63 & (asso[S[3]] + asso[S[2]] + asso[S[0]])];
+ colon = strchr(S, ':');
+ if(unlikely(!colon)) break;
+ if(unlikely(colon[1]!='\t')) break;
+ if(unlikely(colon-S != entry.len)) continue;
+ if(unlikely(memcmp(entry.name,S,colon-S))) continue;
+
+ S = colon+2; // past the '\t'
+
+#ifdef LABEL_OFFSET
+ goto *(&&base + entry.offset);
+#else
+ goto *entry.addr;
+#endif
+
+ case_Name:{
+ unsigned u = 0;
+ while(u < sizeof P->cmd - 1u){
+ int c = *S++;
+ if(unlikely(c=='\n')) break;
+ if(unlikely(c=='\0')) break; // should never happen
+ if(unlikely(c=='\\')){
+ c = *S++;
+ if(c=='\n') break; // should never happen
+ if(!c) break; // should never happen
+ if(c=='n') c='\n'; // else we assume it is '\\'
+ }
+ P->cmd[u++] = c;
+ }
+ P->cmd[u] = '\0';
+ S--; // put back the '\n' or '\0'
+ continue;
+ }
+#ifdef SIGNAL_STRING
+ case_ShdPnd:
+ memcpy(P->signal, S, 16);
+ P->signal[16] = '\0';
+ continue;
+ case_SigBlk:
+ memcpy(P->blocked, S, 16);
+ P->blocked[16] = '\0';
+ continue;
+ case_SigCgt:
+ memcpy(P->sigcatch, S, 16);
+ P->sigcatch[16] = '\0';
+ continue;
+ case_SigIgn:
+ memcpy(P->sigignore, S, 16);
+ P->sigignore[16] = '\0';
+ continue;
+ case_SigPnd:
+ memcpy(P->_sigpnd, S, 16);
+ P->_sigpnd[16] = '\0';
+ continue;
+#else
+ case_ShdPnd:
+ P->signal = unhex(S);
+ continue;
+ case_SigBlk:
+ P->blocked = unhex(S);
+ continue;
+ case_SigCgt:
+ P->sigcatch = unhex(S);
+ continue;
+ case_SigIgn:
+ P->sigignore = unhex(S);
+ continue;
+ case_SigPnd:
+ P->_sigpnd = unhex(S);
+ continue;
+#endif
+ case_State:
+ P->state = *S;
+ continue;
+ case_Tgid:
+ Tgid = strtol(S,&S,10);
+ continue;
+ case_Pid:
+ Pid = strtol(S,&S,10);
+ continue;
+ case_PPid:
+ P->ppid = strtol(S,&S,10);
+ continue;
+ case_Threads:
+ Threads = strtol(S,&S,10);
+ continue;
+ case_Uid:
+ P->ruid = strtol(S,&S,10);
+ P->euid = strtol(S,&S,10);
+ P->suid = strtol(S,&S,10);
+ P->fuid = strtol(S,&S,10);
+ continue;
+ case_Gid:
+ P->rgid = strtol(S,&S,10);
+ P->egid = strtol(S,&S,10);
+ P->sgid = strtol(S,&S,10);
+ P->fgid = strtol(S,&S,10);
+ continue;
+ case_VmData:
+ P->vm_data = strtol(S,&S,10);
+ continue;
+ case_VmExe:
+ P->vm_exe = strtol(S,&S,10);
+ continue;
+ case_VmLck:
+ P->vm_lock = strtol(S,&S,10);
+ continue;
+ case_VmLib:
+ P->vm_lib = strtol(S,&S,10);
+ continue;
+ case_VmRSS:
+ P->vm_rss = strtol(S,&S,10);
+ continue;
+ case_VmSize:
+ P->vm_size = strtol(S,&S,10);
+ continue;
+ case_VmStk:
+ P->vm_stack = strtol(S,&S,10);
+ continue;
+ }
+
+#if 0
+ // recent kernels supply per-tgid pending signals
+ if(is_proc && *ShdPnd){
+ memcpy(P->signal, ShdPnd, 16);
+ P->signal[16] = '\0';
+ }
+#endif
+
+ // recent kernels supply per-tgid pending signals
+#ifdef SIGNAL_STRING
+ if(!is_proc || !P->signal[0]){
+ memcpy(P->signal, P->_sigpnd, 16);
+ P->signal[16] = '\0';
+ }
+#else
+ if(!is_proc || !have_process_pending){
+ P->signal = P->_sigpnd;
+ }
+#endif
+
+ // Linux 2.4.13-pre1 to max 2.4.xx have a useless "Tgid"
+ // that is not initialized for built-in kernel tasks.
+ // Only 2.6.0 and above have "Threads" (nlwp) info.
+
+ if(Threads){
+ P->nlwp = Threads;
+ P->tgid = Tgid; // the POSIX PID value
+ P->tid = Pid; // the thread ID
+ }else{
+ P->nlwp = 1;
+ P->tgid = Pid;
+ P->tid = Pid;
+ }
+
+LEAVE(0x220);
+}
+
+///////////////////////////////////////////////////////////////////////
+
+// Reads /proc/*/stat files, being careful not to trip over processes with
+// names like ":-) 1 2 3 4 5 6".
+static void stat2proc(const char* S, proc_t *restrict P) {
+ unsigned num;
+ char* tmp;
+
+ENTER(0x160);
+
+ /* fill in default values for older kernels */
+ P->processor = 0;
+ P->rtprio = -1;
+ P->sched = -1;
+ P->nlwp = 0;
+
+ S = strchr(S, '(') + 1;
+ tmp = strrchr(S, ')');
+ num = tmp - S;
+ if(unlikely(num >= sizeof P->cmd)) num = sizeof P->cmd - 1;
+ memcpy(P->cmd, S, num);
+ P->cmd[num] = '\0';
+ S = tmp + 2; // skip ") "
+
+ num = sscanf(S,
+ "%c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu "
+ "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */
+ "%ld %ld "
+ "%d "
+ "%ld "
+ "%Lu " /* start_time */
+ "%lu "
+ "%ld "
+ "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
+ "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
+ "%"KLF"u %*lu %*lu "
+ "%d %d "
+ "%lu %lu",
+ &P->state,
+ &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
+ &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt,
+ &P->utime, &P->stime, &P->cutime, &P->cstime,
+ &P->priority, &P->nice,
+ &P->nlwp,
+ &P->alarm,
+ &P->start_time,
+ &P->vsize,
+ &P->rss,
+ &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
+/* P->signal, P->blocked, P->sigignore, P->sigcatch, */ /* can't use */
+ &P->wchan, /* &P->nswap, &P->cnswap, */ /* nswap and cnswap dead for 2.4.xx and up */
+/* -- Linux 2.0.35 ends here -- */
+ &P->exit_signal, &P->processor, /* 2.2.1 ends with "exit_signal" */
+/* -- Linux 2.2.8 to 2.5.17 end here -- */
+ &P->rtprio, &P->sched /* both added to 2.5.18 */
+ );
+
+ if(!P->nlwp){
+ P->nlwp = 1;
+ }
+
+LEAVE(0x160);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static void statm2proc(const char* s, proc_t *restrict P) {
+ int num;
+ num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
+ &P->size, &P->resident, &P->share,
+ &P->trs, &P->lrs, &P->drs, &P->dt);
+/* fprintf(stderr, "statm2proc converted %d fields.\n",num); */
+}
+
+static int file2str(const char *directory, const char *what, char *ret, int cap) {
+ static char filename[80];
+ int fd, num_read;
+
+ sprintf(filename, "%s/%s", directory, what);
+ fd = open(filename, O_RDONLY, 0);
+ if(unlikely(fd==-1)) return -1;
+ num_read = read(fd, ret, cap - 1);
+ close(fd);
+ if(unlikely(num_read<=0)) return -1;
+ ret[num_read] = '\0';
+ return num_read;
+}
+
+static char** file2strvec(const char* directory, const char* what) {
+ char buf[2048]; /* read buf bytes at a time */
+ char *p, *rbuf = 0, *endbuf, **q, **ret;
+ int fd, tot = 0, n, c, end_of_file = 0;
+ int align;
+
+ sprintf(buf, "%s/%s", directory, what);
+ fd = open(buf, O_RDONLY, 0);
+ if(fd==-1) return NULL;
+
+ /* read whole file into a memory buffer, allocating as we go */
+ while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
+ if (n < (int)(sizeof buf - 1))
+ end_of_file = 1;
+ if (n == 0 && rbuf == 0)
+ return NULL; /* process died between our open and read */
+ if (n < 0) {
+ if (rbuf)
+ free(rbuf);
+ return NULL; /* read error */
+ }
+ if (end_of_file && buf[n-1]) /* last read char not null */
+ buf[n++] = '\0'; /* so append null-terminator */
+ rbuf = xrealloc(rbuf, tot + n); /* allocate more memory */
+ memcpy(rbuf + tot, buf, n); /* copy buffer into it */
+ tot += n; /* increment total byte ctr */
+ if (end_of_file)
+ break;
+ }
+ close(fd);
+ if (n <= 0 && !end_of_file) {
+ if (rbuf) free(rbuf);
+ return NULL; /* read error */
+ }
+ endbuf = rbuf + tot; /* count space for pointers */
+ align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
+ for (c = 0, p = rbuf; p < endbuf; p++)
+ if (!*p)
+ c += sizeof(char*);
+ c += sizeof(char*); /* one extra for NULL term */
+
+ rbuf = xrealloc(rbuf, tot + c + align); /* make room for ptrs AT END */
+ endbuf = rbuf + tot; /* addr just past data buf */
+ q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */
+ *q++ = p = rbuf; /* point ptrs to the strings */
+ endbuf--; /* do not traverse final NUL */
+ while (++p < endbuf)
+ if (!*p) /* NUL char implies that */
+ *q++ = p+1; /* next string -> next char */
+
+ *q = 0; /* null ptr list terminator */
+ return ret;
+}
+
+// warning: interface may change
+int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid){
+ char name[32];
+ int fd;
+ unsigned n = 0;
+ dst[0] = '\0';
+ snprintf(name, sizeof name, "/proc/%u/cmdline", pid);
+ fd = open(name, O_RDONLY);
+ if(fd==-1) return 0;
+ for(;;){
+ ssize_t r = read(fd,dst+n,sz-n);
+ if(r==-1){
+ if(errno==EINTR) continue;
+ break;
+ }
+ n += r;
+ if(n==sz) break; // filled the buffer
+ if(r==0) break; // EOF
+ }
+ close(fd);
+ if(n){
+ int i;
+ if(n==sz) n--;
+ dst[n] = '\0';
+ i=n;
+ while(i--){
+ int c = dst[i];
+ if(c<' ' || c>'~') dst[i]=' ';
+ }
+ }
+ return n;
+}
+
+/* These are some nice GNU C expression subscope "inline" functions.
+ * The can be used with arbitrary types and evaluate their arguments
+ * exactly once.
+ */
+
+/* Test if item X of type T is present in the 0 terminated list L */
+# define XinL(T, X, L) ( { \
+ T x = (X), *l = (L); \
+ while (*l && *l != x) l++; \
+ *l == x; \
+ } )
+
+/* Test if item X of type T is present in the list L of length N */
+# define XinLN(T, X, L, N) ( { \
+ T x = (X), *l = (L); \
+ int i = 0, n = (N); \
+ while (i < n && l[i] != x) i++; \
+ i < n && l[i] == x; \
+ } )
+
+//////////////////////////////////////////////////////////////////////////////////
+// This reads process info from /proc in the traditional way, for one process.
+// The pid (tgid? tid?) is already in p, and a path to it in path, with some
+// room to spare.
+static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
+ static struct stat sb; // stat() buffer
+ static char sbuf[1024]; // buffer for stat,statm
+ char *restrict const path = PT->path;
+ unsigned flags = PT->flags;
+
+ if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
+ goto next_proc;
+
+ if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
+ goto next_proc; /* not one of the requested uids */
+
+ p->euid = sb.st_uid; /* need a way to get real uid */
+ p->egid = sb.st_gid; /* need a way to get real gid */
+
+ if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
+ if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
+ goto next_proc; /* error reading /proc/#/stat */
+ stat2proc(sbuf, p); /* parse /proc/#/stat */
+ }
+
+ if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
+ if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
+ statm2proc(sbuf, p); /* ignore statm errors here */
+ } /* statm fields just zero */
+
+ if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
+ if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
+ status2proc(sbuf, p, 1);
+ }
+ }
+
+ // if multithreaded, some values are crap
+ if(p->nlwp > 1){
+ p->wchan = (KLONG)~0ull;
+ }
+
+ /* some number->text resolving which is time consuming and kind of insane */
+ if (flags & PROC_FILLUSR){
+ memcpy(p->euser, user_from_uid(p->euid), sizeof p->euser);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(p->ruser, user_from_uid(p->ruid), sizeof p->ruser);
+ memcpy(p->suser, user_from_uid(p->suid), sizeof p->suser);
+ memcpy(p->fuser, user_from_uid(p->fuid), sizeof p->fuser);
+ }
+ }
+
+ /* some number->text resolving which is time consuming and kind of insane */
+ if (flags & PROC_FILLGRP){
+ memcpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
+ memcpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
+ memcpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
+ }
+ }
+
+ if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
+ p->cmdline = file2strvec(path, "cmdline");
+ else
+ p->cmdline = NULL;
+
+ if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
+ p->environ = file2strvec(path, "environ");
+ else
+ p->environ = NULL;
+
+ return p;
+next_proc:
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This reads /proc/*/task/* data, for one task.
+// p is the POSIX process (task group summary) (not needed by THIS implementation)
+// t is the POSIX thread (task group member, generally not the leader)
+// path is a path to the task, with some room to spare.
+static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
+ static struct stat sb; // stat() buffer
+ static char sbuf[1024]; // buffer for stat,statm
+ unsigned flags = PT->flags;
+
+//printf("hhh\n");
+ if (unlikely(stat(path, &sb) == -1)) /* no such dirent (anymore) */
+ goto next_task;
+
+// if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
+// goto next_task; /* not one of the requested uids */
+
+ t->euid = sb.st_uid; /* need a way to get real uid */
+ t->egid = sb.st_gid; /* need a way to get real gid */
+
+//printf("iii\n");
+ if (flags & PROC_FILLSTAT) { /* read, parse /proc/#/stat */
+ if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))
+ goto next_task; /* error reading /proc/#/stat */
+ stat2proc(sbuf, t); /* parse /proc/#/stat */
+ }
+
+ if (unlikely(flags & PROC_FILLMEM)) { /* read, parse /proc/#/statm */
+#if 0
+ if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))
+ statm2proc(sbuf, t); /* ignore statm errors here */
+#else
+ t->size = p->size;
+ t->resident = p->resident;
+ t->share = p->share;
+ t->trs = p->trs;
+ t->lrs = p->lrs;
+ t->drs = p->drs;
+ t->dt = p->dt;
+#endif
+ } /* statm fields just zero */
+
+ if (flags & PROC_FILLSTATUS) { /* read, parse /proc/#/status */
+ if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){
+ status2proc(sbuf, t, 0);
+ }
+ }
+
+ /* some number->text resolving which is time consuming */
+ if (flags & PROC_FILLUSR){
+ memcpy(t->euser, user_from_uid(t->euid), sizeof t->euser);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(t->ruser, user_from_uid(t->ruid), sizeof t->ruser);
+ memcpy(t->suser, user_from_uid(t->suid), sizeof t->suser);
+ memcpy(t->fuser, user_from_uid(t->fuid), sizeof t->fuser);
+ }
+ }
+
+ /* some number->text resolving which is time consuming */
+ if (flags & PROC_FILLGRP){
+ memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup);
+ if(flags & PROC_FILLSTATUS) {
+ memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup);
+ memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup);
+ memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup);
+ }
+ }
+
+#if 0
+ if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG)) /* read+parse /proc/#/cmdline */
+ t->cmdline = file2strvec(path, "cmdline");
+ else
+ t->cmdline = NULL;
+
+ if (unlikely(flags & PROC_FILLENV)) /* read+parse /proc/#/environ */
+ t->environ = file2strvec(path, "environ");
+ else
+ t->environ = NULL;
+#else
+ t->cmdline = p->cmdline; // better not free these until done with all threads!
+ t->environ = p->environ;
+#endif
+
+ t->ppid = p->ppid; // ought to put the per-task ppid somewhere
+
+ return t;
+next_task:
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This finds processes in /proc in the traditional way.
+// Return non-zero on success.
+static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
+ static struct direct *ent; /* dirent handle */
+ char *restrict const path = PT->path;
+ for (;;) {
+ ent = readdir(PT->procfs);
+ if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
+ if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
+ }
+ p->tgid = strtoul(ent->d_name, NULL, 10);
+ p->tid = p->tgid;
+ memcpy(path, "/proc/", 6);
+ strcpy(path+6, ent->d_name); // trust /proc to not contain evil top-level entries
+ return 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This finds tasks in /proc/*/task/ in the traditional way.
+// Return non-zero on success.
+static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
+ static struct direct *ent; /* dirent handle */
+ if(PT->taskdir_user != p->tgid){
+ if(PT->taskdir){
+ closedir(PT->taskdir);
+ }
+ // use "path" as some tmp space
+ snprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid);
+ PT->taskdir = opendir(path);
+ if(!PT->taskdir) return 0;
+ PT->taskdir_user = p->tgid;
+ }
+ for (;;) {
+ ent = readdir(PT->taskdir);
+ if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
+ if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;
+ }
+ t->tid = strtoul(ent->d_name, NULL, 10);
+ t->tgid = p->tgid;
+ t->ppid = p->ppid; // cover for kernel behavior? we want both actually...?
+ snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name);
+ return 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// This "finds" processes in a list that was given to openproc().
+// Return non-zero on success. (tgid was handy)
+static int listed_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
+ char *restrict const path = PT->path;
+ pid_t tgid = *(PT->pids)++;
+ if(likely( tgid )){
+ snprintf(path, PROCPATHLEN, "/proc/%d", tgid);
+ p->tgid = tgid;
+ p->tid = tgid; // they match for leaders
+ }
+ return tgid;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+/* readproc: return a pointer to a proc_t filled with requested info about the
+ * next process available matching the restriction set. If no more such
+ * processes are available, return a null pointer (boolean false). Use the
+ * passed buffer instead of allocating space if it is non-NULL. */
+
+/* This is optimized so that if a PID list is given, only those files are
+ * searched for in /proc. If other lists are given in addition to the PID list,
+ * the same logic can follow through as for the no-PID list case. This is
+ * fairly complex, but it does try to not to do any unnecessary work.
+ */
+proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {
+ proc_t *ret;
+ proc_t *saved_p;
+
+ PT->did_fake=0;
+// if (PT->taskdir) {
+// closedir(PT->taskdir);
+// PT->taskdir = NULL;
+// PT->taskdir_user = -1;
+// }
+
+ saved_p = p;
+ if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */
+
+ for(;;){
+ // fills in the path, plus p->tid and p->tgid
+ if (unlikely(! PT->finder(PT,p) )) goto out;
+
+ // go read the process data
+ ret = PT->reader(PT,p);
+ if(ret) return ret;
+ }
+
+out:
+ if(!saved_p) free(p);
+ // FIXME: maybe set tid to -1 here, for "-" in display?
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// readtask: return a pointer to a proc_t filled with requested info about the
+// next task available. If no more such tasks are available, return a null
+// pointer (boolean false). Use the passed buffer instead of allocating
+// space if it is non-NULL.
+proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t) {
+ static char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline
+ proc_t *ret;
+ proc_t *saved_t;
+
+ saved_t = t;
+ if(!t) t = xcalloc(t, sizeof *t); /* passed buf or alloced mem */
+
+ // 1. got to fake a thread for old kernels
+ // 2. for single-threaded processes, this is faster (but must patch up stuff that differs!)
+ if(task_dir_missing || p->nlwp < 2){
+ if(PT->did_fake) goto out;
+ PT->did_fake=1;
+ memcpy(t,p,sizeof(proc_t));
+ // use the per-task pending, not per-tgid pending
+#ifdef SIGNAL_STRING
+ memcpy(&t->signal, &t->_sigpnd, sizeof t->signal);
+#else
+ t->signal = t->_sigpnd;
+#endif
+ return t;
+ }
+
+ for(;;){
+ // fills in the path, plus t->tid and t->tgid
+ if (unlikely(! PT->taskfinder(PT,p,t,path) )) goto out; // simple_nexttid
+
+ // go read the task data
+ ret = PT->taskreader(PT,p,t,path); // simple_readtask
+ if(ret) return ret;
+ }
+
+out:
+ if(!saved_t) free(t);
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// initiate a process table scan
+PROCTAB* openproc(int flags, ...) {
+ va_list ap;
+ struct stat sbuf;
+ static int did_stat;
+ PROCTAB* PT = xmalloc(sizeof(PROCTAB));
+
+ if(!did_stat){
+ task_dir_missing = stat("/proc/self/task", &sbuf);
+ did_stat = 1;
+ }
+ PT->taskdir = NULL;
+ PT->taskdir_user = -1;
+ PT->taskfinder = simple_nexttid;
+ PT->taskreader = simple_readtask;
+
+ PT->reader = simple_readproc;
+ if (flags & PROC_PID){
+ PT->procfs = NULL;
+ PT->finder = listed_nextpid;
+ }else{
+ PT->procfs = opendir("/proc");
+ if(!PT->procfs) return NULL;
+ PT->finder = simple_nextpid;
+ }
+ PT->flags = flags;
+
+ va_start(ap, flags); /* Init args list */
+ if (flags & PROC_PID)
+ PT->pids = va_arg(ap, pid_t*);
+ else if (flags & PROC_UID) {
+ PT->uids = va_arg(ap, uid_t*);
+ PT->nuid = va_arg(ap, int);
+ }
+ va_end(ap); /* Clean up args list */
+
+ return PT;
+}
+
+// terminate a process table scan
+void closeproc(PROCTAB* PT) {
+ if (PT){
+ if (PT->procfs) closedir(PT->procfs);
+ if (PT->taskdir) closedir(PT->taskdir);
+ memset(PT,'#',sizeof(PROCTAB));
+ free(PT);
+ }
+}
+
+// deallocate the space allocated by readproc if the passed rbuf was NULL
+void freeproc(proc_t* p) {
+ if (!p) /* in case p is NULL */
+ return;
+ /* ptrs are after strings to avoid copying memory when building them. */
+ /* so free is called on the address of the address of strvec[0]. */
+ if (p->cmdline)
+ free((void*)*p->cmdline);
+ if (p->environ)
+ free((void*)*p->environ);
+ free(p);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+void look_up_our_self(proc_t *p) {
+ char sbuf[1024];
+
+ if(file2str("/proc/self", "stat", sbuf, sizeof sbuf) == -1){
+ fprintf(stderr, "Error, do this: mount -t proc none /proc\n");
+ _exit(47);
+ }
+ stat2proc(sbuf, p); // parse /proc/self/stat
+}
+
+HIDDEN_ALIAS(readproc);
+HIDDEN_ALIAS(readtask);
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * table subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with exit(). Access via tab[N]->member. The pointer
+ * list is NULL terminated.
+ */
+proc_t** readproctab(int flags, ...) {
+ PROCTAB* PT = NULL;
+ proc_t** tab = NULL;
+ int n = 0;
+ va_list ap;
+
+ va_start(ap, flags); /* pass through args to openproc */
+ if (flags & PROC_UID) {
+ /* temporary variables to ensure that va_arg() instances
+ * are called in the right order
+ */
+ uid_t* u;
+ int i;
+
+ u = va_arg(ap, uid_t*);
+ i = va_arg(ap, int);
+ PT = openproc(flags, u, i);
+ }
+ else if (flags & PROC_PID)
+ PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+ else
+ PT = openproc(flags);
+ va_end(ap);
+ do { /* read table: */
+ tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
+ tab[n] = readproc_direct(PT, NULL); /* final null to terminate */
+ } while (tab[n++]); /* stop when NULL reached */
+ closeproc(PT);
+ return tab;
+}
+
+// Try again, this time with threads and selection.
+proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT) {
+ proc_t** ptab = NULL;
+ unsigned n_proc_alloc = 0;
+ unsigned n_proc = 0;
+
+ proc_t** ttab = NULL;
+ unsigned n_task_alloc = 0;
+ unsigned n_task = 0;
+
+ proc_t* data = NULL;
+ unsigned n_alloc = 0;
+ unsigned long n_used = 0;
+
+ proc_data_t *pd;
+
+ for(;;){
+ proc_t *tmp;
+ if(n_alloc == n_used){
+ //proc_t *old = data;
+ n_alloc = n_alloc*5/4+30; // grow by over 25%
+ data = realloc(data,sizeof(proc_t)*n_alloc);
+ //if(!data) return NULL;
+ }
+ if(n_proc_alloc == n_proc){
+ //proc_t **old = ptab;
+ n_proc_alloc = n_proc_alloc*5/4+30; // grow by over 25%
+ ptab = realloc(ptab,sizeof(proc_t*)*n_proc_alloc);
+ //if(!ptab) return NULL;
+ }
+ tmp = readproc_direct(PT, data+n_used);
+ if(!tmp) break;
+ if(!want_proc(tmp)) continue;
+ ptab[n_proc++] = (proc_t*)(n_used++);
+ if(!( PT->flags & PROC_LOOSE_TASKS )) continue;
+ for(;;){
+ proc_t *t;
+ if(n_alloc == n_used){
+ proc_t *old = data;
+ n_alloc = n_alloc*5/4+30; // grow by over 25%
+ data = realloc(data,sizeof(proc_t)*n_alloc);
+ // have to move tmp too
+ tmp = data+(tmp-old);
+ //if(!data) return NULL;
+ }
+ if(n_task_alloc == n_task){
+ //proc_t **old = ttab;
+ n_task_alloc = n_task_alloc*5/4+1; // grow by over 25%
+ ttab = realloc(ttab,sizeof(proc_t*)*n_task_alloc);
+ //if(!ttab) return NULL;
+ }
+ t = readtask_direct(PT, tmp, data+n_used);
+ if(!t) break;
+ if(!want_task(t)) continue;
+ ttab[n_task++] = (proc_t*)(n_used++);
+ }
+ }
+
+ pd = malloc(sizeof(proc_data_t));
+ pd->proc = ptab;
+ pd->task = ttab;
+ pd->nproc = n_proc;
+ pd->ntask = n_task;
+ if(PT->flags & PROC_LOOSE_TASKS){
+ pd->tab = ttab;
+ pd->n = n_task;
+ }else{
+ pd->tab = ptab;
+ pd->n = n_proc;
+ }
+ // change array indexes to pointers
+ while(n_proc--) ptab[n_proc] = data+(long)(ptab[n_proc]);
+ while(n_task--) ttab[n_task] = data+(long)(ttab[n_task]);
+
+ return pd;
+}
+
+/*
+ * get_proc_stats - lookup a single tasks information and fill out a proc_t
+ *
+ * On failure, returns NULL. On success, returns 'p' and 'p' is a valid
+ * and filled out proc_t structure.
+ */
+proc_t * get_proc_stats(pid_t pid, proc_t *p) {
+ static char path[PATH_MAX], sbuf[1024];
+ struct stat statbuf;
+
+ sprintf(path, "/proc/%d", pid);
+ if (stat(path, &statbuf)) {
+ perror("stat");
+ return NULL;
+ }
+
+ if (file2str(path, "stat", sbuf, sizeof sbuf) >= 0)
+ stat2proc(sbuf, p); /* parse /proc/#/stat */
+ if (file2str(path, "statm", sbuf, sizeof sbuf) >= 0)
+ statm2proc(sbuf, p); /* ignore statm errors here */
+ if (file2str(path, "status", sbuf, sizeof sbuf) >= 0)
+ status2proc(sbuf, p, 0 /*FIXME*/);
+
+ return p;
+}
--- /dev/null
+#ifndef PROCPS_PROC_READPROC_H
+#define PROCPS_PROC_READPROC_H
+
+// New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+// Copyright 1996 Charles L. Blake.
+// Copyright 1998 Michael K. Johnson
+// Copyright 1998-2003 Albert Cahalan
+// May be distributed under the terms of the
+// GNU Library General Public License, a copy of which is provided
+// in the file COPYING
+
+
+#include "procps.h"
+#include "pwcache.h"
+
+#define SIGNAL_STRING
+
+EXTERN_C_BEGIN
+
+// ld cutime, cstime, priority, nice, timeout, alarm, rss,
+// c state,
+// d ppid, pgrp, session, tty, tpgid,
+// s signal, blocked, sigignore, sigcatch,
+// lu flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime,
+// lu rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip,
+// lu start_time, vsize, wchan,
+
+// This is to help document a transition from pid to tgid/tid caused
+// by the introduction of thread support. It is used in cases where
+// neither tgid nor tid seemed correct. (in other words, FIXME)
+#define XXXID tid
+
+// Basic data structure which holds all information we can get about a process.
+// (unless otherwise specified, fields are read from /proc/#/stat)
+//
+// Most of it comes from task_struct in linux/sched.h
+//
+typedef struct proc_t {
+// 1st 16 bytes
+ int
+ tid, // (special) task id, the POSIX thread ID (see also: tgid)
+ ppid; // stat,status pid of parent process
+ unsigned
+ pcpu; // stat (special) %CPU usage (is not filled in by readproc!!!)
+ char
+ state, // stat,status single-char code for process state (S=sleeping)
+ pad_1, // n/a padding
+ pad_2, // n/a padding
+ pad_3; // n/a padding
+// 2nd 16 bytes
+ unsigned long long
+ utime, // stat user-mode CPU time accumulated by process
+ stime, // stat kernel-mode CPU time accumulated by process
+// and so on...
+ cutime, // stat cumulative utime of process and reaped children
+ cstime, // stat cumulative stime of process and reaped children
+ start_time; // stat start time of process -- seconds since 1-1-70
+#ifdef SIGNAL_STRING
+ char
+ // Linux 2.1.7x and up have 64 signals. Allow 64, plus '\0' and padding.
+ signal[18], // status mask of pending signals, per-task for readtask() but per-proc for readproc()
+ blocked[18], // status mask of blocked signals
+ sigignore[18], // status mask of ignored signals
+ sigcatch[18], // status mask of caught signals
+ _sigpnd[18]; // status mask of PER TASK pending signals
+#else
+ long long
+ // Linux 2.1.7x and up have 64 signals.
+ signal, // status mask of pending signals, per-task for readtask() but per-proc for readproc()
+ blocked, // status mask of blocked signals
+ sigignore, // status mask of ignored signals
+ sigcatch, // status mask of caught signals
+ _sigpnd; // status mask of PER TASK pending signals
+#endif
+ unsigned KLONG
+ start_code, // stat address of beginning of code segment
+ end_code, // stat address of end of code segment
+ start_stack, // stat address of the bottom of stack for the process
+ kstk_esp, // stat kernel stack pointer
+ kstk_eip, // stat kernel instruction pointer
+ wchan; // stat (special) address of kernel wait channel proc is sleeping in
+ long
+ priority, // stat kernel scheduling priority
+ nice, // stat standard unix nice level of process
+ rss, // stat resident set size from /proc/#/stat (pages)
+ alarm, // stat ?
+ // the next 7 members come from /proc/#/statm
+ size, // statm total # of pages of memory
+ resident, // statm number of resident set (non-swapped) pages (4k)
+ share, // statm number of pages of shared (mmap'd) memory
+ trs, // statm text resident set size
+ lrs, // statm shared-lib resident set size
+ drs, // statm data resident set size
+ dt; // statm dirty pages
+ unsigned long
+ vm_size, // status same as vsize in kb
+ vm_lock, // status locked pages in kb
+ vm_rss, // status same as rss in kb
+ vm_data, // status data size
+ vm_stack, // status stack size
+ vm_exe, // status executable size
+ vm_lib, // status library size (all pages, not just used ones)
+ rtprio, // stat real-time priority
+ sched, // stat scheduling class
+ vsize, // stat number of pages of virtual memory ...
+ rss_rlim, // stat resident set size limit?
+ flags, // stat kernel flags for the process
+ min_flt, // stat number of minor page faults since process start
+ maj_flt, // stat number of major page faults since process start
+ cmin_flt, // stat cumulative min_flt of process and child processes
+ cmaj_flt; // stat cumulative maj_flt of process and child processes
+ char
+ **environ, // (special) environment string vector (/proc/#/environ)
+ **cmdline; // (special) command line string vector (/proc/#/cmdline)
+ char
+ // Be compatible: Digital allows 16 and NT allows 14 ???
+ euser[P_G_SZ], // stat(),status effective user name
+ ruser[P_G_SZ], // status real user name
+ suser[P_G_SZ], // status saved user name
+ fuser[P_G_SZ], // status filesystem user name
+ rgroup[P_G_SZ], // status real group name
+ egroup[P_G_SZ], // status effective group name
+ sgroup[P_G_SZ], // status saved group name
+ fgroup[P_G_SZ], // status filesystem group name
+ cmd[16]; // stat,status basename of executable file in call to exec(2)
+ struct proc_t
+ *ring, // n/a thread group ring
+ *next; // n/a various library uses
+ int
+ pgrp, // stat process group id
+ session, // stat session id
+ nlwp, // stat,status number of threads, or 0 if no clue
+ tgid, // (special) task group ID, the POSIX PID (see also: tid)
+ tty, // stat full device number of controlling terminal
+ euid, egid, // stat(),status effective
+ ruid, rgid, // status real
+ suid, sgid, // status saved
+ fuid, fgid, // status fs (used for file access only)
+ tpgid, // stat terminal process group id
+ exit_signal, // stat might not be SIGCHLD
+ processor; // stat current (or most recent?) CPU
+} proc_t;
+
+// PROCTAB: data structure holding the persistent information readproc needs
+// from openproc(). The setup is intentionally similar to the dirent interface
+// and other system table interfaces (utmp+wtmp come to mind).
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#define PROCPATHLEN 64 // must hold /proc/2000222000/task/2000222000/cmdline
+
+typedef struct PROCTAB {
+ DIR* procfs;
+// char deBug0[64];
+ DIR* taskdir; // for threads
+// char deBug1[64];
+ pid_t taskdir_user; // for threads
+ int did_fake; // used when taskdir is missing
+ int(*finder)(struct PROCTAB *restrict const, proc_t *restrict const);
+ proc_t*(*reader)(struct PROCTAB *restrict const, proc_t *restrict const);
+ int(*taskfinder)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const);
+ proc_t*(*taskreader)(struct PROCTAB *restrict const, const proc_t *restrict const, proc_t *restrict const, char *restrict const);
+ pid_t* pids; // pids of the procs
+ uid_t* uids; // uids of procs
+ int nuid; // cannot really sentinel-terminate unsigned short[]
+ int i; // generic
+ unsigned flags;
+ unsigned u; // generic
+ void * vp; // generic
+ char path[PROCPATHLEN]; // must hold /proc/2000222000/task/2000222000/cmdline
+ unsigned pathlen; // length of string in the above (w/o '\0')
+} PROCTAB;
+
+// initialize a PROCTAB structure holding needed call-to-call persistent data
+extern PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ );
+
+typedef struct proc_data_t {
+ proc_t **tab;
+ proc_t **proc;
+ proc_t **task;
+ int n;
+ int nproc;
+ int ntask;
+} proc_data_t;
+
+extern proc_data_t *readproctab2(int(*want_proc)(proc_t *buf), int(*want_task)(proc_t *buf), PROCTAB *restrict const PT);
+
+// Convenient wrapper around openproc and readproc to slurp in the whole process
+// table subset satisfying the constraints of flags and the optional PID list.
+// Free allocated memory with exit(). Access via tab[N]->member. The pointer
+// list is NULL terminated.
+
+extern proc_t** readproctab(int flags, ... /* same as openproc */ );
+
+// clean-up open files, etc from the openproc()
+extern void closeproc(PROCTAB* PT);
+
+// retrieve the next process matching the criteria set by the openproc()
+extern proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p);
+extern proc_t* readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict t);
+
+// warning: interface may change
+extern int read_cmdline(char *restrict const dst, unsigned sz, unsigned pid);
+
+extern void look_up_our_self(proc_t *p);
+
+// deallocate space allocated by readproc
+
+extern void freeproc(proc_t* p);
+
+//fill out a proc_t for a single task
+extern proc_t * get_proc_stats(pid_t pid, proc_t *p);
+
+// openproc/readproctab:
+//
+// Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be
+// opened.) By default readproc will consider all processes as valid to parse
+// and return, but not actually fill in the cmdline, environ, and /proc/#/statm
+// derived memory fields.
+//
+// `flags' (a bitwise-or of PROC_* below) modifies the default behavior. The
+// "fill" options will cause more of the proc_t to be filled in. The "filter"
+// options all use the second argument as the pointer to a list of objects:
+// process status', process id's, user id's. The third
+// argument is the length of the list (currently only used for lists of user
+// id's since uid_t supports no convenient termination sentinel.)
+
+#define PROC_FILLMEM 0x0001 // read statm
+#define PROC_FILLCOM 0x0002 // alloc and fill in `cmdline'
+#define PROC_FILLENV 0x0004 // alloc and fill in `environ'
+#define PROC_FILLUSR 0x0008 // resolve user id number -> user name
+#define PROC_FILLGRP 0x0010 // resolve group id number -> group name
+#define PROC_FILLSTATUS 0x0020 // read status -- currently unconditional
+#define PROC_FILLSTAT 0x0040 // read stat -- currently unconditional
+#define PROC_FILLWCHAN 0x0080 // look up WCHAN name
+#define PROC_FILLARG 0x0100 // alloc and fill in `cmdline'
+
+#define PROC_LOOSE_TASKS 0x0200 // threat threads as if they were processes
+
+// Obsolete, consider only processes with one of the passed:
+#define PROC_PID 0x1000 // process id numbers ( 0 terminated)
+#define PROC_UID 0x4000 // user id numbers ( length needed )
+
+// it helps to give app code a few spare bits
+#define PROC_SPARE_1 0x01000000
+#define PROC_SPARE_2 0x02000000
+#define PROC_SPARE_3 0x04000000
+#define PROC_SPARE_4 0x08000000
+
+EXTERN_C_END
+#endif
--- /dev/null
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sig.h"
+
+/* Linux signals:
+ *
+ * SIGSYS is required by Unix98.
+ * SIGEMT is part of SysV, BSD, and ancient UNIX tradition.
+ *
+ * They are provided by these Linux ports: alpha, mips, sparc, and sparc64.
+ * You get SIGSTKFLT and SIGUNUSED instead on i386, m68k, ppc, and arm.
+ * (this is a Linux & libc bug -- both must be fixed)
+ *
+ * Total garbage: SIGIO SIGINFO SIGIOT SIGLOST SIGCLD
+ * (popular ones are handled as aliases)
+ * Nearly garbage: SIGSTKFLT SIGUNUSED (nothing else to fill slots)
+ */
+
+/* Linux 2.3.29 replaces SIGUNUSED with the standard SIGSYS signal */
+#ifndef SIGSYS
+# warning Standards require that <signal.h> 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 <signal.h> define SIGRTMIN; assuming 32
+# define SIGRTMIN 32
+#endif
+
+/* It seems the SPARC libc does not know the kernel supports SIGPWR. */
+#ifndef SIGPWR
+# warning Your header files lack SIGPWR. (assuming it is number 29)
+# define SIGPWR 29
+#endif
+
+typedef struct mapstruct {
+ const char *name;
+ int num;
+} mapstruct;
+
+
+static const mapstruct sigtable[] = {
+ {"ABRT", SIGABRT}, /* IOT */
+ {"ALRM", SIGALRM},
+ {"BUS", SIGBUS},
+ {"CHLD", SIGCHLD}, /* CLD */
+ {"CONT", SIGCONT},
+#ifdef SIGEMT
+ {"EMT", SIGEMT},
+#endif
+ {"FPE", SIGFPE},
+ {"HUP", SIGHUP},
+ {"ILL", SIGILL},
+ {"INT", SIGINT},
+ {"KILL", SIGKILL},
+ {"PIPE", SIGPIPE},
+ {"POLL", SIGPOLL}, /* IO */
+ {"PROF", SIGPROF},
+ {"PWR", SIGPWR},
+ {"QUIT", SIGQUIT},
+ {"SEGV", SIGSEGV},
+#ifdef SIGSTKFLT
+ {"STKFLT", SIGSTKFLT},
+#endif
+ {"STOP", SIGSTOP},
+ {"SYS", SIGSYS}, /* UNUSED */
+ {"TERM", SIGTERM},
+ {"TRAP", SIGTRAP},
+ {"TSTP", SIGTSTP},
+ {"TTIN", SIGTTIN},
+ {"TTOU", SIGTTOU},
+ {"URG", SIGURG},
+ {"USR1", SIGUSR1},
+ {"USR2", SIGUSR2},
+ {"VTALRM", SIGVTALRM},
+ {"WINCH", SIGWINCH},
+ {"XCPU", SIGXCPU},
+ {"XFSZ", SIGXFSZ}
+};
+
+static const int number_of_signals = sizeof(sigtable)/sizeof(mapstruct);
+
+static int compare_signal_names(const void *a, const void *b){
+ return strcasecmp( ((const mapstruct*)a)->name, ((const mapstruct*)b)->name );
+}
+
+/* return -1 on failure */
+int signal_name_to_number(const char *restrict name){
+ long val;
+ int offset;
+
+ /* clean up name */
+ if(!strncasecmp(name,"SIG",3)) name += 3;
+
+ if(!strcasecmp(name,"CLD")) return SIGCHLD;
+ if(!strcasecmp(name,"IO")) return SIGPOLL;
+ if(!strcasecmp(name,"IOT")) return SIGABRT;
+
+ /* search the table */
+ {
+ const mapstruct ms = {name,0};
+ const mapstruct *restrict const ptr = bsearch(
+ &ms,
+ sigtable,
+ number_of_signals,
+ sizeof(mapstruct),
+ compare_signal_names
+ );
+ if(ptr) return ptr->num;
+ }
+
+ if(!strcasecmp(name,"RTMIN")) return SIGRTMIN;
+ if(!strcasecmp(name,"EXIT")) return 0;
+ if(!strcasecmp(name,"NULL")) return 0;
+
+ offset = 0;
+ if(!strncasecmp(name,"RTMIN+",6)){
+ name += 6;
+ offset = SIGRTMIN;
+ }
+
+ /* not found, so try as a number */
+ {
+ char *endp;
+ val = strtol(name,&endp,10);
+ if(*endp || endp==name) return -1; /* not valid */
+ }
+ if(val+SIGRTMIN>127) return -1; /* not valid */
+ return val+offset;
+}
+
+const char *signal_number_to_name(int signo){
+ static char buf[32];
+ int n = number_of_signals;
+ signo &= 0x7f; /* need to process exit values too */
+ while(n--){
+ if(sigtable[n].num==signo) return sigtable[n].name;
+ }
+ if(signo == SIGRTMIN) return "RTMIN";
+ if(signo) sprintf(buf, "RTMIN+%d", signo-SIGRTMIN);
+ else strcpy(buf,"0"); /* AIX has NULL; Solaris has EXIT */
+ return buf;
+}
+
+int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line){
+ char buf[1280]; /* 128 signals, "RTMIN+xx" is largest */
+ int ret = 0; /* to be used as exit code by caller */
+ int place = 0; /* position on this line */
+ int amt;
+ if(argc > 128) return 1;
+ while(argc--){
+ char tmpbuf[16];
+ const char *restrict const txt = *argv;
+ if(*txt >= '0' && *txt <= '9'){
+ long val;
+ char *endp;
+ val = strtol(txt,&endp,10);
+ if(*endp){
+ fprintf(stderr, "Signal \"%s\" not known.\n", txt);
+ ret = 1;
+ goto end;
+ }
+ amt = sprintf(tmpbuf, "%s", signal_number_to_name(val));
+ }else{
+ int sno;
+ sno = signal_name_to_number(txt);
+ if(sno == -1){
+ fprintf(stderr, "Signal \"%s\" not known.\n", txt);
+ ret = 1;
+ goto end;
+ }
+ amt = sprintf(tmpbuf, "%d", sno);
+ }
+
+ if(!place){
+ strcpy(buf,tmpbuf);
+ place = amt;
+ goto end;
+ }
+ if(amt+place+1 > max_line){
+ printf("%s\n", buf);
+ strcpy(buf,tmpbuf);
+ place = amt;
+ goto end;
+ }
+ sprintf(buf+place, " %s", tmpbuf);
+ place += amt+1;
+end:
+ argv++;
+ }
+ if(place) printf("%s\n", buf);
+ return ret;
+}
+
+void pretty_print_signals(void){
+ int i = 0;
+ while(++i <= number_of_signals){
+ int n;
+ n = printf("%2d %s", i, signal_number_to_name(i));
+ if(i%7) printf(" \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + n);
+ else printf("\n");
+ }
+ if((i-1)%7) printf("\n");
+}
+
+void unix_print_signals(void){
+ int pos = 0;
+ int i = 0;
+ while(++i <= number_of_signals){
+ if(i-1) printf("%c", (pos>73)?(pos=0,'\n'):(pos++,' ') );
+ pos += printf("%s", signal_number_to_name(i));
+ }
+ printf("\n");
+}
+
+/* sanity check */
+static int init_signal_list(void) __attribute__((constructor));
+static int init_signal_list(void){
+ if(number_of_signals != 31){
+ fprintf(stderr, "WARNING: %d signals -- adjust and recompile.\n", number_of_signals);
+ }
+ return 0;
+}
--- /dev/null
+#ifndef PROC_SIG_H
+#define PROC_SIG_H
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+/* return -1 on failure */
+extern int signal_name_to_number(const char *restrict name);
+
+extern const char *signal_number_to_name(int signo);
+
+extern int print_given_signals(int argc, const char *restrict const *restrict argv, int max_line);
+
+extern void pretty_print_signals(void);
+
+extern void unix_print_signals(void);
+
+EXTERN_C_END
+#endif
--- /dev/null
+/*
+ * slab.c - slab related functions for libproc
+ *
+ * Chris Rivera <cmrivera@ufl.edu>
+ * Robert Love <rml@tech9.net>
+ *
+ * This program is licensed under the GNU Library General Public License, v2
+ *
+ * Copyright (C) 2003 Chris Rivera
+ * Copyright 2004, Albert Cahalan
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "slab.h"
+#include "procps.h"
+
+#define SLABINFO_LINE_LEN 2048
+#define SLABINFO_VER_LEN 100
+#define SLABINFO_FILE "/proc/slabinfo"
+
+static struct slab_info *free_index;
+
+/*
+ * get_slabnode - allocate slab_info structures using a free list
+ *
+ * In the fast path, we simply return a node off the free list. In the slow
+ * list, we malloc() a new node. The free list is never automatically reaped,
+ * both for simplicity and because the number of slab caches is fairly
+ * constant.
+ */
+static struct slab_info *get_slabnode(void)
+{
+ struct slab_info *node;
+
+ if (free_index) {
+ node = free_index;
+ free_index = free_index->next;
+ } else {
+ node = malloc(sizeof(struct slab_info));
+ if (!node)
+ perror("malloc");
+ }
+
+ return node;
+}
+
+/*
+ * slab_badname_detect - return true if current slab was declared with
+ * whitespaces for instance
+ * FIXME :Other cases ?
+ */
+
+static int slab_badname_detect(const char *restrict buffer)
+{
+ int numberarea=0;
+ while (*buffer){
+ if((*buffer)==' ')
+ numberarea=1;
+ if(isalpha(*buffer)&&numberarea)
+ return 1;
+ buffer++;
+ }
+ return 0;
+}
+
+/*
+ * put_slabinfo - return all allocated nodes to the free list
+ */
+void put_slabinfo(struct slab_info *head)
+{
+ free_index = head;
+}
+
+/*
+ * free_slabinfo - deallocate the memory associated with each node in the
+ * slab_info linked list
+ */
+void free_slabinfo(struct slab_info *list)
+{
+ while (list) {
+ struct slab_info *temp = list->next;
+ free(list);
+ list = temp;
+ }
+}
+
+// parse_slabinfo20 - actual parse routine for slabinfo 2.x (2.6 kernels)
+// Note: difference between 2.0 and 2.1 is in the ": globalstat" part where version 2.1
+// has extra column <nodeallocs>. We don't use ": globalstat" part in both versions.
+//
+// Formats (we don't use "statistics" extensions)
+//
+// slabinfo - version: 2.1
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail>
+//
+// slabinfo - version: 2.1 (statistics)
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail> \
+// : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> <nodeallocs> \
+// : cpustat <allochit> <allocmiss> <freehit> <freemiss>
+//
+// slabinfo - version: 2.0
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail>
+//
+// slabinfo - version: 2.0 (statistics)
+// # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \
+// : tunables <batchcount> <limit> <sharedfactor> \
+// : slabdata <active_slabs> <num_slabs> <sharedavail> \
+// : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> \
+// : cpustat <allochit> <allocmiss> <freehit> <freemiss>
+static int parse_slabinfo20(struct slab_info **list, struct slab_stat *stats,
+ FILE *f)
+{
+ struct slab_info *curr = NULL, *prev = NULL;
+ char buffer[SLABINFO_LINE_LEN];
+ int entries = 0;
+ int page_size = getpagesize();
+
+ stats->min_obj_size = INT_MAX;
+ stats->max_obj_size = 0;
+
+ while (fgets(buffer, SLABINFO_LINE_LEN, f)) {
+ int assigned;
+
+ if (buffer[0] == '#')
+ continue;
+
+ curr = get_slabnode();
+ if (!curr)
+ break;
+
+ if (entries++ == 0)
+ *list = curr;
+ else
+ prev->next = curr;
+
+ assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN)
+ "s %d %d %d %d %d : tunables %*d %*d %*d : \
+ slabdata %d %d %*d", curr->name,
+ &curr->nr_active_objs, &curr->nr_objs,
+ &curr->obj_size, &curr->objs_per_slab,
+ &curr->pages_per_slab, &curr->nr_active_slabs,
+ &curr->nr_slabs);
+
+ if (assigned < 8) {
+ fprintf(stderr, "unrecognizable data in slabinfo!\n");
+ curr = NULL;
+ break;
+ }
+
+ if (curr->obj_size < stats->min_obj_size)
+ stats->min_obj_size = curr->obj_size;
+ if (curr->obj_size > stats->max_obj_size)
+ stats->max_obj_size = curr->obj_size;
+
+ curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size;
+
+ if (curr->nr_objs) {
+ curr->use = 100 * curr->nr_active_objs / curr->nr_objs;
+ stats->nr_active_caches++;
+ } else
+ curr->use = 0;
+
+ stats->nr_objs += curr->nr_objs;
+ stats->nr_active_objs += curr->nr_active_objs;
+ stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size;
+ stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size;
+ stats->nr_pages += curr->nr_slabs * curr->pages_per_slab;
+ stats->nr_slabs += curr->nr_slabs;
+ stats->nr_active_slabs += curr->nr_active_slabs;
+
+ prev = curr;
+ }
+
+ if (!curr) {
+ fprintf(stderr, "\rerror reading slabinfo!\n");
+ return 1;
+ }
+
+ curr->next = NULL;
+ stats->nr_caches = entries;
+ if (stats->nr_objs)
+ stats->avg_obj_size = stats->total_size / stats->nr_objs;
+
+ return 0;
+}
+
+/*
+ * parse_slabinfo11 - actual parsing routine for slabinfo 1.1 (2.4 kernels)
+ */
+static int parse_slabinfo11(struct slab_info **list, struct slab_stat *stats,
+ FILE *f)
+{
+ struct slab_info *curr = NULL, *prev = NULL;
+ char buffer[SLABINFO_LINE_LEN];
+ int entries = 0;
+ int page_size = getpagesize();
+
+ stats->min_obj_size = INT_MAX;
+ stats->max_obj_size = 0;
+
+ while (fgets(buffer, SLABINFO_LINE_LEN, f)) {
+ int assigned;
+
+ curr = get_slabnode();
+ if (!curr)
+ break;
+
+ if (entries++ == 0)
+ *list = curr;
+ else
+ prev->next = curr;
+
+ assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN)
+ "s %d %d %d %d %d %d",
+ curr->name, &curr->nr_active_objs,
+ &curr->nr_objs, &curr->obj_size,
+ &curr->nr_active_slabs, &curr->nr_slabs,
+ &curr->pages_per_slab);
+
+ if (assigned < 6) {
+ fprintf(stderr, "unrecognizable data in your slabinfo version 1.1\n\r");
+ if(slab_badname_detect(buffer))
+ fprintf(stderr, "Found an error in cache name at line %s\n", buffer);
+ curr = NULL;
+ break;
+ }
+
+ if (curr->obj_size < stats->min_obj_size)
+ stats->min_obj_size = curr->obj_size;
+ if (curr->obj_size > stats->max_obj_size)
+ stats->max_obj_size = curr->obj_size;
+
+ curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size;
+
+ if (curr->nr_objs) {
+ curr->use = 100 * curr->nr_active_objs / curr->nr_objs;
+ stats->nr_active_caches++;
+ } else
+ curr->use = 0;
+
+ if (curr->obj_size)
+ curr->objs_per_slab = curr->pages_per_slab *
+ page_size / curr->obj_size;
+
+ stats->nr_objs += curr->nr_objs;
+ stats->nr_active_objs += curr->nr_active_objs;
+ stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size;
+ stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size;
+ stats->nr_pages += curr->nr_slabs * curr->pages_per_slab;
+ stats->nr_slabs += curr->nr_slabs;
+ stats->nr_active_slabs += curr->nr_active_slabs;
+
+ prev = curr;
+ }
+
+ if (!curr) {
+ fprintf(stderr, "\rerror reading slabinfo!\n");
+ return 1;
+ }
+
+ curr->next = NULL;
+ stats->nr_caches = entries;
+ if (stats->nr_objs)
+ stats->avg_obj_size = stats->total_size / stats->nr_objs;
+
+ return 0;
+}
+
+/*
+ * parse_slabinfo10 - actual parsing routine for slabinfo 1.0 (2.2 kernels)
+ *
+ * Not yet implemented. Please feel free.
+ */
+static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats,
+ FILE *f)
+{
+ (void) list, (void) stats, (void) f;
+ fprintf(stderr, "slabinfo version 1.0 not yet supported\n");
+ return 1;
+}
+
+/*
+ * slabinfo - parse the system's slabinfo and fill out both a linked list of
+ * slab_info structures and the slab_stat structure
+ *
+ * The function returns zero on success, in which case 'list' and 'stats' are
+ * valid. Nonzero is returned on failure and the state of 'list' and 'stats'
+ * are undefined.
+ */
+int get_slabinfo(struct slab_info **list, struct slab_stat *stats)
+{
+ FILE *slabfile;
+ char buffer[SLABINFO_VER_LEN];
+ int major, minor, ret = 0;
+
+ slabfile = fopen(SLABINFO_FILE, "r");
+ if (!slabfile) {
+ perror("fopen " SLABINFO_FILE);
+ return 1;
+ }
+
+ if (!fgets(buffer, SLABINFO_VER_LEN, slabfile)) {
+ fprintf(stderr, "cannot read from slabinfo\n");
+ return 1;
+ }
+
+ if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) {
+ fprintf(stderr, "not the good old slabinfo we know\n");
+ return 1;
+ }
+
+ if (major == 2)
+ ret = parse_slabinfo20(list, stats, slabfile);
+ else if (major == 1 && minor == 1)
+ ret = parse_slabinfo11(list, stats, slabfile);
+ else if (major == 1 && minor == 0)
+ ret = parse_slabinfo10(list, stats, slabfile);
+ else {
+ fprintf(stderr, "unrecognizable slabinfo version\n");
+ return 1;
+ }
+
+ fclose(slabfile);
+
+ return ret;
+}
--- /dev/null
+#ifndef _PROC_SLAB_H
+#define _PROC_SLAB_H
+
+#define SLAB_INFO_NAME_LEN 64
+
+struct slab_info {
+ char name[SLAB_INFO_NAME_LEN]; /* name of this cache */
+ struct slab_info *next;
+ unsigned long cache_size; /* size of entire cache */
+ unsigned nr_objs; /* number of objects in this cache */
+ unsigned nr_active_objs; /* number of active objects */
+ unsigned obj_size; /* size of each object */
+ unsigned objs_per_slab; /* number of objects per slab */
+ unsigned pages_per_slab; /* number of pages per slab */
+ unsigned nr_slabs; /* number of slabs in this cache */
+ unsigned nr_active_slabs; /* number of active slabs */
+ unsigned use; /* percent full: total / active */
+};
+
+struct slab_stat {
+ unsigned long total_size; /* size of all objects */
+ unsigned long active_size; /* size of all active objects */
+ unsigned nr_objs; /* number of objects, among all caches */
+ unsigned nr_active_objs; /* number of active objects, among all caches */
+ unsigned nr_pages; /* number of pages consumed by all objects */
+ unsigned nr_slabs; /* number of slabs, among all caches */
+ unsigned nr_active_slabs; /* number of active slabs, among all caches */
+ unsigned nr_caches; /* number of caches */
+ unsigned nr_active_caches; /* number of active caches */
+ unsigned avg_obj_size; /* average object size */
+ unsigned min_obj_size; /* size of smallest object */
+ unsigned max_obj_size; /* size of largest object */
+};
+
+extern void put_slabinfo(struct slab_info *);
+extern void free_slabinfo(struct slab_info *);
+extern int get_slabinfo(struct slab_info **, struct slab_stat *);
+
+#endif /* _PROC_SLAB_H */
--- /dev/null
+#if 0
+#include "procps.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "procps.h"
+
+struct smap_entry {
+ unsigned KLONG start;
+ unsigned KLONG beyond;
+ long long offset;
+ char flags[8];
+ unsigned dev_major;
+ unsigned dev_minor;
+ unsigned long long inode;
+
+ unsigned long rss;
+ unsigned long pss;
+ unsigned long sclean;
+ unsigned long sdirty;
+ unsigned long pclean;
+ unsigned long pdirty;
+ unsigned long ref;
+ unsigned long swap;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This code will surely make normal programmers cry. I need speed though,
+// and /proc/*/smaps should make anybody cry. (WTF kind of brain damage...?)
+
+struct smap_summary {
+ unsigned long size;
+ unsigned long rss;
+ unsigned long pss;
+ unsigned long sclean;
+ unsigned long sdirty;
+ unsigned long pclean;
+ unsigned long pdirty;
+ unsigned long ref;
+ unsigned long swap;
+};
+
+struct ssjt {
+ char str[16];
+ int len;
+ int offset;
+};
+
+#define JTE(o,x) {#x,sizeof(#x)-1,o}
+
+void get_smap_sums(struct smap_summary *restrict ss, const char *restrict const filename){
+ static struct ssjt table[] = {
+ JTE(-1,default),
+ JTE( 1,Rss),
+ JTE(-1,default),
+ JTE( 2,Pss),
+ JTE( 8,Swap),
+ JTE( 5,Private_Clean),
+ JTE( 6,Private_Dirty),
+ JTE(-1,default),
+ JTE( 7,Referenced),
+ JTE(-1,default),
+ JTE( 0,Size),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default), // KernelPageSize would go here
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE( 4,Shared_Dirty),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE( 3,Shared_Clean),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ JTE(-1,default),
+ };
+ char buf[20480];
+ int p1 = 0;
+ int p2 = 0;
+ memset(ss,0,sizeof *ss);
+ int fd = open(filename,O_RDONLY);
+ if(fd==-1)
+ return;
+ for(;;){
+ char *nlp = memchr(buf+p1,'\n',p2-p1);
+ if(!nlp){
+ if(p1){
+ // the memmove should never do anything, because the
+ // kernel seems to give us the greatest number of
+ // complete lines of text that fit in a single page
+ // (and thus p2-p1 is zero)
+ memmove(buf,buf+p1,p2-p1);
+ p2 -= p1;
+ p1 = 0;
+ }
+ ssize_t rb = read(fd,buf+p1,sizeof buf - p1);
+ if(rb < 1)
+ break;
+ p2 += rb;
+ nlp = memchr(buf+p1,'\n',p2-p1);
+ if(!nlp)
+ break;
+ }
+ char *s = buf+p1;
+ int len = nlp-s;
+ p1 += len+1;
+ if(len<27)
+ continue;
+//printf("j <%13.13s>\n",s);
+ if(s[0]<'A' || s[0]>'Z')
+ continue;
+ unsigned hash = ( (s[8]&15) + (s[1]&15) ) ^ (s[0]&3);
+ hash &= 31;
+//printf("x %2d <%13.13s>\n",hash,s);
+ if(s[table[hash].len] != ':')
+ continue;
+//printf("y %2d <%13.13s>\n",hash,s);
+ if(memcmp(table[hash].str,s,table[hash].len))
+ continue;
+//printf("z %2d <%13.13s>\n",hash,s);
+ s += table[hash].len;
+ while(*++s==' ')
+ ;
+ unsigned long ul = 0;
+ for(;;){
+ char c = *s++;
+ if(c != ' '){
+ ul *= 10;
+ ul += c-'0';
+ continue;
+ }
+ break;
+ }
+// if(table[hash].offset == 2)
+// printf("Pss:%20lu kB\n",ul);
+ unsigned long *ulp = &ss->size + table[hash].offset;
+ *ulp += ul;
+// memcpy(ss+table[hash].offset*sizeof(unsigned long), &ul, sizeof(unsigned long));
+ }
+ close(fd);
+}
+
+int main(int argc, char *argv[]){
+ struct smap_summary ss;
+ get_smap_sums(&ss, argv[1]);
+ printf("%9lu\n",ss.size);
+ printf("%9lu\n",ss.rss);
+ printf("%9lu\n",ss.pss);
+ printf("%9lu\n",ss.sclean);
+ printf("%9lu\n",ss.sdirty);
+ printf("%9lu\n",ss.pclean);
+ printf("%9lu\n",ss.pdirty);
+ printf("%9lu\n",ss.ref);
+ printf("%9lu\n",ss.swap);
+ return 0;
+}
+#endif
--- /dev/null
+// Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com
+// Copyright 1998-2003 Albert Cahalan
+//
+// This file is placed under the conditions of the GNU Library
+// General Public License, version 2, or any later version.
+// See file COPYING for information on distribution conditions.
+//
+// File for parsing top-level /proc entities. */
+//
+// June 2003, Fabian Frederick, disk and slab info
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include "version.h"
+#include "sysinfo.h" /* include self to verify prototypes */
+
+#ifndef HZ
+#include <netinet/in.h> /* htons */
+#endif
+
+long smp_num_cpus; /* number of CPUs */
+
+#define BAD_OPEN_MESSAGE \
+"Error: /proc must be mounted\n" \
+" To mount /proc at boot you need an /etc/fstab line like:\n" \
+" /proc /proc proc defaults\n" \
+" In the meantime, run \"mount /proc /proc -t proc\"\n"
+
+#define STAT_FILE "/proc/stat"
+static int stat_fd = -1;
+#define UPTIME_FILE "/proc/uptime"
+static int uptime_fd = -1;
+#define LOADAVG_FILE "/proc/loadavg"
+static int loadavg_fd = -1;
+#define MEMINFO_FILE "/proc/meminfo"
+static int meminfo_fd = -1;
+#define VMINFO_FILE "/proc/vmstat"
+static int vminfo_fd = -1;
+
+// As of 2.6.24 /proc/meminfo seems to need 888 on 64-bit,
+// and would need 1258 if the obsolete fields were there.
+static char buf[2048];
+
+/* This macro opens filename only if necessary and seeks to 0 so
+ * that successive calls to the functions are more efficient.
+ * It also reads the current contents of the file into the global buf.
+ */
+#define FILE_TO_BUF(filename, fd) do{ \
+ static int local_n; \
+ if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
+ fputs(BAD_OPEN_MESSAGE, stderr); \
+ fflush(NULL); \
+ _exit(102); \
+ } \
+ lseek(fd, 0L, SEEK_SET); \
+ if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
+ perror(filename); \
+ fflush(NULL); \
+ _exit(103); \
+ } \
+ buf[local_n] = '\0'; \
+}while(0)
+
+/* evals 'x' twice */
+#define SET_IF_DESIRED(x,y) do{ if(x) *(x) = (y); }while(0)
+
+
+/***********************************************************************/
+int uptime(double *restrict uptime_secs, double *restrict idle_secs) {
+ double up=0, idle=0;
+ char *restrict savelocale;
+
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd);
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC,"C");
+ if (sscanf(buf, "%lf %lf", &up, &idle) < 2) {
+ setlocale(LC_NUMERIC,savelocale);
+ fputs("bad data in " UPTIME_FILE "\n", stderr);
+ return 0;
+ }
+ setlocale(LC_NUMERIC,savelocale);
+ SET_IF_DESIRED(uptime_secs, up);
+ SET_IF_DESIRED(idle_secs, idle);
+ return up; /* assume never be zero seconds in practice */
+}
+
+/***********************************************************************
+ * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
+ * is the kernel clock tick rate. One of these units is called a jiffy.
+ * The HZ value used in the kernel may vary according to hacker desire.
+ * According to Linus Torvalds, this is not true. He considers the values
+ * in /proc as being in architecture-dependant units that have no relation
+ * to the kernel clock tick rate. Examination of the kernel source code
+ * reveals that opinion as wishful thinking.
+ *
+ * In any case, we need the HZ constant as used in /proc. (the real HZ value
+ * may differ, but we don't care) There are several ways we could get HZ:
+ *
+ * 1. Include the kernel header file. If it changes, recompile this library.
+ * 2. Use the sysconf() function. When HZ changes, recompile the C library!
+ * 3. Ask the kernel. This is obviously correct...
+ *
+ * Linus Torvalds won't let us ask the kernel, because he thinks we should
+ * not know the HZ value. Oh well, we don't have to listen to him.
+ * Someone smuggled out the HZ value. :-)
+ *
+ * This code should work fine, even if Linus fixes the kernel to match his
+ * stated behavior. The code only fails in case of a partial conversion.
+ *
+ * Recent update: on some architectures, the 2.4 kernel provides an
+ * ELF note to indicate HZ. This may be for ARM or user-mode Linux
+ * support. This ought to be investigated. Note that sysconf() is still
+ * unreliable, because it doesn't return an error code when it is
+ * used with a kernel that doesn't support the ELF note. On some other
+ * architectures there may be a system call or sysctl() that will work.
+ */
+
+unsigned long long Hertz;
+
+static void old_Hertz_hack(void){
+ unsigned long long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */
+ double up_1, up_2, seconds;
+ unsigned long long jiffies;
+ unsigned h;
+ char *restrict savelocale;
+
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ do{
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_1);
+ /* uptime(&up_1, NULL); */
+ FILE_TO_BUF(STAT_FILE,stat_fd);
+ sscanf(buf, "cpu %Lu %Lu %Lu %Lu", &user_j, &nice_j, &sys_j, &other_j);
+ FILE_TO_BUF(UPTIME_FILE,uptime_fd); sscanf(buf, "%lf", &up_2);
+ /* uptime(&up_2, NULL); */
+ } while((long long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
+ setlocale(LC_NUMERIC, savelocale);
+ jiffies = user_j + nice_j + sys_j + other_j;
+ seconds = (up_1 + up_2) / 2;
+ h = (unsigned)( (double)jiffies/seconds/smp_num_cpus );
+ /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
+ switch(h){
+ case 9 ... 11 : Hertz = 10; break; /* S/390 (sometimes) */
+ case 18 ... 22 : Hertz = 20; break; /* user-mode Linux */
+ case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */
+ case 48 ... 52 : Hertz = 50; break;
+ case 58 ... 61 : Hertz = 60; break;
+ case 62 ... 65 : Hertz = 64; break; /* StrongARM /Shark */
+ case 95 ... 105 : Hertz = 100; break; /* normal Linux */
+ case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */
+ case 195 ... 204 : Hertz = 200; break; /* normal << 1 */
+ case 247 ... 252 : Hertz = 250; break;
+ case 253 ... 260 : Hertz = 256; break;
+ case 393 ... 408 : Hertz = 400; break; /* normal << 2 */
+ case 790 ... 808 : Hertz = 800; break; /* normal << 3 */
+ case 990 ... 1010 : Hertz = 1000; break; /* ARM */
+ case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */
+ case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */
+ default:
+#ifdef HZ
+ Hertz = (unsigned long long)HZ; /* <asm/param.h> */
+#else
+ /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
+ Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
+#endif
+ fprintf(stderr, "Unknown HZ value! (%d) Assume %Ld.\n", h, Hertz);
+ }
+}
+
+// same as: euid != uid || egid != gid
+#ifndef AT_SECURE
+#define AT_SECURE 23 // secure mode boolean (true if setuid, etc.)
+#endif
+
+#ifndef AT_CLKTCK
+#define AT_CLKTCK 17 // frequency of times()
+#endif
+
+#define NOTE_NOT_FOUND 42
+
+//extern char** environ;
+
+/* for ELF executables, notes are pushed before environment and args */
+static unsigned long find_elf_note(unsigned long findme){
+ unsigned long *ep = (unsigned long *)environ;
+ while(*ep++);
+ while(*ep){
+ if(ep[0]==findme) return ep[1];
+ ep+=2;
+ }
+ return NOTE_NOT_FOUND;
+}
+
+int have_privs;
+
+static int check_for_privs(void){
+ unsigned long rc = find_elf_note(AT_SECURE);
+ if(rc==NOTE_NOT_FOUND){
+ // not valid to run this code after UID or GID change!
+ // (if needed, may use AT_UID and friends instead)
+ rc = geteuid() != getuid() || getegid() != getgid();
+ }
+ return !!rc;
+}
+
+static void init_libproc(void) __attribute__((constructor));
+static void init_libproc(void){
+ have_privs = check_for_privs();
+ // ought to count CPUs in /proc/stat instead of relying
+ // on glibc, which foolishly tries to parse /proc/cpuinfo
+ //
+ // SourceForge has an old Alpha running Linux 2.2.20 that
+ // appears to have a non-SMP kernel on a 2-way SMP box.
+ // _SC_NPROCESSORS_CONF returns 2, resulting in HZ=512
+ // _SC_NPROCESSORS_ONLN returns 1, which should work OK
+ smp_num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if(smp_num_cpus<1) smp_num_cpus=1; /* SPARC glibc is buggy */
+
+ if(linux_version_code > LINUX_VERSION(2, 4, 0)){
+ Hertz = find_elf_note(AT_CLKTCK);
+ if(Hertz!=NOTE_NOT_FOUND) return;
+ fputs("2.4+ kernel w/o ELF notes? -- report this\n", stderr);
+ }
+ old_Hertz_hack();
+}
+
+#if 0
+/***********************************************************************
+ * The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we
+ * recover jiffies by adding up the 4 or 5 numbers we are given. SMP kernels
+ * (as of pre-2.4 era) can report idle time going backwards, perhaps due
+ * to non-atomic reads and updates. There is no locking for these values.
+ */
+#ifndef NAN
+#define NAN (-0.0)
+#endif
+#define JT unsigned long long
+void eight_cpu_numbers(double *restrict uret, double *restrict nret, double *restrict sret, double *restrict iret, double *restrict wret, double *restrict xret, double *restrict yret, double *restrict zret){
+ double tmp_u, tmp_n, tmp_s, tmp_i, tmp_w, tmp_x, tmp_y, tmp_z;
+ double scale; /* scale values to % */
+ static JT old_u, old_n, old_s, old_i, old_w, old_x, old_y, old_z;
+ JT new_u, new_n, new_s, new_i, new_w, new_x, new_y, new_z;
+ JT ticks_past; /* avoid div-by-0 by not calling too often :-( */
+
+ tmp_w = 0.0;
+ new_w = 0;
+ tmp_x = 0.0;
+ new_x = 0;
+ tmp_y = 0.0;
+ new_y = 0;
+ tmp_z = 0.0;
+ new_z = 0;
+
+ FILE_TO_BUF(STAT_FILE,stat_fd);
+ sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", &new_u, &new_n, &new_s, &new_i, &new_w, &new_x, &new_y, &new_z);
+ ticks_past = (new_u+new_n+new_s+new_i+new_w+new_x+new_y+new_z)-(old_u+old_n+old_s+old_i+old_w+old_x+old_y+old_z);
+ if(ticks_past){
+ scale = 100.0 / (double)ticks_past;
+ tmp_u = ( (double)new_u - (double)old_u ) * scale;
+ tmp_n = ( (double)new_n - (double)old_n ) * scale;
+ tmp_s = ( (double)new_s - (double)old_s ) * scale;
+ tmp_i = ( (double)new_i - (double)old_i ) * scale;
+ tmp_w = ( (double)new_w - (double)old_w ) * scale;
+ tmp_x = ( (double)new_x - (double)old_x ) * scale;
+ tmp_y = ( (double)new_y - (double)old_y ) * scale;
+ tmp_z = ( (double)new_z - (double)old_z ) * scale;
+ }else{
+ tmp_u = NAN;
+ tmp_n = NAN;
+ tmp_s = NAN;
+ tmp_i = NAN;
+ tmp_w = NAN;
+ tmp_x = NAN;
+ tmp_y = NAN;
+ tmp_z = NAN;
+ }
+ SET_IF_DESIRED(uret, tmp_u);
+ SET_IF_DESIRED(nret, tmp_n);
+ SET_IF_DESIRED(sret, tmp_s);
+ SET_IF_DESIRED(iret, tmp_i);
+ SET_IF_DESIRED(wret, tmp_w);
+ SET_IF_DESIRED(xret, tmp_x);
+ SET_IF_DESIRED(yret, tmp_y);
+ SET_IF_DESIRED(zret, tmp_z);
+ old_u=new_u;
+ old_n=new_n;
+ old_s=new_s;
+ old_i=new_i;
+ old_w=new_w;
+ old_x=new_x;
+ old_y=new_y;
+ old_z=new_z;
+}
+#undef JT
+#endif
+
+/***********************************************************************/
+void loadavg(double *restrict av1, double *restrict av5, double *restrict av15) {
+ double avg_1=0, avg_5=0, avg_15=0;
+ char *restrict savelocale;
+
+ FILE_TO_BUF(LOADAVG_FILE,loadavg_fd);
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) {
+ fputs("bad data in " LOADAVG_FILE "\n", stderr);
+ exit(1);
+ }
+ setlocale(LC_NUMERIC, savelocale);
+ SET_IF_DESIRED(av1, avg_1);
+ SET_IF_DESIRED(av5, avg_5);
+ SET_IF_DESIRED(av15, avg_15);
+}
+
+ static char buff[BUFFSIZE]; /* used in the procedures */
+/***********************************************************************/
+
+static void crash(const char *filename) {
+ perror(filename);
+ exit(EXIT_FAILURE);
+}
+
+/***********************************************************************/
+
+static void getrunners(unsigned int *restrict running, unsigned int *restrict blocked) {
+ struct direct *ent;
+ DIR *proc;
+
+ *running=0;
+ *blocked=0;
+
+ if((proc=opendir("/proc"))==NULL) crash("/proc");
+
+ while(( ent=readdir(proc) )) {
+ char tbuf[32];
+ char *cp;
+ int fd;
+ char c;
+
+ if (!isdigit(ent->d_name[0])) continue;
+ sprintf(tbuf, "/proc/%s/stat", ent->d_name);
+
+ fd = open(tbuf, O_RDONLY, 0);
+ if (fd == -1) continue;
+ memset(tbuf, '\0', sizeof tbuf); // didn't feel like checking read()
+ read(fd, tbuf, sizeof tbuf - 1); // need 32 byte buffer at most
+ close(fd);
+
+ cp = strrchr(tbuf, ')');
+ if(!cp) continue;
+ c = cp[2];
+
+ if (c=='R') {
+ (*running)++;
+ continue;
+ }
+ if (c=='D') {
+ (*blocked)++;
+ continue;
+ }
+ }
+ closedir(proc);
+}
+
+/***********************************************************************/
+
+void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz,
+ unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout,
+ unsigned *restrict intr, unsigned *restrict ctxt,
+ unsigned int *restrict running, unsigned int *restrict blocked,
+ unsigned int *restrict btime, unsigned int *restrict processes) {
+ static int fd;
+ unsigned long long llbuf = 0;
+ int need_vmstat_file = 0;
+ int need_proc_scan = 0;
+ const char* b;
+ buff[BUFFSIZE-1] = 0; /* ensure null termination in buffer */
+
+ if(fd){
+ lseek(fd, 0L, SEEK_SET);
+ }else{
+ fd = open("/proc/stat", O_RDONLY, 0);
+ if(fd == -1) crash("/proc/stat");
+ }
+ read(fd,buff,BUFFSIZE-1);
+ *intr = 0;
+ *ciow = 0; /* not separated out until the 2.5.41 kernel */
+ *cxxx = 0; /* not separated out until the 2.6.0-test4 kernel */
+ *cyyy = 0; /* not separated out until the 2.6.0-test4 kernel */
+ *czzz = 0; /* not separated out until the 2.6.11 kernel */
+
+ b = strstr(buff, "cpu ");
+ if(b) sscanf(b, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", cuse, cice, csys, cide, ciow, cxxx, cyyy, czzz);
+
+ b = strstr(buff, "page ");
+ if(b) sscanf(b, "page %lu %lu", pin, pout);
+ else need_vmstat_file = 1;
+
+ b = strstr(buff, "swap ");
+ if(b) sscanf(b, "swap %lu %lu", s_in, sout);
+ else need_vmstat_file = 1;
+
+ b = strstr(buff, "intr ");
+ if(b) sscanf(b, "intr %Lu", &llbuf);
+ *intr = llbuf;
+
+ b = strstr(buff, "ctxt ");
+ if(b) sscanf(b, "ctxt %Lu", &llbuf);
+ *ctxt = llbuf;
+
+ b = strstr(buff, "btime ");
+ if(b) sscanf(b, "btime %u", btime);
+
+ b = strstr(buff, "processes ");
+ if(b) sscanf(b, "processes %u", processes);
+
+ b = strstr(buff, "procs_running ");
+ if(b) sscanf(b, "procs_running %u", running);
+ else need_proc_scan = 1;
+
+ b = strstr(buff, "procs_blocked ");
+ if(b) sscanf(b, "procs_blocked %u", blocked);
+ else need_proc_scan = 1;
+
+ if(need_proc_scan){ /* Linux 2.5.46 (approximately) and below */
+ getrunners(running, blocked);
+ }
+
+ (*running)--; // exclude vmstat itself
+
+ if(need_vmstat_file){ /* Linux 2.5.40-bk4 and above */
+ vminfo();
+ *pin = vm_pgpgin;
+ *pout = vm_pgpgout;
+ *s_in = vm_pswpin;
+ *sout = vm_pswpout;
+ }
+}
+
+/***********************************************************************/
+/*
+ * Copyright 1999 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+typedef struct mem_table_struct {
+ const char *name; /* memory type name */
+ unsigned long *slot; /* slot in return struct */
+} mem_table_struct;
+
+static int compare_mem_table_structs(const void *a, const void *b){
+ return strcmp(((const mem_table_struct*)a)->name,((const mem_table_struct*)b)->name);
+}
+
+/* example data, following junk, with comments added:
+ *
+ * MemTotal: 61768 kB old
+ * MemFree: 1436 kB old
+ * MemShared: 0 kB old (now always zero; not calculated)
+ * Buffers: 1312 kB old
+ * Cached: 20932 kB old
+ * Active: 12464 kB new
+ * Inact_dirty: 7772 kB new
+ * Inact_clean: 2008 kB new
+ * Inact_target: 0 kB new
+ * Inact_laundry: 0 kB new, and might be missing too
+ * HighTotal: 0 kB
+ * HighFree: 0 kB
+ * LowTotal: 61768 kB
+ * LowFree: 1436 kB
+ * SwapTotal: 122580 kB old
+ * SwapFree: 60352 kB old
+ * Inactive: 20420 kB 2.5.41+
+ * Dirty: 0 kB 2.5.41+
+ * Writeback: 0 kB 2.5.41+
+ * Mapped: 9792 kB 2.5.41+
+ * Slab: 4564 kB 2.5.41+
+ * Committed_AS: 8440 kB 2.5.41+
+ * PageTables: 304 kB 2.5.41+
+ * ReverseMaps: 5738 2.5.41+
+ * SwapCached: 0 kB 2.5.??+
+ * HugePages_Total: 220 2.5.??+
+ * HugePages_Free: 138 2.5.??+
+ * Hugepagesize: 4096 kB 2.5.??+
+ */
+
+/* obsolete */
+unsigned long kb_main_shared;
+/* old but still kicking -- the important stuff */
+unsigned long kb_main_buffers;
+unsigned long kb_main_cached;
+unsigned long kb_main_free;
+unsigned long kb_main_total;
+unsigned long kb_swap_free;
+unsigned long kb_swap_total;
+/* recently introduced */
+unsigned long kb_high_free;
+unsigned long kb_high_total;
+unsigned long kb_low_free;
+unsigned long kb_low_total;
+/* 2.4.xx era */
+unsigned long kb_active;
+unsigned long kb_inact_laundry;
+unsigned long kb_inact_dirty;
+unsigned long kb_inact_clean;
+unsigned long kb_inact_target;
+unsigned long kb_swap_cached; /* late 2.4 and 2.6+ only */
+/* derived values */
+unsigned long kb_swap_used;
+unsigned long kb_main_used;
+/* 2.5.41+ */
+unsigned long kb_writeback;
+unsigned long kb_slab;
+unsigned long nr_reversemaps;
+unsigned long kb_committed_as;
+unsigned long kb_dirty;
+unsigned long kb_inactive;
+unsigned long kb_mapped;
+unsigned long kb_pagetables;
+// seen on a 2.6.x kernel:
+static unsigned long kb_vmalloc_chunk;
+static unsigned long kb_vmalloc_total;
+static unsigned long kb_vmalloc_used;
+// seen on 2.6.24-rc6-git12
+static unsigned long kb_anon_pages;
+static unsigned long kb_bounce;
+static unsigned long kb_commit_limit;
+static unsigned long kb_nfs_unstable;
+static unsigned long kb_swap_reclaimable;
+static unsigned long kb_swap_unreclaimable;
+
+void meminfo(void){
+ char namebuf[16]; /* big enough to hold any row name */
+ mem_table_struct findme = { namebuf, NULL};
+ mem_table_struct *found;
+ char *head;
+ char *tail;
+ static const mem_table_struct mem_table[] = {
+ {"Active", &kb_active}, // important
+ {"AnonPages", &kb_anon_pages},
+ {"Bounce", &kb_bounce},
+ {"Buffers", &kb_main_buffers}, // important
+ {"Cached", &kb_main_cached}, // important
+ {"CommitLimit", &kb_commit_limit},
+ {"Committed_AS", &kb_committed_as},
+ {"Dirty", &kb_dirty}, // kB version of vmstat nr_dirty
+ {"HighFree", &kb_high_free},
+ {"HighTotal", &kb_high_total},
+ {"Inact_clean", &kb_inact_clean},
+ {"Inact_dirty", &kb_inact_dirty},
+ {"Inact_laundry",&kb_inact_laundry},
+ {"Inact_target", &kb_inact_target},
+ {"Inactive", &kb_inactive}, // important
+ {"LowFree", &kb_low_free},
+ {"LowTotal", &kb_low_total},
+ {"Mapped", &kb_mapped}, // kB version of vmstat nr_mapped
+ {"MemFree", &kb_main_free}, // important
+ {"MemShared", &kb_main_shared}, // important, but now gone!
+ {"MemTotal", &kb_main_total}, // important
+ {"NFS_Unstable", &kb_nfs_unstable},
+ {"PageTables", &kb_pagetables}, // kB version of vmstat nr_page_table_pages
+ {"ReverseMaps", &nr_reversemaps}, // same as vmstat nr_page_table_pages
+ {"SReclaimable", &kb_swap_reclaimable}, // "swap reclaimable" (dentry and inode structures)
+ {"SUnreclaim", &kb_swap_unreclaimable},
+ {"Slab", &kb_slab}, // kB version of vmstat nr_slab
+ {"SwapCached", &kb_swap_cached},
+ {"SwapFree", &kb_swap_free}, // important
+ {"SwapTotal", &kb_swap_total}, // important
+ {"VmallocChunk", &kb_vmalloc_chunk},
+ {"VmallocTotal", &kb_vmalloc_total},
+ {"VmallocUsed", &kb_vmalloc_used},
+ {"Writeback", &kb_writeback}, // kB version of vmstat nr_writeback
+ };
+ const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct);
+
+ FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);
+
+ kb_inactive = ~0UL;
+
+ head = buf;
+ for(;;){
+ tail = strchr(head, ':');
+ if(!tail) break;
+ *tail = '\0';
+ if(strlen(head) >= sizeof(namebuf)){
+ head = tail+1;
+ goto nextline;
+ }
+ strcpy(namebuf,head);
+ found = bsearch(&findme, mem_table, mem_table_count,
+ sizeof(mem_table_struct), compare_mem_table_structs
+ );
+ head = tail+1;
+ if(!found) goto nextline;
+ *(found->slot) = strtoul(head,&tail,10);
+nextline:
+ tail = strchr(head, '\n');
+ if(!tail) break;
+ head = tail+1;
+ }
+ if(!kb_low_total){ /* low==main except with large-memory support */
+ kb_low_total = kb_main_total;
+ kb_low_free = kb_main_free;
+ }
+ if(kb_inactive==~0UL){
+ kb_inactive = kb_inact_dirty + kb_inact_clean + kb_inact_laundry;
+ }
+ kb_swap_used = kb_swap_total - kb_swap_free;
+ kb_main_used = kb_main_total - kb_main_free;
+}
+
+/*****************************************************************/
+
+/* read /proc/vminfo only for 2.5.41 and above */
+
+typedef struct vm_table_struct {
+ const char *name; /* VM statistic name */
+ unsigned long *slot; /* slot in return struct */
+} vm_table_struct;
+
+static int compare_vm_table_structs(const void *a, const void *b){
+ return strcmp(((const vm_table_struct*)a)->name,((const vm_table_struct*)b)->name);
+}
+
+// see include/linux/page-flags.h and mm/page_alloc.c
+unsigned long vm_nr_dirty; // dirty writable pages
+unsigned long vm_nr_writeback; // pages under writeback
+unsigned long vm_nr_pagecache; // pages in pagecache -- gone in 2.5.66+ kernels
+unsigned long vm_nr_page_table_pages;// pages used for pagetables
+unsigned long vm_nr_reverse_maps; // includes PageDirect
+unsigned long vm_nr_mapped; // mapped into pagetables
+unsigned long vm_nr_slab; // in slab
+unsigned long vm_pgpgin; // kB disk reads (same as 1st num on /proc/stat page line)
+unsigned long vm_pgpgout; // kB disk writes (same as 2nd num on /proc/stat page line)
+unsigned long vm_pswpin; // swap reads (same as 1st num on /proc/stat swap line)
+unsigned long vm_pswpout; // swap writes (same as 2nd num on /proc/stat swap line)
+unsigned long vm_pgalloc; // page allocations
+unsigned long vm_pgfree; // page freeings
+unsigned long vm_pgactivate; // pages moved inactive -> active
+unsigned long vm_pgdeactivate; // pages moved active -> inactive
+unsigned long vm_pgfault; // total faults (major+minor)
+unsigned long vm_pgmajfault; // major faults
+unsigned long vm_pgscan; // pages scanned by page reclaim
+unsigned long vm_pgrefill; // inspected by refill_inactive_zone
+unsigned long vm_pgsteal; // total pages reclaimed
+unsigned long vm_kswapd_steal; // pages reclaimed by kswapd
+// next 3 as defined by the 2.5.52 kernel
+unsigned long vm_pageoutrun; // times kswapd ran page reclaim
+unsigned long vm_allocstall; // times a page allocator ran direct reclaim
+unsigned long vm_pgrotated; // pages rotated to the tail of the LRU for immediate reclaim
+// seen on a 2.6.8-rc1 kernel, apparently replacing old fields
+static unsigned long vm_pgalloc_dma; //
+static unsigned long vm_pgalloc_high; //
+static unsigned long vm_pgalloc_normal; //
+static unsigned long vm_pgrefill_dma; //
+static unsigned long vm_pgrefill_high; //
+static unsigned long vm_pgrefill_normal; //
+static unsigned long vm_pgscan_direct_dma; //
+static unsigned long vm_pgscan_direct_high; //
+static unsigned long vm_pgscan_direct_normal; //
+static unsigned long vm_pgscan_kswapd_dma; //
+static unsigned long vm_pgscan_kswapd_high; //
+static unsigned long vm_pgscan_kswapd_normal; //
+static unsigned long vm_pgsteal_dma; //
+static unsigned long vm_pgsteal_high; //
+static unsigned long vm_pgsteal_normal; //
+// seen on a 2.6.8-rc1 kernel
+static unsigned long vm_kswapd_inodesteal; //
+static unsigned long vm_nr_unstable; //
+static unsigned long vm_pginodesteal; //
+static unsigned long vm_slabs_scanned; //
+
+void vminfo(void){
+ char namebuf[16]; /* big enough to hold any row name */
+ vm_table_struct findme = { namebuf, NULL};
+ vm_table_struct *found;
+ char *head;
+ char *tail;
+ static const vm_table_struct vm_table[] = {
+ {"allocstall", &vm_allocstall},
+ {"kswapd_inodesteal", &vm_kswapd_inodesteal},
+ {"kswapd_steal", &vm_kswapd_steal},
+ {"nr_dirty", &vm_nr_dirty}, // page version of meminfo Dirty
+ {"nr_mapped", &vm_nr_mapped}, // page version of meminfo Mapped
+ {"nr_page_table_pages", &vm_nr_page_table_pages},// same as meminfo PageTables
+ {"nr_pagecache", &vm_nr_pagecache}, // gone in 2.5.66+ kernels
+ {"nr_reverse_maps", &vm_nr_reverse_maps}, // page version of meminfo ReverseMaps GONE
+ {"nr_slab", &vm_nr_slab}, // page version of meminfo Slab
+ {"nr_unstable", &vm_nr_unstable},
+ {"nr_writeback", &vm_nr_writeback}, // page version of meminfo Writeback
+ {"pageoutrun", &vm_pageoutrun},
+ {"pgactivate", &vm_pgactivate},
+ {"pgalloc", &vm_pgalloc}, // GONE (now separate dma,high,normal)
+ {"pgalloc_dma", &vm_pgalloc_dma},
+ {"pgalloc_high", &vm_pgalloc_high},
+ {"pgalloc_normal", &vm_pgalloc_normal},
+ {"pgdeactivate", &vm_pgdeactivate},
+ {"pgfault", &vm_pgfault},
+ {"pgfree", &vm_pgfree},
+ {"pginodesteal", &vm_pginodesteal},
+ {"pgmajfault", &vm_pgmajfault},
+ {"pgpgin", &vm_pgpgin}, // important
+ {"pgpgout", &vm_pgpgout}, // important
+ {"pgrefill", &vm_pgrefill}, // GONE (now separate dma,high,normal)
+ {"pgrefill_dma", &vm_pgrefill_dma},
+ {"pgrefill_high", &vm_pgrefill_high},
+ {"pgrefill_normal", &vm_pgrefill_normal},
+ {"pgrotated", &vm_pgrotated},
+ {"pgscan", &vm_pgscan}, // GONE (now separate direct,kswapd and dma,high,normal)
+ {"pgscan_direct_dma", &vm_pgscan_direct_dma},
+ {"pgscan_direct_high", &vm_pgscan_direct_high},
+ {"pgscan_direct_normal",&vm_pgscan_direct_normal},
+ {"pgscan_kswapd_dma", &vm_pgscan_kswapd_dma},
+ {"pgscan_kswapd_high", &vm_pgscan_kswapd_high},
+ {"pgscan_kswapd_normal",&vm_pgscan_kswapd_normal},
+ {"pgsteal", &vm_pgsteal}, // GONE (now separate dma,high,normal)
+ {"pgsteal_dma", &vm_pgsteal_dma},
+ {"pgsteal_high", &vm_pgsteal_high},
+ {"pgsteal_normal", &vm_pgsteal_normal},
+ {"pswpin", &vm_pswpin}, // important
+ {"pswpout", &vm_pswpout}, // important
+ {"slabs_scanned", &vm_slabs_scanned},
+ };
+ const int vm_table_count = sizeof(vm_table)/sizeof(vm_table_struct);
+
+ vm_pgalloc = 0;
+ vm_pgrefill = 0;
+ vm_pgscan = 0;
+ vm_pgsteal = 0;
+
+ FILE_TO_BUF(VMINFO_FILE,vminfo_fd);
+
+ head = buf;
+ for(;;){
+ tail = strchr(head, ' ');
+ if(!tail) break;
+ *tail = '\0';
+ if(strlen(head) >= sizeof(namebuf)){
+ head = tail+1;
+ goto nextline;
+ }
+ strcpy(namebuf,head);
+ found = bsearch(&findme, vm_table, vm_table_count,
+ sizeof(vm_table_struct), compare_vm_table_structs
+ );
+ head = tail+1;
+ if(!found) goto nextline;
+ *(found->slot) = strtoul(head,&tail,10);
+nextline:
+
+//if(found) fprintf(stderr,"%s=%d\n",found->name,*(found->slot));
+//else fprintf(stderr,"%s not found\n",findme.name);
+
+ tail = strchr(head, '\n');
+ if(!tail) break;
+ head = tail+1;
+ }
+ if(!vm_pgalloc)
+ vm_pgalloc = vm_pgalloc_dma + vm_pgalloc_high + vm_pgalloc_normal;
+ if(!vm_pgrefill)
+ vm_pgrefill = vm_pgrefill_dma + vm_pgrefill_high + vm_pgrefill_normal;
+ if(!vm_pgscan)
+ vm_pgscan = vm_pgscan_direct_dma + vm_pgscan_direct_high + vm_pgscan_direct_normal
+ + vm_pgscan_kswapd_dma + vm_pgscan_kswapd_high + vm_pgscan_kswapd_normal;
+ if(!vm_pgsteal)
+ vm_pgsteal = vm_pgsteal_dma + vm_pgsteal_high + vm_pgsteal_normal;
+}
+
+///////////////////////////////////////////////////////////////////////
+// based on Fabian Frederick's /proc/diskstats parser
+
+
+unsigned int getpartitions_num(struct disk_stat *disks, int ndisks){
+ int i=0;
+ int partitions=0;
+
+ for (i=0;i<ndisks;i++){
+ partitions+=disks[i].partitions;
+ }
+ return partitions;
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+unsigned int getdiskstat(struct disk_stat **disks, struct partition_stat **partitions){
+ FILE* fd;
+ int cDisk = 0;
+ int cPartition = 0;
+ int fields;
+ unsigned dummy;
+
+ *disks = NULL;
+ *partitions = NULL;
+ buff[BUFFSIZE-1] = 0;
+ fd = fopen("/proc/diskstats", "rb");
+ if(!fd) crash("/proc/diskstats");
+
+ for (;;) {
+ if (!fgets(buff,BUFFSIZE-1,fd)){
+ fclose(fd);
+ break;
+ }
+ fields = sscanf(buff, " %*d %*d %*s %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u", &dummy);
+ if (fields == 1){
+ (*disks) = realloc(*disks, (cDisk+1)*sizeof(struct disk_stat));
+ sscanf(buff, " %*d %*d %15s %u %u %llu %u %u %u %llu %u %u %u %u",
+ //&disk_major,
+ //&disk_minor,
+ (*disks)[cDisk].disk_name,
+ &(*disks)[cDisk].reads,
+ &(*disks)[cDisk].merged_reads,
+ &(*disks)[cDisk].reads_sectors,
+ &(*disks)[cDisk].milli_reading,
+ &(*disks)[cDisk].writes,
+ &(*disks)[cDisk].merged_writes,
+ &(*disks)[cDisk].written_sectors,
+ &(*disks)[cDisk].milli_writing,
+ &(*disks)[cDisk].inprogress_IO,
+ &(*disks)[cDisk].milli_spent_IO,
+ &(*disks)[cDisk].weighted_milli_spent_IO
+ );
+ (*disks)[cDisk].partitions=0;
+ cDisk++;
+ }else{
+ (*partitions) = realloc(*partitions, (cPartition+1)*sizeof(struct partition_stat));
+ fflush(stdout);
+ sscanf(buff, " %*d %*d %15s %u %llu %u %u",
+ //&part_major,
+ //&part_minor,
+ (*partitions)[cPartition].partition_name,
+ &(*partitions)[cPartition].reads,
+ &(*partitions)[cPartition].reads_sectors,
+ &(*partitions)[cPartition].writes,
+ &(*partitions)[cPartition].requested_writes
+ );
+ (*partitions)[cPartition++].parent_disk = cDisk-1;
+ (*disks)[cDisk-1].partitions++;
+ }
+ }
+
+ return cDisk;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// based on Fabian Frederick's /proc/slabinfo parser
+
+unsigned int getslabinfo (struct slab_cache **slab){
+ FILE* fd;
+ int cSlab = 0;
+ buff[BUFFSIZE-1] = 0;
+ *slab = NULL;
+ fd = fopen("/proc/slabinfo", "rb");
+ if(!fd) crash("/proc/slabinfo");
+ while (fgets(buff,BUFFSIZE-1,fd)){
+ if(!memcmp("slabinfo - version:",buff,19)) continue; // skip header
+ if(*buff == '#') continue; // skip comments
+ (*slab) = realloc(*slab, (cSlab+1)*sizeof(struct slab_cache));
+ sscanf(buff, "%47s %u %u %u %u", // allow 47; max seen is 24
+ (*slab)[cSlab].name,
+ &(*slab)[cSlab].active_objs,
+ &(*slab)[cSlab].num_objs,
+ &(*slab)[cSlab].objsize,
+ &(*slab)[cSlab].objperslab
+ ) ;
+ cSlab++;
+ }
+ fclose(fd);
+ return cSlab;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+unsigned get_pid_digits(void){
+ char pidbuf[24];
+ char *endp;
+ long rc;
+ int fd;
+ static unsigned ret;
+
+ if(ret) goto out;
+ ret = 5;
+ fd = open("/proc/sys/kernel/pid_max", O_RDONLY);
+ if(fd==-1) goto out;
+ rc = read(fd, pidbuf, sizeof pidbuf);
+ close(fd);
+ if(rc<3) goto out;
+ pidbuf[rc] = '\0';
+ rc = strtol(pidbuf,&endp,10);
+ if(rc<42) goto out;
+ if(*endp && *endp!='\n') goto out;
+ rc--; // the pid_max value is really the max PID plus 1
+ ret = 0;
+ while(rc){
+ rc /= 10;
+ ret++;
+ }
+out:
+ return ret;
+}
--- /dev/null
+#ifndef PROC_SYSINFO_H
+#define PROC_SYSINFO_H
+#include <sys/types.h>
+#include <sys/dir.h>
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+extern unsigned long long Hertz; /* clock tick frequency */
+extern long smp_num_cpus; /* number of CPUs */
+extern int have_privs; /* boolean, true if setuid or similar */
+
+#if 0
+#define JT double
+extern void eight_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret, JT *wret, JT *xret, JT *yret, JT *zret);
+#undef JT
+#endif
+
+extern int uptime (double *uptime_secs, double *idle_secs);
+extern void loadavg(double *av1, double *av5, double *av15);
+
+
+/* obsolete */
+extern unsigned long kb_main_shared;
+/* old but still kicking -- the important stuff */
+extern unsigned long kb_main_buffers;
+extern unsigned long kb_main_cached;
+extern unsigned long kb_main_free;
+extern unsigned long kb_main_total;
+extern unsigned long kb_swap_free;
+extern unsigned long kb_swap_total;
+/* recently introduced */
+extern unsigned long kb_high_free;
+extern unsigned long kb_high_total;
+extern unsigned long kb_low_free;
+extern unsigned long kb_low_total;
+/* 2.4.xx era */
+extern unsigned long kb_active;
+extern unsigned long kb_inact_laundry; // grrr...
+extern unsigned long kb_inact_dirty;
+extern unsigned long kb_inact_clean;
+extern unsigned long kb_inact_target;
+extern unsigned long kb_swap_cached; /* late 2.4+ */
+/* derived values */
+extern unsigned long kb_swap_used;
+extern unsigned long kb_main_used;
+/* 2.5.41+ */
+extern unsigned long kb_writeback;
+extern unsigned long kb_slab;
+extern unsigned long nr_reversemaps;
+extern unsigned long kb_committed_as;
+extern unsigned long kb_dirty;
+extern unsigned long kb_inactive;
+extern unsigned long kb_mapped;
+extern unsigned long kb_pagetables;
+
+#define BUFFSIZE (64*1024)
+typedef unsigned long long jiff;
+extern void getstat(jiff *restrict cuse, jiff *restrict cice, jiff *restrict csys, jiff *restrict cide, jiff *restrict ciow, jiff *restrict cxxx, jiff *restrict cyyy, jiff *restrict czzz,
+ unsigned long *restrict pin, unsigned long *restrict pout, unsigned long *restrict s_in, unsigned long *restrict sout,
+ unsigned *restrict intr, unsigned *restrict ctxt,
+ unsigned int *restrict running, unsigned int *restrict blocked,
+ unsigned int *restrict btime, unsigned int *restrict processes);
+
+extern void meminfo(void);
+
+
+extern unsigned long vm_nr_dirty;
+extern unsigned long vm_nr_writeback;
+extern unsigned long vm_nr_pagecache;
+extern unsigned long vm_nr_page_table_pages;
+extern unsigned long vm_nr_reverse_maps;
+extern unsigned long vm_nr_mapped;
+extern unsigned long vm_nr_slab;
+extern unsigned long vm_pgpgin;
+extern unsigned long vm_pgpgout;
+extern unsigned long vm_pswpin;
+extern unsigned long vm_pswpout;
+extern unsigned long vm_pgalloc;
+extern unsigned long vm_pgfree;
+extern unsigned long vm_pgactivate;
+extern unsigned long vm_pgdeactivate;
+extern unsigned long vm_pgfault;
+extern unsigned long vm_pgmajfault;
+extern unsigned long vm_pgscan;
+extern unsigned long vm_pgrefill;
+extern unsigned long vm_pgsteal;
+extern unsigned long vm_kswapd_steal;
+extern unsigned long vm_pageoutrun;
+extern unsigned long vm_allocstall;
+
+extern void vminfo(void);
+
+typedef struct disk_stat{
+ unsigned long long reads_sectors;
+ unsigned long long written_sectors;
+ char disk_name [16];
+ unsigned inprogress_IO;
+ unsigned merged_reads;
+ unsigned merged_writes;
+ unsigned milli_reading;
+ unsigned milli_spent_IO;
+ unsigned milli_writing;
+ unsigned partitions;
+ unsigned reads;
+ unsigned weighted_milli_spent_IO;
+ unsigned writes;
+}disk_stat;
+
+typedef struct partition_stat{
+ char partition_name [16];
+ unsigned long long reads_sectors;
+ unsigned parent_disk; // index into a struct disk_stat array
+ unsigned reads;
+ unsigned writes;
+ unsigned requested_writes;
+}partition_stat;
+
+extern unsigned int getpartitions_num(struct disk_stat *disks, int ndisks);
+extern unsigned int getdiskstat (struct disk_stat**,struct partition_stat**);
+
+typedef struct slab_cache{
+ char name[48];
+ unsigned active_objs;
+ unsigned num_objs;
+ unsigned objsize;
+ unsigned objperslab;
+}slab_cache;
+
+extern unsigned int getslabinfo (struct slab_cache**);
+
+extern unsigned get_pid_digits(void) FUNCTION;
+
+EXTERN_C_END
+#endif /* SYSINFO_H */
--- /dev/null
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Ammended by cblake to only export the function symbol.
+ *
+ * Modified by Albert Cahalan, ????-2003
+ *
+ * Redistributable under the terms of the
+ * GNU Library General Public License; see COPYING
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "version.h"
+
+#ifdef MINORVERSION
+const char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION;
+#else
+const char procps_version[] = "procps version " VERSION "." SUBVERSION;
+#endif
+
+void display_version(void) {
+ fprintf(stdout, "%s\n", procps_version);
+}
+
+/* Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ */
+#include <sys/utsname.h>
+
+#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
+
+int linux_version_code;
+
+static void init_Linux_version(void) __attribute__((constructor));
+static void init_Linux_version(void) {
+ static struct utsname uts;
+ int x = 0, y = 0, z = 0; /* cleared in case sscanf() < 3 */
+
+ if (uname(&uts) == -1) /* failure implies impending death */
+ exit(1);
+ if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3)
+ fprintf(stderr, /* *very* unlikely to happen by accident */
+ "Non-standard uts for running kernel:\n"
+ "release %s=%d.%d.%d gives version code %d\n",
+ uts.release, x, y, z, LINUX_VERSION(x,y,z));
+ linux_version_code = LINUX_VERSION(x, y, z);
+}
--- /dev/null
+#ifndef PROC_VERSION_H
+#define PROC_VERSION_H
+
+#include "procps.h"
+
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ * Distributable under the terms of the GNU Library General Public License
+ *
+ * Copyright 2002 Albert Cahalan
+ */
+
+EXTERN_C_BEGIN
+
+extern void display_version(void); /* display suite version */
+extern const char procps_version[]; /* global buf for suite version */
+
+extern int linux_version_code; /* runtime version of LINUX_VERSION_CODE
+ in /usr/include/linux/version.h */
+
+/* Convenience macros for composing/decomposing version codes */
+#define LINUX_VERSION(x,y,z) (0x10000*(x) + 0x100*(y) + z)
+#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF)
+#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF)
+#define LINUX_VERSION_PATCH(x) ( (x) & 0xFF)
+
+EXTERN_C_END
+
+#endif /* PROC_VERSION_H */
--- /dev/null
+#ifndef PROCPS_PROC_WCHAN_H
+#define PROCPS_PROC_WCHAN_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+typedef void (*message_fn)(const char *restrict, ...) __attribute__((format(printf,1,2)));
+
+extern const char * lookup_wchan(unsigned KLONG address, unsigned pid);
+extern int open_psdb(const char *restrict override);
+extern int open_psdb_message(const char *restrict override, message_fn message);
+
+EXTERN_C_END
+
+#endif
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utmp.h>
+#include <sys/ioctl.h>
+#include "whattime.h"
+#include "sysinfo.h"
+
+static char buf[128];
+static double av[3];
+
+char *sprint_uptime(void) {
+ struct utmp *utmpstruct;
+ int upminutes, uphours, updays;
+ int pos;
+ struct tm *realtime;
+ time_t realseconds;
+ int numuser;
+ double uptime_secs, idle_secs;
+
+/* first get the current time */
+
+ time(&realseconds);
+ realtime = localtime(&realseconds);
+ pos = sprintf(buf, " %02d:%02d:%02d ",
+ realtime->tm_hour, realtime->tm_min, realtime->tm_sec);
+
+/* read and calculate the amount of uptime */
+
+ uptime(&uptime_secs, &idle_secs);
+
+ updays = (int) uptime_secs / (60*60*24);
+ strcat (buf, "up ");
+ pos += 3;
+ if (updays)
+ pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : "");
+ upminutes = (int) uptime_secs / 60;
+ uphours = upminutes / 60;
+ uphours = uphours % 24;
+ upminutes = upminutes % 60;
+ if(uphours)
+ pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes);
+ else
+ pos += sprintf(buf + pos, "%d min, ", upminutes);
+
+/* count the number of users */
+
+ numuser = 0;
+ setutent();
+ while ((utmpstruct = getutent())) {
+ if ((utmpstruct->ut_type == USER_PROCESS) &&
+ (utmpstruct->ut_name[0] != '\0'))
+ numuser++;
+ }
+ endutent();
+
+ pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s");
+
+ loadavg(&av[0], &av[1], &av[2]);
+
+ pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f",
+ av[0], av[1], av[2]);
+
+ return buf;
+}
+
+void print_uptime(void) {
+ printf("%s\n", sprint_uptime());
+}
--- /dev/null
+#ifndef PROC_WHATTIME_H
+#define PROC_WHATTIME_H
+
+#include "procps.h"
+
+EXTERN_C_BEGIN
+
+extern void print_uptime(void);
+extern char *sprint_uptime(void);
+
+EXTERN_C_END
+
+#endif
--- /dev/null
+Begin4
+Title: procps
+Version: 3.2.7
+Entered-date: 2006-06-25
+Description: Linux system utilities
+Keywords: procps /proc libproc sysctl pmap ps uptime tload slabtop
+ free w top vmstat watch skill snice kill pgrep pkill
+Author: Albert Cahalan, Michael K. Johnson, Jim Warner, etc.
+Maintained-by: various <procps-feedback@lists.sf.net>
+Primary-site: http://procps.sf.net/
+ 281kB procps-3.2.7.tar.gz
+Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html
+ 281kB procps-3.2.7.tar.gz
+Copying-policy: mixed
+End
--- /dev/null
+URL: http://procps.sf.net/
+Summary: System and process monitoring utilities
+Name: procps
+%define major_version 3
+%define minor_version 2
+%define revision 8
+%define version %{major_version}.%{minor_version}.%{revision}
+Version: %{version}
+Release: 1
+License: LGPL, GPL, BSD-like
+Group: Applications/System
+Source: http://procps.sf.net/procps-%{version}.tar.gz
+BuildRoot: %{_tmppath}/procps-root
+Packager: <procps-feedback@lists.sf.net>
+
+%description
+The procps package contains a set of system utilities which provide
+system information. Procps includes ps, free, sysctl, skill, snice,
+tload, top, uptime, vmstat, w, and watch. You need some of these.
+
+%prep
+%setup
+
+%build
+make SKIP="/bin/kill /usr/share/man/man1/kill.1" CFLAGS="$RPM_OPT_FLAGS"
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make SKIP="/bin/kill /usr/share/man/man1/kill.1" DESTDIR=$RPM_BUILD_ROOT ldconfig=echo install="install -D" lib="$RPM_BUILD_ROOT/%{_lib}/" install
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+# add libproc to the cache
+/sbin/ldconfig
+
+%files
+%defattr(0644,root,root,755)
+%doc NEWS BUGS TODO COPYING COPYING.LIB README.top README AUTHORS sysctl.conf
+%attr(555,root,root) /lib*/libproc*.so*
+%attr(555,root,root) /bin/*
+%attr(555,root,root) /sbin/*
+%attr(555,root,root) /usr/bin/*
+
+%attr(0644,root,root) /usr/share/man/man1/*
+%attr(0644,root,root) /usr/share/man/man5/*
+%attr(0644,root,root) /usr/share/man/man8/*
+
+%changelog
+* Fri Apr 14 09:23:45 PDT 2006 Jesse Brandeburg <jesse.brandeburg@in...>
+- fix missing trailing slash in %install to fix builds on x86_64
--- /dev/null
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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
+\f
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+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.
--- /dev/null
+Initially I only want to translate the --help output and man page.
+Common error messages would be next on the list. I want to avoid
+run-time overhead and bloat.
+
+Translations of the --help output should not be longer than 22 lines long.
+Feel free to leave out the less useful options to save space. (not even
+the English help text has all the options)
+
+I think these are the most important options:
+
+*** selection ***
+-C by command name list
+-G by real group ID list (supports names)
+-U by real user ID list (supports names)
+-u by effective user ID list (supports names)
+-e all processes
+-p by process ID list
+
+*** output ***
+--no-heading No header line.
+-o,o user-defined output
+-j,j job control format
+-l,l long format
+-f full format
+s signal format
+u user-oriented format
+--forest ASCII art forest (process hierarchy)
+c show true command name
+
+List of man page translators:
+
+de Wed Jan 10 19:09:15 2001 by Martin Schulze <joey@infodrom.ffis.de>
+es 19 Jan 1999 by Diego Sevilla Ruiz (dsevilla@ditec.um.es)
+fr 09/06/1997 par Christophe Blaess (ccb@club-internet.fr)
+hu Horv#th Andr#s (the '#' is 'a' w/ '/') <horvatha@rs1.szif.hu>
+it Traduzione in italiano di Giovanni Bortolozzo <borto@dei.unipd.it>
+it Revisione parziale di Daniele Giacomini <daniele@evo.it> 30/03/1999
+ja Tue Nov 14 2000 by NAKANO Takeo <nakano@apm.seikei.ac.jp>
+nl <manpages-nl@nl.linux.org>
--- /dev/null
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#ifndef PROCPS_PS_H
+#define PROCPS_PS_H
+
+#include "../proc/procps.h"
+#include "../proc/escape.h"
+#include "../proc/readproc.h"
+
+#if 0
+#define trace(args...) printf(## args)
+#else
+#define trace(args...)
+#endif
+
+
+/***************** GENERAL DEFINE ********************/
+
+
+/* selection list */
+#define SEL_RUID 1
+#define SEL_EUID 2
+#define SEL_SUID 3
+#define SEL_FUID 4
+#define SEL_RGID 5
+#define SEL_EGID 6
+#define SEL_SGID 7
+#define SEL_FGID 8
+#define SEL_PGRP 9
+#define SEL_PID 10
+#define SEL_TTY 11
+#define SEL_SESS 12
+#define SEL_COMM 13
+#define SEL_PPID 14
+
+/* Since an enum could be smashed by a #define, it would be bad. */
+#define U98 0 /* Unix98 standard */ /* This must be 0 */
+#define XXX 1 /* Common extension */
+#define DEC 2 /* Digital Unix */
+#define AIX 3 /* AIX */
+#define SCO 4 /* SCO */
+#define LNX 5 /* Linux original :-) */
+#define BSD 6 /* FreeBSD and OpenBSD */
+#define SUN 7 /* SunOS 5 (Solaris) */
+#define HPU 8 /* HP-UX */
+#define SGI 9 /* Irix */
+#define SOE 10 /* IBM's S/390 OpenEdition */
+#define TST 11 /* test code */
+
+/*
+ * Try not to overflow the output buffer:
+ * 32 pages for env+cmd
+ * 64 kB pages on IA-64
+ * 4 chars for "\377", or 1 when mangling to '?' (ESC_STRETCH)
+ * plus some slack for other stuff
+ * That is about 8.5 MB on IA-64, or 0.6 MB on i386
+ *
+ * Sadly, current kernels only supply one page of env/command data.
+ * The buffer is now protected with a guard page, and via other means
+ * to avoid hitting the guard page.
+ */
+
+/* output buffer size */
+#define OUTBUF_SIZE (2 * 64*1024 * ESC_STRETCH)
+
+/******************* PS DEFINE *******************/
+
+// Column flags
+// Justification control for flags field comes first.
+#define CF_JUST_MASK 0x0f
+// CF_AIXHACK 0
+#define CF_USER 1 // left if text, right if numeric
+#define CF_LEFT 2
+#define CF_RIGHT 3
+#define CF_UNLIMITED 4
+#define CF_WCHAN 5 // left if text, right if numeric
+#define CF_SIGNAL 6 // right in 9, or 16 if screen_cols>107
+// Then the other flags
+#define CF_PIDMAX 0x00000010 // react to pid_max
+// Only one allowed; use separate bits to catch errors.
+#define CF_PRINT_THREAD_ONLY 0x10000000
+#define CF_PRINT_PROCESS_ONLY 0x20000000
+#define CF_PRINT_EVERY_TIME 0x40000000
+#define CF_PRINT_AS_NEEDED 0x80000000 // means we have no clue, so assume EVERY TIME
+#define CF_PRINT_MASK 0xf0000000
+
+#define needs_for_select (PROC_FILLSTAT | PROC_FILLSTATUS)
+
+/* thread_flags */
+#define TF_B_H 0x0001
+#define TF_B_m 0x0002
+#define TF_U_m 0x0004
+#define TF_U_T 0x0008
+#define TF_U_L 0x0010
+#define TF_show_proc 0x0100 // show the summary line
+#define TF_show_task 0x0200 // show the per-thread lines
+#define TF_show_both 0x0400 // distinct proc/task format lists
+#define TF_loose_tasks 0x0800 // let sorting break up task groups (BSDish)
+#define TF_no_sort 0x1000 // don't know if thread-grouping should survive a sort
+#define TF_no_forest 0x2000 // don't see how to do threads w/ forest option
+#define TF_must_use 0x4000 // options only make sense if LWP/SPID column added
+
+/* personality control flags */
+#define PER_BROKEN_o 0x0001
+#define PER_BSD_h 0x0002
+#define PER_BSD_m 0x0004
+#define PER_IRIX_l 0x0008
+#define PER_FORCE_BSD 0x0010
+#define PER_GOOD_o 0x0020
+#define PER_OLD_m 0x0040
+#define PER_NO_DEFAULT_g 0x0080
+#define PER_ZAP_ADDR 0x0100
+#define PER_SANE_USER 0x0200
+#define PER_HPUX_x 0x0400
+#define PER_SVR4_x 0x0800
+#define PER_BSD_COLS 0x1000
+#define PER_UNIX_COLS 0x2000
+
+/* Simple selections by bit mask */
+#define SS_B_x 0x01
+#define SS_B_g 0x02
+#define SS_U_d 0x04
+#define SS_U_a 0x08
+#define SS_B_a 0x10
+
+/* predefined format flags such as: -l -f l u s -j */
+#define FF_Uf 0x0001 /* -f */
+#define FF_Uj 0x0002 /* -j */
+#define FF_Ul 0x0004 /* -l */
+#define FF_Bj 0x0008 /* j */
+#define FF_Bl 0x0010 /* l */
+#define FF_Bs 0x0020 /* s */
+#define FF_Bu 0x0040 /* u */
+#define FF_Bv 0x0080 /* v */
+#define FF_LX 0x0100 /* X */
+#define FF_Lm 0x0200 /* m */ /* overloaded: threads, sort, format */
+#define FF_Fc 0x0400 /* --context */ /* Flask security context format */
+
+/* predefined format modifier flags such as: -l -f l u s -j */
+#define FM_c 0x0001 /* -c */
+#define FM_j 0x0002 /* -j */ /* only set when !sysv_j_format */
+#define FM_y 0x0004 /* -y */
+//#define FM_L 0x0008 /* -L */
+#define FM_P 0x0010 /* -P */
+#define FM_M 0x0020 /* -M */
+//#define FM_T 0x0040 /* -T */
+#define FM_F 0x0080 /* -F */ /* -F also sets the regular -f flags */
+
+/* sorting & formatting */
+/* U,B,G is Unix,BSD,Gnu and then there is the option itself */
+#define SF_U_O 1
+#define SF_U_o 2
+#define SF_B_O 3
+#define SF_B_o 4
+#define SF_B_m 5 /* overloaded: threads, sort, format */
+#define SF_G_sort 6
+#define SF_G_format 7
+
+/* headers */
+#define HEAD_SINGLE 0 /* default, must be 0 */
+#define HEAD_NONE 1
+#define HEAD_MULTI 2
+
+
+/********************** GENERAL TYPEDEF *******************/
+
+/* Other fields that might be useful:
+ *
+ * char *name; user-defined column name (format specification)
+ * int reverse; sorting in reverse (sort specification)
+ *
+ * name in place of u
+ * reverse in place of n
+ */
+
+typedef union sel_union {
+ pid_t pid;
+ pid_t ppid;
+ uid_t uid;
+ gid_t gid;
+ dev_t tty;
+ char cmd[16]; /* this is _not_ \0 terminated */
+} sel_union;
+
+typedef struct selection_node {
+ struct selection_node *next;
+ sel_union *u; /* used if selection type has a list of values */
+ int n; /* used if selection type has a list of values */
+ int typecode;
+} selection_node;
+
+typedef struct sort_node {
+ struct sort_node *next;
+ int (*sr)(const proc_t* P, const proc_t* Q); /* sort function */
+ int reverse; /* can sort backwards */
+ int typecode;
+ int need;
+} sort_node;
+
+typedef struct format_node {
+ struct format_node *next;
+ char *name; /* user can override default name */
+ int (*pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function
+/* int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */
+ int width;
+ int need;
+ int vendor; /* Vendor that invented this */
+ int flags;
+ int typecode;
+} format_node;
+
+typedef struct format_struct {
+ const char *spec; /* format specifier */
+ const char *head; /* default header in the POSIX locale */
+ int (* const pr)(char *restrict const outbuf, const proc_t *restrict const pp); // print function
+ int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */
+ const int width;
+ const int need; /* data we will need (files to read, etc.) */
+ const int vendor; /* Where does this come from? */
+ const int flags;
+} format_struct;
+
+/* though ps-specific, needed by general file */
+typedef struct macro_struct {
+ const char *spec; /* format specifier */
+ const char *head; /* default header in the POSIX locale */
+} macro_struct;
+
+/**************** PS TYPEDEF ***********************/
+
+typedef struct aix_struct {
+ const int desc; /* 1-character format code */
+ const char *spec; /* format specifier */
+ const char *head; /* default header in the POSIX locale */
+} aix_struct;
+
+typedef struct shortsort_struct {
+ const int desc; /* 1-character format code */
+ const char *spec; /* format specifier */
+} shortsort_struct;
+
+/* Save these options for later: -o o -O O --format --sort */
+typedef struct sf_node {
+ struct sf_node *next; /* next arg */
+ format_node *f_cooked; /* convert each arg alone, then merge */
+ sort_node *s_cooked; /* convert each arg alone, then merge */
+ char *sf;
+ int sf_code;
+} sf_node;
+
+/********************* UNDECIDED GLOBALS **************/
+
+/* output.c */
+extern void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt);
+extern void print_format_specifiers(void);
+extern const aix_struct *search_aix_array(const int findme);
+extern const shortsort_struct *search_shortsort_array(const int findme);
+extern const format_struct *search_format_array(const char *findme);
+extern const macro_struct *search_macro_array(const char *findme);
+extern void init_output(void);
+extern int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp);
+
+/* global.c */
+extern void reset_global(void);
+
+/* global.c */
+extern int all_processes;
+extern const char *bsd_j_format;
+extern const char *bsd_l_format;
+extern const char *bsd_s_format;
+extern const char *bsd_u_format;
+extern const char *bsd_v_format;
+extern int bsd_c_option;
+extern int bsd_e_option;
+extern uid_t cached_euid;
+extern dev_t cached_tty;
+extern char forest_prefix[4 * 32*1024 + 100];
+extern int forest_type;
+extern unsigned format_flags; /* -l -f l u s -j... */
+extern format_node *format_list; /* digested formatting options */
+extern unsigned format_modifiers; /* -c -j -y -P -L... */
+extern int header_gap;
+extern int header_type; /* none, single, multi... */
+extern int include_dead_children;
+extern int lines_to_next_header;
+extern int max_line_width;
+extern const char *namelist_file;
+extern int negate_selection;
+extern int page_size; // "int" for math reasons?
+extern unsigned personality;
+extern int prefer_bsd_defaults;
+extern int running_only;
+extern int screen_cols;
+extern int screen_rows;
+extern unsigned long seconds_since_boot;
+extern selection_node *selection_list;
+extern unsigned simple_select;
+extern sort_node *sort_list;
+extern const char *sysv_f_format;
+extern const char *sysv_fl_format;
+extern const char *sysv_j_format;
+extern const char *sysv_l_format;
+extern unsigned thread_flags;
+extern int unix_f_option;
+extern int user_is_number;
+extern int wchan_is_number;
+
+/************************* PS GLOBALS *********************/
+
+/* sortformat.c */
+extern int defer_sf_option(const char *arg, int source);
+extern const char *process_sf_options(int localbroken);
+extern void reset_sortformat(void);
+
+/* select.c */
+extern int want_this_proc(proc_t *buf);
+extern const char *select_bits_setup(void);
+
+/* help.c */
+extern const char *help_message;
+
+/* global.c */
+extern void self_info(void);
+
+/* parser.c */
+extern int arg_parse(int argc, char *argv[]);
+
+#endif
--- /dev/null
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#if (__GNU_LIBRARY__ >= 6)
+# include <locale.h>
+#endif
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* major/minor number */
+#include <sys/sysmacros.h>
+
+#include <signal.h> /* catch signals */
+
+#include "common.h"
+#include "../proc/wchan.h"
+#include "../proc/version.h"
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "../proc/sig.h"
+
+#ifndef SIGCHLD
+#define SIGCHLD SIGCLD
+#endif
+
+/* just reports a crash */
+static void signal_handler(int signo){
+ if(signo==SIGPIPE) _exit(0); /* "ps | head" will cause this */
+ /* fprintf() is not reentrant, but we _exit() anyway */
+ fprintf(stderr,
+ "\n\n"
+ "Signal %d (%s) caught by ps (%s).\n"
+ "Please send bug reports to <feedback@lists.sf.net> or <albert@users.sf.net>\n",
+ signo,
+ signal_number_to_name(signo),
+ procps_version
+ );
+ _exit(signo+128);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+#undef DEBUG
+#ifdef DEBUG
+void init_stack_trace(char *prog_name);
+
+#include <ctype.h>
+
+void hex_dump(void *vp){
+ char *charlist;
+ int i = 0;
+ int line = 45;
+ char *cp = (char *)vp;
+
+ while(line--){
+ printf("%8lx ", (unsigned long)cp);
+ charlist = cp;
+ cp += 16;
+ for(i=0; i<16; i++){
+ if((charlist[i]>31) && (charlist[i]<127)){
+ printf("%c", charlist[i]);
+ }else{
+ printf(".");
+ }
+ }
+ printf(" ");
+ for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i])));
+ printf("\n");
+ i=0;
+ }
+}
+
+static void show_tgid(char *s, int n, sel_union *data){
+ printf("%s ", s);
+ while(--n){
+ printf("%d,", data[n].tgid);
+ }
+ printf("%d\n", data[0].tgid);
+}
+
+static void show_uid(char *s, int n, sel_union *data){
+ struct passwd *pw_data;
+ printf("%s ", s);
+ while(--n){
+ pw_data = getpwuid(data[n].uid);
+ if(pw_data) printf("%s,", pw_data->pw_name);
+ else printf("%d,", data[n].uid);
+ }
+ pw_data = getpwuid(data[n].uid);
+ if(pw_data) printf("%s\n", pw_data->pw_name);
+ else printf("%d\n", data[n].uid);
+}
+
+static void show_gid(char *s, int n, sel_union *data){
+ struct group *gr_data;
+ printf("%s ", s);
+ while(--n){
+ gr_data = getgrgid(data[n].gid);
+ if(gr_data) printf("%s,", gr_data->gr_name);
+ else printf("%d,", data[n].gid);
+ }
+ gr_data = getgrgid(data[n].gid);
+ if(gr_data) printf("%s\n", gr_data->gr_name);
+ else printf("%d\n", data[n].gid);
+}
+
+static void show_tty(char *s, int n, sel_union *data){
+ printf("%s ", s);
+ while(--n){
+ printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty));
+ }
+ printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty));
+}
+
+static void show_cmd(char *s, int n, sel_union *data){
+ printf("%s ", s);
+ while(--n){
+ printf("%.8s,", data[n].cmd);
+ }
+ printf("%.8s\n", data[0].cmd);
+}
+
+static void arg_show(void){
+ selection_node *walk = selection_list;
+ while(walk){
+ switch(walk->typecode){
+ case SEL_RUID: show_uid("RUID", walk->n, walk->u); break;
+ case SEL_EUID: show_uid("EUID", walk->n, walk->u); break;
+ case SEL_SUID: show_uid("SUID", walk->n, walk->u); break;
+ case SEL_FUID: show_uid("FUID", walk->n, walk->u); break;
+ case SEL_RGID: show_gid("RGID", walk->n, walk->u); break;
+ case SEL_EGID: show_gid("EGID", walk->n, walk->u); break;
+ case SEL_SGID: show_gid("SGID", walk->n, walk->u); break;
+ case SEL_FGID: show_gid("FGID", walk->n, walk->u); break;
+ case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break;
+ case SEL_PID : show_pid("PID ", walk->n, walk->u); break;
+ case SEL_PPID: show_pid("PPID", walk->n, walk->u); break;
+ case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break;
+ case SEL_SESS: show_pid("SESS", walk->n, walk->u); break;
+ case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break;
+ default: printf("Garbage typecode value!\n");
+ }
+ walk = walk->next;
+ }
+}
+
+#endif
+//////////////////////////////////////////////////////////////////////////
+
+
+/***** check the header */
+/* Unix98: must not print empty header */
+static void check_headers(void){
+ format_node *walk = format_list;
+ int head_normal = 0;
+ if(header_type==HEAD_MULTI){
+ header_gap = screen_rows-1; /* true BSD */
+ return;
+ }
+ if(header_type==HEAD_NONE){
+ lines_to_next_header = -1; /* old Linux */
+ return;
+ }
+ while(walk){
+ if(!*(walk->name)){
+ walk = walk->next;
+ continue;
+ }
+ if(walk->pr){
+ head_normal++;
+ walk = walk->next;
+ continue;
+ }
+ walk = walk->next;
+ }
+ if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */
+}
+
+/***** check sort needs */
+/* see what files need to be read, etc. */
+static unsigned check_sort_needs(sort_node *walk){
+ unsigned needs = 0;
+ while(walk){
+ needs |= walk->need;
+ walk = walk->next;
+ }
+ return needs;
+}
+
+/***** check needs */
+/* see what files need to be read, etc. */
+static unsigned collect_format_needs(format_node *walk){
+ unsigned needs = 0;
+ while(walk){
+ needs |= walk->need;
+ walk = walk->next;
+ }
+ return needs;
+}
+
+static format_node *proc_format_list;
+static format_node *task_format_list;
+
+static unsigned needs_for_threads;
+static unsigned needs_for_sort;
+static unsigned proc_format_needs;
+static unsigned task_format_needs;
+
+#define needs_for_format (proc_format_needs|task_format_needs)
+
+#define PROC_ONLY_FLAGS (PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM)
+
+/***** munge lists and determine openproc() flags */
+static void lists_and_needs(void){
+ check_headers();
+
+ // only care about the difference when showing both
+ if(thread_flags & TF_show_both){
+ format_node pfn, tfn; // junk, to handle special case at begin of list
+ format_node *walk = format_list;
+ format_node *p_end = &pfn;
+ format_node *t_end = &tfn;
+ while(walk){
+ format_node *new = malloc(sizeof(format_node));
+ memcpy(new,walk,sizeof(format_node));
+ p_end->next = walk;
+ t_end->next = new;
+ p_end = walk;
+ t_end = new;
+ switch(walk->flags & CF_PRINT_MASK){
+ case CF_PRINT_THREAD_ONLY:
+ p_end->pr = pr_nop;
+ p_end->need = 0;
+ break;
+ case CF_PRINT_PROCESS_ONLY:
+ t_end->pr = pr_nop;
+ t_end->need = 0;
+ break;
+ default:
+ fprintf(stderr, "please report this bug\n");
+ // FALL THROUGH
+ case CF_PRINT_AS_NEEDED:
+ case CF_PRINT_EVERY_TIME:
+ break;
+ }
+ walk = walk->next;
+ }
+ t_end->next = NULL;
+ p_end->next = NULL;
+ proc_format_list = pfn.next;
+ task_format_list = tfn.next;
+ }else{
+ proc_format_list = format_list;
+ task_format_list = format_list;
+ }
+
+ proc_format_needs = collect_format_needs(proc_format_list);
+ task_format_needs = collect_format_needs(task_format_list);
+
+ needs_for_sort = check_sort_needs(sort_list);
+
+ // move process-only flags to the process
+ proc_format_needs |= (task_format_needs &~ PROC_ONLY_FLAGS);
+ task_format_needs &= ~PROC_ONLY_FLAGS;
+
+ if(bsd_c_option){
+ proc_format_needs &= ~PROC_FILLARG;
+ needs_for_sort &= ~PROC_FILLARG;
+ }
+ if(!unix_f_option){
+ proc_format_needs &= ~PROC_FILLCOM;
+ needs_for_sort &= ~PROC_FILLCOM;
+ }
+ // convert ARG to COM as a standard
+ if(proc_format_needs & PROC_FILLARG){
+ proc_format_needs |= PROC_FILLCOM;
+ proc_format_needs &= ~PROC_FILLARG;
+ }
+ if(bsd_e_option){
+ if(proc_format_needs&PROC_FILLCOM) proc_format_needs |= PROC_FILLENV;
+ }
+
+ /* FIXME broken filthy hack -- got to unify some stuff here */
+ if( ( (proc_format_needs|task_format_needs|needs_for_sort) & PROC_FILLWCHAN) && !wchan_is_number)
+ if (open_psdb(namelist_file)) wchan_is_number = 1;
+
+ if(thread_flags&TF_loose_tasks) needs_for_threads |= PROC_LOOSE_TASKS;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+/***** fill in %CPU; not in libproc because of include_dead_children */
+/* Note: for sorting, not display, so 0..0x7fffffff would be OK */
+static int want_this_proc_pcpu(proc_t *buf){
+ unsigned long long used_jiffies;
+ unsigned long pcpu = 0;
+ unsigned long long avail_jiffies;
+
+ if(!want_this_proc(buf)) return 0;
+
+ used_jiffies = buf->utime + buf->stime;
+ if(include_dead_children) used_jiffies += (buf->cutime + buf->cstime);
+
+ avail_jiffies = seconds_since_boot * Hertz - buf->start_time;
+ if(avail_jiffies) pcpu = (used_jiffies << 24) / avail_jiffies;
+
+ buf->pcpu = pcpu; // fits in an int, summing children on 128 CPUs
+
+ return 1;
+}
+
+/***** just display */
+static void simple_spew(void){
+ proc_t buf;
+ PROCTAB* ptp;
+ ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
+ if(!ptp) {
+ fprintf(stderr, "Error: can not access /proc.\n");
+ exit(1);
+ }
+ memset(&buf, '#', sizeof(proc_t));
+ switch(thread_flags & (TF_show_proc|TF_loose_tasks|TF_show_task)){
+ case TF_show_proc: // normal non-thread output
+ while(readproc(ptp,&buf)){
+ if(want_this_proc(&buf)){
+ show_one_proc(&buf, proc_format_list);
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ }
+ break;
+ case TF_show_proc|TF_loose_tasks: // H option
+ while(readproc(ptp,&buf)){
+ proc_t buf2;
+ // must still have the process allocated
+ while(readtask(ptp,&buf,&buf2)){
+ if(!want_this_proc(&buf)) continue;
+ show_one_proc(&buf2, task_format_list);
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ }
+ break;
+ case TF_show_proc|TF_show_task: // m and -m options
+ while(readproc(ptp,&buf)){
+ if(want_this_proc(&buf)){
+ proc_t buf2;
+ show_one_proc(&buf, proc_format_list);
+ // must still have the process allocated
+ while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ }
+ break;
+ case TF_show_task: // -L and -T options
+ while(readproc(ptp,&buf)){
+ if(want_this_proc(&buf)){
+ proc_t buf2;
+ // must still have the process allocated
+ while(readtask(ptp,&buf,&buf2)) show_one_proc(&buf2, task_format_list);
+ }
+ if(buf.cmdline) free((void*)*buf.cmdline); // ought to reuse
+ if(buf.environ) free((void*)*buf.environ); // ought to reuse
+ }
+ break;
+ }
+ closeproc(ptp);
+}
+
+/***** forest output requires sorting by ppid; add start_time by default */
+static void prep_forest_sort(void){
+ sort_node *tmp_list = sort_list;
+ const format_struct *incoming;
+
+ if(!sort_list) { /* assume start time order */
+ incoming = search_format_array("start_time");
+ if(!incoming) fprintf(stderr, "Could not find start_time!\n");
+ tmp_list = malloc(sizeof(sort_node));
+ tmp_list->reverse = 0;
+ tmp_list->typecode = '?'; /* what was this for? */
+ tmp_list->sr = incoming->sr;
+ tmp_list->need = incoming->need;
+ tmp_list->next = sort_list;
+ sort_list = tmp_list;
+ }
+ /* this is required for the forest option */
+ incoming = search_format_array("ppid");
+ if(!incoming) fprintf(stderr, "Could not find ppid!\n");
+ tmp_list = malloc(sizeof(sort_node));
+ tmp_list->reverse = 0;
+ tmp_list->typecode = '?'; /* what was this for? */
+ tmp_list->sr = incoming->sr;
+ tmp_list->need = incoming->need;
+ tmp_list->next = sort_list;
+ sort_list = tmp_list;
+}
+
+/* we rely on the POSIX requirement for zeroed memory */
+//static proc_t *processes[98*1024]; // FIXME
+static proc_t **processes;
+
+/***** compare function for qsort */
+static int compare_two_procs(const void *a, const void *b){
+ sort_node *tmp_list = sort_list;
+ while(tmp_list){
+ int result;
+ result = (*tmp_list->sr)(*(const proc_t *const*)a, *(const proc_t *const*)b);
+ if(result) return (tmp_list->reverse) ? -result : result;
+ tmp_list = tmp_list->next;
+ }
+ return 0; /* no conclusion */
+}
+
+/***** show pre-sorted array of process pointers */
+static void show_proc_array(PROCTAB *restrict ptp, int n){
+ proc_t **p = processes;
+ while(n--){
+ if(thread_flags & TF_show_proc) show_one_proc(*p, proc_format_list);
+ if(thread_flags & TF_show_task){
+ proc_t buf2;
+ // must still have the process allocated
+ while(readtask(ptp,*p,&buf2)) show_one_proc(&buf2, task_format_list);
+ // must not attempt to free cmdline and environ
+ }
+ /* no point freeing any of this -- won't need more mem */
+// if((*p)->cmdline) free((void*)*(*p)->cmdline);
+// if((*p)->environ) free((void*)*(*p)->environ);
+// memset(*p, '%', sizeof(proc_t)); /* debug */
+// free(*p);
+ p++;
+ }
+}
+
+/***** show tree */
+/* this needs some optimization work */
+#define ADOPTED(x) 1
+static void show_tree(const int self, const int n, const int level, const int have_sibling){
+ int i = 0;
+ if(level){
+ /* add prefix of "+" or "L" */
+ if(have_sibling) forest_prefix[level-1] = '+';
+ else forest_prefix[level-1] = 'L';
+ forest_prefix[level] = '\0';
+ }
+ show_one_proc(processes[self],format_list); /* first show self */
+ /* no point freeing any of this -- won't need more mem */
+// if(processes[self]->cmdline) free((void*)*processes[self]->cmdline);
+// if(processes[self]->environ) free((void*)*processes[self]->environ);
+ for(;;){ /* look for children */
+ if(i >= n) return; /* no children */
+ if(processes[i]->ppid == processes[self]->XXXID) break;
+ i++;
+ }
+ if(level){
+ /* change our prefix to "|" or " " for the children */
+ if(have_sibling) forest_prefix[level-1] = '|';
+ else forest_prefix[level-1] = ' ';
+ forest_prefix[level] = '\0';
+ }
+ for(;;){
+ int self_pid;
+ int more_children = 1;
+ if(i >= n) break; /* over the edge */
+ self_pid=processes[self]->XXXID;
+ if(i+1 >= n)
+ more_children = 0;
+ else
+ if(processes[i+1]->ppid != self_pid) more_children = 0;
+ if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u')
+ show_tree(i++, n, level, more_children);
+ else
+ show_tree(i++, n, level+1, more_children);
+ if(!more_children) break;
+ }
+ /* chop prefix that children added -- do we need this? */
+ forest_prefix[level] = '\0';
+// memset(processes[self], '$', sizeof(proc_t)); /* debug */
+}
+
+/***** show forest */
+static void show_forest(const int n){
+ int i = n;
+ int j;
+ while(i--){ /* cover whole array looking for trees */
+ j = n;
+ while(j--){ /* search for parent: if none, i is a tree! */
+ if(processes[j]->XXXID == processes[i]->ppid) goto not_root;
+ }
+ show_tree(i,n,0,0);
+not_root:
+ ;
+ }
+ /* don't free the array because it takes time and ps will exit anyway */
+}
+
+static int want_this_proc_nop(proc_t *dummy){
+ (void)dummy;
+ return 1;
+}
+
+/***** sorted or forest */
+static void fancy_spew(void){
+ proc_data_t *pd = NULL;
+ PROCTAB *restrict ptp;
+ int n = 0; /* number of processes & index into array */
+
+ ptp = openproc(needs_for_format | needs_for_sort | needs_for_select | needs_for_threads);
+ if(!ptp) {
+ fprintf(stderr, "Error: can not access /proc.\n");
+ exit(1);
+ }
+
+ if(thread_flags & TF_loose_tasks){
+ pd = readproctab2(want_this_proc_nop, want_this_proc_pcpu, ptp);
+ }else{
+ pd = readproctab2(want_this_proc_pcpu, (void*)0xdeadbeaful, ptp);
+ }
+ n = pd->n;
+ processes = pd->tab;
+
+ if(!n) return; /* no processes */
+ if(forest_type) prep_forest_sort();
+ qsort(processes, n, sizeof(proc_t*), compare_two_procs);
+ if(forest_type) show_forest(n);
+ else show_proc_array(ptp,n);
+ closeproc(ptp);
+}
+
+
+/***** no comment */
+int main(int argc, char *argv[]){
+#if (__GNU_LIBRARY__ >= 6)
+ setlocale (LC_CTYPE, "");
+#endif
+
+#ifdef DEBUG
+ init_stack_trace(argv[0]);
+#else
+ do {
+ struct sigaction sa;
+ int i = 32;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = signal_handler;
+ sigfillset(&sa.sa_mask);
+ while(i--) switch(i){
+ default:
+ sigaction(i,&sa,NULL);
+ case 0:
+ case SIGINT: /* ^C */
+ case SIGTSTP: /* ^Z */
+ case SIGTTOU: /* see stty(1) man page */
+ case SIGQUIT: /* ^\ */
+ case SIGPROF: /* profiling */
+ case SIGKILL: /* can not catch */
+ case SIGSTOP: /* can not catch */
+ case SIGWINCH: /* don't care if window size changes */
+ ;
+ }
+ } while (0);
+#endif
+
+ reset_global(); /* must be before parser */
+ arg_parse(argc,argv);
+
+/* arg_show(); */
+ trace("screen is %ux%u\n",screen_cols,screen_rows);
+/* printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */
+ trace("======= ps output follows =======\n");
+
+ init_output(); /* must be between parser and output */
+
+ lists_and_needs();
+
+ if(forest_type || sort_list) fancy_spew(); /* sort or forest */
+ else simple_spew(); /* no sort, no forest */
+ show_one_proc((proc_t *)-1,format_list); /* no output yet? */
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <stdlib.h>
+#include <termios.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#include "common.h"
+
+#include <sys/sysmacros.h>
+#include "../proc/wchan.h"
+#include "../proc/version.h"
+#include "../proc/sysinfo.h"
+
+
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__ -1
+#endif
+#ifndef __GLIBC__
+#define __GLIBC__ -1
+#endif
+#ifndef __GLIBC_MINOR__
+#define __GLIBC_MINOR__ -1
+#endif
+
+
+static const char * saved_personality_text = "You found a bug!";
+
+int all_processes = -1;
+const char *bsd_j_format = (const char *)0xdeadbeef;
+const char *bsd_l_format = (const char *)0xdeadbeef;
+const char *bsd_s_format = (const char *)0xdeadbeef;
+const char *bsd_u_format = (const char *)0xdeadbeef;
+const char *bsd_v_format = (const char *)0xdeadbeef;
+int bsd_c_option = -1;
+int bsd_e_option = -1;
+uid_t cached_euid = -1;
+dev_t cached_tty = -1;
+char forest_prefix[4 * 32*1024 + 100]; // FIXME
+int forest_type = -1;
+unsigned format_flags = 0xffffffff; /* -l -f l u s -j... */
+format_node *format_list = (format_node *)0xdeadbeef; /* digested formatting options */
+unsigned format_modifiers = 0xffffffff; /* -c -j -y -P -L... */
+int header_gap = -1;
+int header_type = -1;
+int include_dead_children = -1;
+int lines_to_next_header = -1;
+const char *namelist_file = (const char *)0xdeadbeef;
+int negate_selection = -1;
+int running_only = -1;
+int page_size = -1; // "int" for math reasons?
+unsigned personality = 0xffffffff;
+int prefer_bsd_defaults = -1;
+int screen_cols = -1;
+int screen_rows = -1;
+unsigned long seconds_since_boot = -1;
+selection_node *selection_list = (selection_node *)0xdeadbeef;
+unsigned simple_select = 0xffffffff;
+sort_node *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */
+const char *sysv_f_format = (const char *)0xdeadbeef;
+const char *sysv_fl_format = (const char *)0xdeadbeef;
+const char *sysv_j_format = (const char *)0xdeadbeef;
+const char *sysv_l_format = (const char *)0xdeadbeef;
+unsigned thread_flags = 0xffffffff;
+int unix_f_option = -1;
+int user_is_number = -1;
+int wchan_is_number = -1;
+
+
+static void reset_selection_list(void){
+ selection_node *old;
+ selection_node *walk = selection_list;
+ if(selection_list == (selection_node *)0xdeadbeef){
+ selection_list = NULL;
+ return;
+ }
+ while(walk){
+ old = walk;
+ walk = old->next;
+ free(old->u);
+ free(old);
+ }
+ selection_list = NULL;
+}
+
+// The rules:
+// 1. Defaults are implementation-specific. (ioctl,termcap,guess)
+// 2. COLUMNS and LINES override the defaults. (standards compliance)
+// 3. Command line options override everything else.
+// 4. Actual output may be more if the above is too narrow.
+//
+// SysV tends to spew semi-wide output in all cases. The args
+// will be limited to 64 or 80 characters, without regard to
+// screen size. So lines of 120 to 160 chars are normal.
+// Tough luck if you want more or less than that! HP-UX has a
+// new "-x" option for 1024-char args in place of comm that
+// we'll implement at some point.
+//
+// BSD tends to make a good effort, then fall back to 80 cols.
+// Use "ww" to get infinity. This is nicer for "ps | less"
+// and "watch ps". It can run faster too.
+static void set_screen_size(void){
+ struct winsize ws;
+ char *columns; /* Unix98 environment variable */
+ char *lines; /* Unix98 environment variable */
+
+ do{
+ int fd;
+ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ if(ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ if(ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ fd = open("/dev/tty", O_NOCTTY|O_NONBLOCK|O_RDONLY);
+ if(fd != -1){
+ int ret = ioctl(fd, TIOCGWINSZ, &ws);
+ close(fd);
+ if(ret != -1 && ws.ws_col>0 && ws.ws_row>0) break;
+ }
+ // TODO: ought to do tgetnum("co") and tgetnum("li") here
+ ws.ws_col = 80;
+ ws.ws_row = 24;
+ }while(0);
+ screen_cols = ws.ws_col; // hmmm, NetBSD subtracts 1
+ screen_rows = ws.ws_row;
+
+ // TODO: delete this line
+ if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE;
+
+ columns = getenv("COLUMNS");
+ if(columns && *columns){
+ long t;
+ char *endptr;
+ t = strtol(columns, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t;
+ }
+
+ lines = getenv("LINES");
+ if(lines && *lines){
+ long t;
+ char *endptr;
+ t = strtol(lines, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t;
+ }
+
+ if((screen_cols<9) || (screen_rows<2))
+ fprintf(stderr,"Your %dx%d screen size is bogus. Expect trouble.\n",
+ screen_cols, screen_rows
+ );
+}
+
+/**************** personality control **************/
+
+typedef struct personality_table_struct {
+ const char *name; /* personality name */
+ const void *jump; /* See gcc extension info. :-) */
+} personality_table_struct;
+
+static int compare_personality_table_structs(const void *a, const void *b){
+ return strcasecmp(((const personality_table_struct*)a)->name,((const personality_table_struct*)b)->name);
+}
+
+static const char *set_personality(void){
+ const char *s;
+ size_t sl;
+ char buf[16];
+ personality_table_struct findme = { buf, NULL};
+ personality_table_struct *found;
+ static const personality_table_struct personality_table[] = {
+ {"390", &&case_390},
+ {"aix", &&case_aix},
+ {"bsd", &&case_bsd},
+ {"compaq", &&case_compaq},
+ {"debian", &&case_debian},
+ {"default", &&case_default},
+ {"digital", &&case_digital},
+ {"gnu", &&case_gnu},
+ {"hp", &&case_hp},
+ {"hpux", &&case_hpux},
+ {"irix", &&case_irix},
+ {"linux", &&case_linux},
+ {"old", &&case_old},
+ {"os390", &&case_os390},
+ {"posix", &&case_posix},
+ {"s390", &&case_s390},
+ {"sco", &&case_sco},
+ {"sgi", &&case_sgi},
+ {"solaris2", &&case_solaris2},
+ {"sunos4", &&case_sunos4},
+ {"svr4", &&case_svr4},
+ {"sysv", &&case_sysv},
+ {"tru64", &&case_tru64},
+ {"unix", &&case_unix},
+ {"unix95", &&case_unix95},
+ {"unix98", &&case_unix98},
+ {"unknown", &&case_unknown}
+ };
+ const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct);
+
+ personality = 0;
+ prefer_bsd_defaults = 0;
+
+ bsd_j_format = "OL_j";
+ bsd_l_format = "OL_l";
+ bsd_s_format = "OL_s";
+ bsd_u_format = "OL_u";
+ bsd_v_format = "OL_v";
+
+ /* When these are NULL, the code does SysV output modifier logic */
+ sysv_f_format = NULL;
+ sysv_fl_format = NULL;
+ sysv_j_format = NULL;
+ sysv_l_format = NULL;
+
+ s = getenv("PS_PERSONALITY");
+ if(!s || !*s) s = getenv("CMD_ENV");
+ if(!s || !*s) s="unknown"; /* "Do The Right Thing[tm]" */
+ if(getenv("I_WANT_A_BROKEN_PS")) s="old";
+ sl = strlen(s);
+ if(sl > 15) return "Environment specified an unknown personality.";
+ strncpy(buf, s, sl);
+ buf[sl] = '\0';
+ saved_personality_text = strdup(buf);
+
+ found = bsearch(&findme, personality_table, personality_table_count,
+ sizeof(personality_table_struct), compare_personality_table_structs
+ );
+
+ if(!found) return "Environment specified an unknown personality.";
+
+ goto *(found->jump); /* See gcc extension info. :-) */
+
+ case_bsd:
+ personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m;
+ prefer_bsd_defaults = 1;
+ bsd_j_format = "FB_j";
+ bsd_l_format = "FB_l";
+ /* bsd_s_format not used */
+ bsd_u_format = "FB_u";
+ bsd_v_format = "FB_v";
+ return NULL;
+
+ case_old:
+ personality = PER_FORCE_BSD | PER_OLD_m;
+ prefer_bsd_defaults = 1;
+ return NULL;
+
+ case_debian: /* Toss this? They don't seem to care much. */
+ case_gnu:
+ personality = PER_GOOD_o | PER_OLD_m;
+ prefer_bsd_defaults = 1;
+ sysv_f_format = "RD_f";
+ /* sysv_fl_format = "RD_fl"; */ /* old Debian ps can't do this! */
+ sysv_j_format = "RD_j";
+ sysv_l_format = "RD_l";
+ return NULL;
+
+ case_linux:
+ personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER;
+ return NULL;
+
+ case_default: /* use defaults for ps, ignoring other environment variables */
+ return NULL;
+
+ case_unknown: /* defaults, but also check inferior environment variables */
+ if(
+ getenv("UNIX95") /* Irix */
+ || getenv("POSIXLY_CORRECT") /* most gnu stuff */
+ || (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */
+ ) personality = PER_BROKEN_o;
+ return NULL;
+
+ case_aix:
+ bsd_j_format = "FB_j";
+ bsd_l_format = "FB_l";
+ /* bsd_s_format not used */
+ bsd_u_format = "FB_u";
+ bsd_v_format = "FB_v";
+ return NULL;
+
+ case_tru64:
+ case_compaq:
+ case_digital:
+ // no PER_NO_DEFAULT_g even though man page claims it
+ // Reality: the g is a NOP
+ personality = PER_GOOD_o | PER_BSD_h;
+ prefer_bsd_defaults = 1;
+ sysv_f_format = "F5FMT";
+ sysv_fl_format = "FL5FMT";
+ sysv_j_format = "JFMT";
+ sysv_l_format = "L5FMT";
+ bsd_j_format = "JFMT";
+ bsd_l_format = "LFMT";
+ bsd_s_format = "SFMT";
+ bsd_u_format = "UFMT";
+ bsd_v_format = "VFMT";
+ return NULL;
+
+ case_sunos4:
+ personality = PER_NO_DEFAULT_g;
+ prefer_bsd_defaults = 1;
+ bsd_j_format = "FB_j";
+ bsd_l_format = "FB_l";
+ /* bsd_s_format not used */
+ bsd_u_format = "FB_u";
+ bsd_v_format = "FB_v";
+ return NULL;
+
+ case_irix:
+ case_sgi:
+ s = getenv("_XPG");
+ if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o;
+ else personality = PER_IRIX_l;
+ return NULL;
+
+ case_os390: /* IBM's OS/390 OpenEdition on the S/390 mainframe */
+ case_s390:
+ case_390:
+ sysv_j_format = "J390"; /* don't know what -jl and -jf do */
+ return NULL;
+
+ case_hp:
+ case_hpux:
+ personality = PER_BROKEN_o | PER_HPUX_x;
+ return NULL;
+
+ case_svr4:
+ case_sysv:
+ case_sco:
+ personality = PER_BROKEN_o | PER_SVR4_x;
+ return NULL;
+
+ case_posix:
+ case_solaris2:
+ case_unix95:
+ case_unix98:
+ case_unix:
+ personality = PER_BROKEN_o;
+ return NULL;
+}
+
+
+/************ Call this to reinitialize everything ***************/
+void reset_global(void){
+ static proc_t p;
+ reset_selection_list();
+ look_up_our_self(&p);
+ set_screen_size();
+ set_personality();
+
+ all_processes = 0;
+ bsd_c_option = 0;
+ bsd_e_option = 0;
+ cached_euid = geteuid();
+ cached_tty = p.tty;
+/* forest_prefix must be all zero because of POSIX */
+ forest_type = 0;
+ format_flags = 0; /* -l -f l u s -j... */
+ format_list = NULL; /* digested formatting options */
+ format_modifiers = 0; /* -c -j -y -P -L... */
+ header_gap = -1; /* send lines_to_next_header to -infinity */
+ header_type = HEAD_SINGLE;
+ include_dead_children = 0;
+ lines_to_next_header = 1;
+ namelist_file = NULL;
+ negate_selection = 0;
+ page_size = getpagesize();
+ running_only = 0;
+ seconds_since_boot = uptime(0,0);
+ selection_list = NULL;
+ simple_select = 0;
+ sort_list = NULL;
+ thread_flags = 0;
+ unix_f_option = 0;
+ user_is_number = 0;
+ wchan_is_number = 0;
+}
+
+static const char archdefs[] =
+#ifdef __alpha__
+" alpha"
+#endif
+#ifdef __arm__
+" arm"
+#endif
+#ifdef __hppa__
+" hppa"
+#endif
+#ifdef __i386__
+" i386"
+#endif
+#ifdef __ia64__
+" ia64"
+#endif
+#ifdef __mc68000__
+" mc68000"
+#endif
+#ifdef __mips64__
+" mips64"
+#endif
+#ifdef __mips__
+" mips"
+#endif
+#ifdef __powerpc__
+" powerpc"
+#endif
+#ifdef __sh3__
+" sh3"
+#endif
+#ifdef __sh__
+" sh"
+#endif
+#ifdef __sparc__
+" sparc"
+#endif
+#ifdef __sparc_v9__
+" sparc_v9"
+#endif
+#ifdef __x86_64__
+" x86_64"
+#endif
+"";
+
+/*********** spew variables ***********/
+void self_info(void){
+ fprintf(stderr,
+ "BSD j %s\n"
+ "BSD l %s\n"
+ "BSD s %s\n"
+ "BSD u %s\n"
+ "BSD v %s\n"
+ "SysV -f %s\n"
+ "SysV -fl %s\n"
+ "SysV -j %s\n"
+ "SysV -l %s\n"
+ "\n",
+ bsd_j_format ? bsd_j_format : "(none)",
+ bsd_l_format ? bsd_l_format : "(none)",
+ bsd_s_format ? bsd_s_format : "(none)",
+ bsd_u_format ? bsd_u_format : "(none)",
+ bsd_v_format ? bsd_v_format : "(none)",
+ sysv_f_format ? sysv_f_format : "(none)",
+ sysv_fl_format ? sysv_fl_format : "(none)",
+ sysv_j_format ? sysv_j_format : "(none)",
+ sysv_l_format ? sysv_l_format : "(none)"
+ );
+
+ display_version();
+ fprintf(stderr, "Linux version %d.%d.%d\n",
+ LINUX_VERSION_MAJOR(linux_version_code),
+ LINUX_VERSION_MINOR(linux_version_code),
+ LINUX_VERSION_PATCH(linux_version_code)
+ );
+ /* __libc_print_version(); */ /* how can we get the run-time version? */
+ fprintf(stderr, "Compiled with: glibc %d.%d, gcc %d.%d\n\n",
+ __GLIBC__, __GLIBC_MINOR__, __GNUC__, __GNUC_MINOR__
+ );
+
+ fprintf(stderr,
+ "header_gap=%d lines_to_next_header=%d\n"
+ "screen_cols=%d screen_rows=%d\n"
+ "\n",
+ header_gap, lines_to_next_header,
+ screen_cols, screen_rows
+ );
+
+ fprintf(stderr,
+ "personality=0x%08x (from \"%s\")\n"
+ "EUID=%d TTY=%d,%d Hertz=%Ld page_size=%d\n",
+ personality, saved_personality_text,
+ cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz,
+ (int)(page_size)
+ );
+
+ fprintf(stderr,
+ "sizeof(proc_t)=%d sizeof(long)=%d sizeof(KLONG)=%d\n",
+ (int)sizeof(proc_t), (int)sizeof(long), (int)sizeof(KLONG)
+ );
+
+ fprintf(stderr, "archdefs:%s\n", archdefs);
+
+ open_psdb(namelist_file);
+ fprintf(stderr,"namelist_file=\"%s\"\n",namelist_file?namelist_file:"<no System.map file>");
+}
--- /dev/null
+/*
+ * Copyright 1998-2004 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/*
+ * The help message must not become longer, because it must fit
+ * on an 80x24 screen _with_ the error message and command prompt.
+ */
+
+const char *help_message =
+"********* simple selection ********* ********* selection by list *********\n"
+"-A all processes -C by command name\n"
+"-N negate selection -G by real group ID (supports names)\n"
+"-a all w/ tty except session leaders -U by real user ID (supports names)\n"
+"-d all except session leaders -g by session OR by effective group name\n"
+"-e all processes -p by process ID\n"
+"T all processes on this terminal -s processes in the sessions given\n"
+"a all w/ tty, including other users -t by tty\n"
+"g OBSOLETE -- DO NOT USE -u by effective user ID (supports names)\n"
+"r only running processes U processes for specified users\n"
+"x processes w/o controlling ttys t by tty\n"
+"*********** output format ********** *********** long options ***********\n"
+"-o,o user-defined -f full --Group --User --pid --cols --ppid\n"
+"-j,j job control s signal --group --user --sid --rows --info\n"
+"-O,O preloaded -o v virtual memory --cumulative --format --deselect\n"
+"-l,l long u user-oriented --sort --tty --forest --version\n"
+"-F extra full X registers --heading --no-heading --context\n"
+" ********* misc options *********\n"
+"-V,V show version L list format codes f ASCII art forest\n"
+"-m,m,-L,-T,H threads S children in sum -y change -l format\n"
+"-M,Z security data c true command name -c scheduling class\n"
+"-w,w wide output n numeric WCHAN,UID -H process hierarchy\n"
+;
+
+
+
+/* Missing:
+ *
+ * -P e k
+ *
+ */
--- /dev/null
+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?
--- /dev/null
+# This file gets included into the main Makefile, in the top directory.
+
+INSTALL += $(bin)ps $(man1)ps.1
+
+# files to remove
+CLEAN += ps/ps ps/debug
+
+# a directory for cleaning
+DIRS += ps/
+
+# a file to create
+ALL += ps/ps
+
+PS_C := display global help output parser select sortformat
+PSNAMES := $(addprefix ps/,$(PS_C))
+PSOBJ := $(addsuffix .o,$(PSNAMES))
+PSSRC := $(addsuffix .c,$(PSNAMES))
+
+PS_X := COPYING HACKING TRANSLATION common.h module.mk it p ps.1 regression
+TARFILES += $(PSSRC) $(addprefix ps/,$(PS_X))
+
+ps/ps: $(PSOBJ) $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ $(ldl)
+
+# This just adds the stacktrace code
+ps/debug: $(PSOBJ) stacktrace.o $(LIBPROC)
+ $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ -lefence $(ldl)
+
+$(PSOBJ): %.o: %.c ps/common.h $(LIBPROC)
+ $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) $< -o $@
+
+ps/stacktrace.o: ps/stacktrace.c
+
+
+$(bin)ps: ps/ps
+ $(install) --mode a=rx $< $@
+
+$(man1)ps.1 : ps/ps.1
+ $(install) --mode a=r $< $@
+ -rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz
--- /dev/null
+/*
+ * Copyright 1999-2004 by Albert Cahalan; all rights reserved.
+ *
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/*
+ * This file is really gross, and I know it. I looked into several
+ * alternate ways to deal with the mess, and they were all ugly.
+ *
+ * FreeBSD has a fancy hack using offsets into a struct -- that
+ * saves code but it is _really_ gross. See the PO macro below.
+ *
+ * We could have a second column width for wide output format.
+ * For example, Digital prints the real-time signals.
+ */
+
+
+/*
+ * Data table idea:
+ *
+ * table 1 maps aix to specifier
+ * table 2 maps shortsort to specifier
+ * table 3 maps macro to specifiers
+ * table 4 maps specifier to title,datatype,offset,vendor,helptext
+ * table 5 maps datatype to justification,width,widewidth,sorting,printing
+ *
+ * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
+ * It must be enough to determine printing and sorting.
+ *
+ * After the tables, increase width as needed to fit the header.
+ *
+ * Table 5 could go in a file with the output functions.
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "../proc/wchan.h"
+#include "../proc/procps.h"
+#include "../proc/devname.h"
+#include "../proc/escape.h"
+#include "common.h"
+
+/* TODO:
+ * Stop assuming system time is local time.
+ */
+
+#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
+
+static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
+static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */
+
+
+
+static int wide_signals; /* true if we have room */
+
+static unsigned long seconds_since_1970;
+static unsigned long time_of_boot;
+static unsigned long page_shift;
+
+
+/*************************************************************************/
+/************ Lots of sort functions, starting with the NOP **************/
+
+static int sr_nop(const proc_t* a, const proc_t* b){
+ (void)a;(void)b; /* shut up gcc */
+ return 0;
+}
+
+#define CMP_STR(NAME) \
+static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \
+ return strcmp(P->NAME, Q->NAME); \
+}
+
+#define CMP_INT(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+ if (P->NAME < Q->NAME) return -1; \
+ if (P->NAME > Q->NAME) return 1; \
+ return 0; \
+}
+
+/* fast version, for values which either:
+ * a. differ by no more than 0x7fffffff
+ * b. only need to be grouped same w/ same
+ */
+#define CMP_SMALL(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+ return (int)(P->NAME) - (int)(Q->NAME); \
+}
+
+CMP_INT(rtprio)
+CMP_SMALL(sched)
+CMP_INT(cutime)
+CMP_INT(cstime)
+CMP_SMALL(priority) /* nice */
+CMP_SMALL(nlwp)
+CMP_SMALL(nice) /* priority */
+CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */
+CMP_INT(alarm)
+CMP_INT(size) /* total pages */ /* vm_size, vsize */
+CMP_INT(resident) /* resident pages */ /* vm_rss, rss */
+CMP_INT(share) /* shared pages */
+CMP_INT(trs) /* executable pages */
+CMP_INT(lrs) /* obsolete "library" pages above 0x60000000 */
+CMP_INT(drs) /* other pages (assumed data?) */
+CMP_INT(dt) /* dirty pages */
+
+CMP_INT(vm_size) /* kB VM */ /* size, vsize */
+CMP_INT(vm_lock) /* kB locked */
+CMP_INT(vm_rss) /* kB rss */ /* rss, resident */
+CMP_INT(vm_data) /* kB "data" == data-stack */
+CMP_INT(vm_stack) /* kB stack */
+CMP_INT(vm_exe) /* kB "exec" == exec-lib */
+CMP_INT(vm_lib) /* kB "libraries" */
+CMP_INT(vsize) /* pages VM */ /* size, vm_size */
+CMP_INT(rss_rlim)
+CMP_SMALL(flags)
+CMP_INT(min_flt)
+CMP_INT(maj_flt)
+CMP_INT(cmin_flt)
+CMP_INT(cmaj_flt)
+CMP_INT(utime)
+CMP_INT(stime) /* Old: sort by systime. New: show start time. Uh oh. */
+CMP_INT(start_code)
+CMP_INT(end_code)
+CMP_INT(start_stack)
+CMP_INT(kstk_esp)
+CMP_INT(kstk_eip)
+CMP_INT(start_time)
+CMP_INT(wchan)
+
+/* CMP_STR(*environ) */
+/* CMP_STR(*cmdline) */
+
+CMP_STR(ruser)
+CMP_STR(euser)
+CMP_STR(suser)
+CMP_STR(fuser)
+CMP_STR(rgroup)
+CMP_STR(egroup)
+CMP_STR(sgroup)
+CMP_STR(fgroup)
+CMP_STR(cmd)
+/* CMP_STR(ttyc) */ /* FIXME -- use strncmp with 8 max */
+
+CMP_INT(ruid)
+CMP_INT(rgid)
+CMP_INT(euid)
+CMP_INT(egid)
+CMP_INT(suid)
+CMP_INT(sgid)
+CMP_INT(fuid)
+CMP_INT(fgid)
+CMP_SMALL(tid)
+CMP_SMALL(tgid)
+CMP_SMALL(ppid)
+CMP_SMALL(pgrp)
+CMP_SMALL(session)
+CMP_INT(tty)
+CMP_SMALL(tpgid)
+
+CMP_SMALL(pcpu)
+
+CMP_SMALL(state)
+
+/* approximation to: kB of address space that could end up in swap */
+static int sr_swapable(const proc_t* P, const proc_t* Q) {
+ unsigned long p_swapable = P->vm_data + P->vm_stack;
+ unsigned long q_swapable = Q->vm_data + Q->vm_stack;
+ if (p_swapable < q_swapable) return -1;
+ if (p_swapable > q_swapable) return 1;
+ return 0;
+}
+
+
+/***************************************************************************/
+/************ Lots of format functions, starting with the NOP **************/
+
+// so popular it can't be "static"
+int pr_nop(char *restrict const outbuf, const proc_t *restrict const pp){
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%c", '-');
+}
+
+
+/********* Unix 98 ************/
+
+/***
+
+Only comm and args are allowed to contain blank characters; all others are
+not. Any implementation-dependent variables will be specified in the system
+documentation along with the default header and indicating if the field
+may contain blank characters.
+
+Some headers do not have a standardized specifier!
+
+%CPU pcpu The % of cpu time used recently, with unspecified "recently".
+ADDR The address of the process.
+C Processor utilisation for scheduling.
+CMD The command name, or everything with -f.
+COMMAND args Command + args. May chop as desired. May use either version.
+COMMAND comm argv[0]
+ELAPSED etime Elapsed time since the process was started. [[dd-]hh:]mm:ss
+F Flags (octal and additive)
+GROUP group Effective group ID, prefer text over decimal.
+NI nice Decimal system scheduling priority, see nice(1).
+PGID pgid The decimal value of the process group ID.
+PID pid Decimal PID.
+PPID ppid Decimal PID.
+PRI Priority. Higher numbers mean lower priority.
+RGROUP rgroup Real group ID, prefer text over decimal.
+RUSER ruser Real user ID, prefer text over decimal.
+S The state of the process.
+STIME Starting time of the process.
+SZ The size in blocks of the core image of the process.
+TIME time Cumulative CPU time. [dd-]hh:mm:ss
+TT tty Name of tty in format used by who(1).
+TTY The controlling terminal for the process.
+UID UID, or name when -f
+USER user Effective user ID, prefer text over decimal.
+VSZ vsz Virtual memory size in decimal kB.
+WCHAN Where waiting/sleeping or blank if running.
+
+The nice value is used to compute the priority.
+
+For some undefined ones, Digital does:
+
+F flag Process flags -- but in hex!
+PRI pri Process priority
+S state Symbolic process status
+TTY tt,tty,tname,longtname -- all do "ttyp1", "console", "??"
+UID uid Process user ID (effective UID)
+WCHAN wchan Address of event on which a
+
+For some undefined ones, Sun does:
+
+ADDR addr memory address of the process
+C c Processor utilization for scheduling (obsolete).
+CMD
+F f
+S s state: OSRZT
+STIME start time, printed w/o blanks. If 24h old, months & days
+SZ size (in pages) of the swappable process's image in main memory
+TTY
+UID uid
+WCHAN wchan
+
+For some undefined ones, SCO does:
+ADDR addr Virtual address of the process' entry in the process table.
+SZ swappable size in kB of the virtual data and stack
+STIME stime hms or md time format
+***/
+
+/* Source & destination are known. Return bytes or screen characters? */
+static int forest_helper(char *restrict const outbuf){
+ char *p = forest_prefix;
+ char *q = outbuf;
+ int rightward=max_rightward;
+ if(!*p) return 0;
+ /* Arrrgh! somebody defined unix as 1 */
+ if(forest_type == 'u') goto unixy;
+ while(*p){
+ switch(*p){
+ case ' ': strcpy(q, " "); break;
+ case 'L': strcpy(q, " \\_ "); break;
+ case '+': strcpy(q, " \\_ "); break;
+ case '|': strcpy(q, " | "); break;
+ case '\0': return q-outbuf; /* redundant & not used */
+ }
+ if (rightward-4 < 0) {
+ *(q+rightward)='\0';
+ return max_rightward;
+ }
+ q += 4;
+ rightward -= 4;
+ p++;
+ }
+ return q-outbuf; /* gcc likes this here */
+unixy:
+ while(*p){
+ switch(*p){
+ case ' ': strcpy(q, " "); break;
+ case 'L': strcpy(q, " "); break;
+ case '+': strcpy(q, " "); break;
+ case '|': strcpy(q, " "); break;
+ case '\0': return q-outbuf; /* redundant & not used */
+ }
+ if (rightward-2 < 0) {
+ *(q+rightward)='\0';
+ return max_rightward;
+ }
+ q += 2;
+ rightward -= 2;
+ p++;
+ }
+ return q-outbuf; /* gcc likes this here */
+}
+
+
+/* XPG4-UNIX, according to Digital:
+The "args" and "command" specifiers show what was passed to the command.
+Modifications to the arguments are not shown.
+*/
+
+/*
+ * pp->cmd short accounting name (comm & ucomm)
+ * pp->cmdline long name with args (args & command)
+ * pp->environ environment
+ */
+
+// FIXME: some of these may hit the guard page in forest mode
+
+/* "command" is the same thing: long unless c */
+static int pr_args(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ unsigned flags;
+ int rightward=max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if(bsd_c_option) flags = ESC_DEFUNCT;
+ else flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
+
+ if(bsd_e_option && rightward>1){
+ const char **env = (const char**)pp->environ;
+ if(env && *env){
+ *endp++ = ' ';
+ rightward--;
+ endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
+ }
+ }
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+
+/* "ucomm" is the same thing: short unless -f */
+static int pr_comm(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ unsigned flags;
+ int rightward=max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if(unix_f_option) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ else flags = ESC_DEFUNCT;
+ endp += escape_command(endp, pp, OUTBUF_SIZE, &rightward, flags);
+
+ if(bsd_e_option && rightward>1){
+ const char **env = (const char**)pp->environ;
+ if(env && *env){
+ *endp++ = ' ';
+ rightward--;
+ endp += escape_strlist(endp, env, OUTBUF_SIZE, &rightward);
+ }
+ }
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+/* Non-standard, from SunOS 5 */
+static int pr_fname(char *restrict const outbuf, const proc_t *restrict const pp){
+ char *endp = outbuf;
+ int rightward = max_rightward;
+
+ if(forest_prefix){
+ int fh = forest_helper(outbuf);
+ endp += fh;
+ rightward -= fh;
+ }
+ if (rightward>8) /* 8=default, but forest maybe feeds more */
+ rightward = 8;
+
+ endp += escape_str(endp, pp->cmd, OUTBUF_SIZE, &rightward);
+ //return endp - outbuf;
+ return max_rightward-rightward;
+}
+
+/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
+static int pr_etime(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ char *cp = outbuf;
+ t = seconds_since_boot - (unsigned long)(pp->start_time / Hertz);
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ dd = t;
+ cp +=( dd ? snprintf(cp, COLWID, "%u-", dd) : 0 );
+ cp +=( (dd || hh) ? snprintf(cp, COLWID, "%02u:", hh) : 0 );
+ cp += snprintf(cp, COLWID, "%02u:%02u", mm, ss) ;
+ return (int)(cp-outbuf);
+}
+
+/* "Processor utilisation for scheduling." --- we use %cpu w/o fraction */
+static int pr_c(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu = 0; /* scaled %cpu, 99 means 99% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz;
+ if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
+ if (pcpu > 99U) pcpu = 99U;
+ return snprintf(outbuf, COLWID, "%2u", pcpu);
+}
+/* normal %CPU in ##.# format. */
+static int pr_pcpu(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz;
+ if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+ if (pcpu > 999U)
+ return snprintf(outbuf, COLWID, "%u", pcpu/10U);
+ return snprintf(outbuf, COLWID, "%u.%u", pcpu/10U, pcpu%10U);
+}
+/* this is a "per-mill" format, like %cpu with no decimal point */
+static int pr_cp(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long total_time; /* jiffies used by this process */
+ unsigned pcpu = 0; /* scaled %cpu, 999 means 99.9% */
+ unsigned long long seconds; /* seconds of process life */
+ total_time = pp->utime + pp->stime;
+ if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+ seconds = seconds_since_boot - pp->start_time / Hertz ;
+ if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+ if (pcpu > 999U) pcpu = 999U;
+ return snprintf(outbuf, COLWID, "%3u", pcpu);
+}
+
+static int pr_pgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->pgrp);
+}
+static int pr_pid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->tgid);
+}
+static int pr_ppid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->ppid);
+}
+
+
+/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
+static int pr_time(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long t;
+ unsigned dd,hh,mm,ss;
+ int c;
+ t = (pp->utime + pp->stime) / Hertz;
+ ss = t%60;
+ t /= 60;
+ mm = t%60;
+ t /= 60;
+ hh = t%24;
+ t /= 24;
+ dd = t;
+ c =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0 );
+ c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss) );
+ return c;
+}
+
+/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages.
+ * Unix98 requires "vsz" to be kB.
+ * Tru64 does both vsize and vsz like "1.23M"
+ *
+ * Our pp->vm_size is kB and our pp->vsize is pages.
+ *
+ * TODO: add flag for "1.23M" behavior, on this and other columns.
+ */
+static int pr_vsz(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+// "PRI" is created by "opri", or by "pri" when -c is used.
+//
+// Unix98 only specifies that a high "PRI" is low priority.
+// Sun and SCO add the -c behavior. Sun defines "pri" and "opri".
+// Linux may use "priority" for historical purposes.
+//
+// According to the kernel's fs/proc/array.c and kernel/sched.c source,
+// the kernel reports it in /proc via this:
+// p->prio - MAX_RT_PRIO
+// such that "RT tasks are offset by -200. Normal tasks are centered
+// around 0, value goes from -16 to +15" but who knows if that is
+// before or after the conversion...
+//
+// <linux/sched.h> says:
+// MAX_RT_PRIO is currently 100. (so we see 0 in /proc)
+// RT tasks have a p->prio of 0 to 99. (so we see -100 to -1)
+// non-RT tasks are from 100 to 139. (so we see 0 to 39)
+// Lower values have higher priority, as in the UNIX standard.
+//
+// In any case, pp->priority+100 should get us back to what the kernel
+// has for p->prio.
+//
+// Test results with the "yes" program on a 2.6.x kernel:
+//
+// # ps -C19,_20 -o pri,opri,intpri,priority,ni,pcpu,pid,comm
+// PRI PRI PRI PRI NI %CPU PID COMMAND
+// 0 99 99 39 19 10.6 8686 19
+// 34 65 65 5 -20 94.7 8687 _20
+//
+// Grrr. So the UNIX standard "PRI" must NOT be from "pri".
+// Either of the others will do. We use "opri" for this.
+// (and use "pri" when the "-c" option is used)
+// Probably we should have Linux-specific "pri_for_l" and "pri_for_lc"
+//
+// sched_get_priority_min.2 says the Linux static priority is
+// 1..99 for RT and 0 for other... maybe 100 is kernel-only?
+//
+// A nice range would be -99..0 for RT and 1..40 for normal,
+// which is pp->priority+1. (3-digit max, positive is normal,
+// negative or 0 is RT, and meets the standard for PRI)
+//
+
+// legal as UNIX "PRI"
+// "priority" (was -20..20, now -100..39)
+static int pr_priority(char *restrict const outbuf, const proc_t *restrict const pp){ /* -20..20 */
+ return snprintf(outbuf, COLWID, "%ld", pp->priority);
+}
+
+// legal as UNIX "PRI"
+// "intpri" and "opri" (was 39..79, now -40..99)
+static int pr_opri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 39..79 */
+ return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority);
+}
+
+// legal as UNIX "PRI"
+// "pri_foo" -- match up w/ nice values of sleeping processes (-120..19)
+static int pr_pri_foo(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority - 20);
+}
+
+// legal as UNIX "PRI"
+// "pri_bar" -- makes RT pri show as negative (-99..40)
+static int pr_pri_bar(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority + 1);
+}
+
+// legal as UNIX "PRI"
+// "pri_baz" -- the kernel's ->prio value, as of Linux 2.6.8 (1..140)
+static int pr_pri_baz(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->priority + 100);
+}
+
+
+// not legal as UNIX "PRI"
+// "pri" (was 20..60, now 0..139)
+static int pr_pri(char *restrict const outbuf, const proc_t *restrict const pp){ /* 20..60 */
+ return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
+}
+
+// not legal as UNIX "PRI"
+// "pri_api" -- match up w/ RT API (-40..99)
+static int pr_pri_api(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", -1 - pp->priority);
+}
+
+static int pr_nice(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched!=0 && pp->sched!=-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->nice);
+}
+
+// HP-UX "cls": RT RR RR2 ???? HPUX FIFO KERN
+// Solaris "class": SYS TS FX IA RT FSS (FIFO is RR w/ Inf quant)
+// FIFO+RR share RT; FIFO has Inf quant
+// IA=interactive; FX=fixed; TS=timeshare; SYS=system
+// FSS=fairshare; INTS=interrupts
+// Tru64 "policy": FF RR TS
+// IRIX "class": RT TS B BC WL GN
+// RT=real-time; TS=time-share; B=batch; BC=batch-critical
+// WL=weightless; GN=gang-scheduled
+// see miser(1) for this; PRI has some letter codes too
+static int pr_class(char *restrict const outbuf, const proc_t *restrict const pp){
+ switch(pp->sched){
+ case -1: return snprintf(outbuf, COLWID, "-"); // not reported
+ case 0: return snprintf(outbuf, COLWID, "TS"); // SCHED_OTHER SCHED_NORMAL
+ case 1: return snprintf(outbuf, COLWID, "FF"); // SCHED_FIFO
+ case 2: return snprintf(outbuf, COLWID, "RR"); // SCHED_RR
+ case 3: return snprintf(outbuf, COLWID, "B"); // SCHED_BATCH
+ case 4: return snprintf(outbuf, COLWID, "ISO"); // reserved for SCHED_ISO (Con Kolivas)
+ case 5: return snprintf(outbuf, COLWID, "IDL"); // SCHED_IDLE
+ case 6: return snprintf(outbuf, COLWID, "#6"); //
+ case 7: return snprintf(outbuf, COLWID, "#7"); //
+ case 8: return snprintf(outbuf, COLWID, "#8"); //
+ case 9: return snprintf(outbuf, COLWID, "#9"); //
+ default: return snprintf(outbuf, COLWID, "?"); // unknown value
+ }
+}
+// Based on "type", FreeBSD would do:
+// REALTIME "real:%u", prio
+// NORMAL "normal"
+// IDLE "idle:%u", prio
+// default "%u:%u", type, prio
+// We just print the priority, and have other keywords for type.
+static int pr_rtprio(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched==0 || pp->sched==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->rtprio);
+}
+static int pr_sched(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->sched==-1) return snprintf(outbuf, COLWID, "-");
+ return snprintf(outbuf, COLWID, "%ld", pp->sched);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int pr_wchan(char *restrict const outbuf, const proc_t *restrict const pp){
+/*
+ * Unix98 says "blank if running" and also "no blanks"! :-(
+ * Unix98 also says to use '-' if something is meaningless.
+ * Digital uses both '*' and '-', with undocumented differences.
+ * (the '*' for -1 (rare) and the '-' for 0)
+ * Sun claims to use a blank AND use '-', in the same man page.
+ * Perhaps "blank" should mean '-'.
+ *
+ * AIX uses '-' for running processes, the location when there is
+ * only one thread waiting in the kernel, and '*' when there is
+ * more than one thread waiting in the kernel.
+ *
+ * The output should be truncated to maximal columns width -- overflow
+ * is not supported for the "wchan".
+ */
+ const char *w;
+ size_t len;
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ if(wchan_is_number) return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
+ w = lookup_wchan(pp->wchan, pp->XXXID);
+ len = strlen(w);
+ if(len>max_rightward) len=max_rightward;
+ memcpy(outbuf, w, len);
+ outbuf[len] = '\0';
+ return len;
+}
+
+static int pr_wname(char *restrict const outbuf, const proc_t *restrict const pp){
+/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too.
+ *
+ * We use '-' for running processes, the location when there is
+ * only one thread waiting in the kernel, and '*' when there is
+ * more than one thread waiting in the kernel.
+ *
+ * The output should be truncated to maximal columns width -- overflow
+ * is not supported for the "wchan".
+ */
+ const char *w;
+ size_t len;
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ w = lookup_wchan(pp->wchan, pp->XXXID);
+ len = strlen(w);
+ if(len>max_rightward) len=max_rightward;
+ memcpy(outbuf, w, len);
+ outbuf[len] = '\0';
+ return len;
+}
+
+static int pr_nwchan(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(!(pp->wchan & 0xffffff)) return memcpy(outbuf,"-",2),1;
+ return snprintf(outbuf, COLWID, "%x", (unsigned)(pp->wchan) & 0xffffffu);
+}
+
+/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */
+/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */
+static int pr_tty4(char *restrict const outbuf, const proc_t *restrict const pp){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+ return dev_to_tty(outbuf, 4, pp->tty, pp->XXXID, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS);
+}
+
+/* Unix98: format is unspecified, but must match that used by who(1). */
+static int pr_tty8(char *restrict const outbuf, const proc_t *restrict const pp){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+ return dev_to_tty(outbuf, COLWID, pp->tty, pp->XXXID, ABBREV_DEV);
+}
+
+#if 0
+/* This BSD state display may contain spaces, which is illegal. */
+static int pr_oldstate(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%s", status(pp));
+}
+#endif
+
+// This state display is Unix98 compliant and has lots of info like BSD.
+static int pr_stat(char *restrict const outbuf, const proc_t *restrict const pp){
+ int end = 0;
+ outbuf[end++] = pp->state;
+// if(pp->rss==0 && pp->state!='Z') outbuf[end++] = 'W'; // useless "swapped out"
+ if(pp->nice < 0) outbuf[end++] = '<';
+ if(pp->nice > 0) outbuf[end++] = 'N';
+// In this order, NetBSD would add:
+// traced 'X'
+// systrace 'x'
+// exiting 'E' (not printed for zombies)
+// vforked 'V'
+// system 'K' (and do not print 'L' too)
+ if(pp->vm_lock) outbuf[end++] = 'L';
+ if(pp->session == pp->tgid) outbuf[end++] = 's'; // session leader
+ if(pp->nlwp > 1) outbuf[end++] = 'l'; // multi-threaded
+ if(pp->pgrp == pp->tpgid) outbuf[end++] = '+'; // in foreground process group
+ outbuf[end] = '\0';
+ return end;
+}
+
+/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */
+static int pr_s(char *restrict const outbuf, const proc_t *restrict const pp){
+ outbuf[0] = pp->state;
+ outbuf[1] = '\0';
+ return 1;
+}
+
+static int pr_flag(char *restrict const outbuf, const proc_t *restrict const pp){
+ /* Unix98 requires octal flags */
+ /* this user-hostile and volatile junk gets 1 character */
+ return snprintf(outbuf, COLWID, "%o", (unsigned)(pp->flags>>6U)&0x7U);
+}
+
+// plus these: euid,ruid,egroup,rgroup (elsewhere in this file)
+
+/*********** non-standard ***********/
+
+/*** BSD
+sess session pointer
+(SCO has:Process session leader ID as a decimal value. (SESSION))
+jobc job control count
+cpu short-term cpu usage factor (for scheduling)
+sl sleep time (in seconds; 127 = infinity)
+re core residency time (in seconds; 127 = infinity)
+pagein pageins (same as majflt)
+lim soft memory limit
+tsiz text size (in Kbytes)
+***/
+
+static int pr_stackp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->start_stack));
+}
+
+static int pr_esp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_esp));
+}
+
+static int pr_eip(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%08x", (unsigned)(pp->kstk_eip));
+}
+
+/* This function helps print old-style time formats */
+static int old_time_helper(char *dst, unsigned long long t, unsigned long long rel) {
+ if(!t) return snprintf(dst, COLWID, " -");
+ if(t == ~0ULL) return snprintf(dst, COLWID, " xx");
+ if((long long)(t-=rel) < 0) t=0ULL;
+ if(t>9999ULL) return snprintf(dst, COLWID, "%5Lu", t/100ULL);
+ else return snprintf(dst, COLWID, "%2u.%02u", (unsigned)t/100U, (unsigned)t%100U);
+}
+
+static int pr_bsdtime(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long long t;
+ unsigned u;
+ t = pp->utime + pp->stime;
+ if(include_dead_children) t += (pp->cutime + pp->cstime);
+ u = t / Hertz;
+ return snprintf(outbuf, COLWID, "%3u:%02u", u/60U, u%60U);
+}
+
+static int pr_bsdstart(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t start;
+ time_t seconds_ago;
+ start = time_of_boot + pp->start_time / Hertz;
+ seconds_ago = seconds_since_1970 - start;
+ if(seconds_ago < 0) seconds_ago=0;
+ if(seconds_ago > 3600*24) strcpy(outbuf, ctime(&start)+4);
+ else strcpy(outbuf, ctime(&start)+10);
+ outbuf[6] = '\0';
+ return 6;
+}
+
+static int pr_alarm(char *restrict const outbuf, const proc_t *restrict const pp){
+ return old_time_helper(outbuf, pp->alarm, 0ULL);
+}
+
+/* HP-UX puts this in pages and uses "vsz" for kB */
+static int pr_sz(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(page_size/1024));
+}
+
+
+/*
+ * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss
+ * I suspect some/all of those are broken. They seem to have been
+ * inherited by Linux and AIX from early BSD systems. FreeBSD only
+ * retains tsiz. The prefixed versions come from Debian.
+ * Sun and Digital have none of this crap. The code here comes
+ * from an old Linux ps, and might not be correct for ELF executables.
+ *
+ * AIX TRS size of resident-set (real memory) of text
+ * AIX TSIZ size of text (shared-program) image
+ * FreeBSD tsiz text size (in Kbytes)
+ * 4.3BSD NET/2 trss text resident set size (in Kbytes)
+ * 4.3BSD NET/2 tsiz text size (in Kbytes)
+ */
+
+/* kB data size. See drs, tsiz & trs. */
+static int pr_dsiz(char *restrict const outbuf, const proc_t *restrict const pp){
+ long dsiz = 0;
+ if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", dsiz);
+}
+
+/* kB text (code) size. See trs, dsiz & drs. */
+static int pr_tsiz(char *restrict const outbuf, const proc_t *restrict const pp){
+ long tsiz = 0;
+ if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", tsiz);
+}
+
+/* kB _resident_ data size. See dsiz, tsiz & trs. */
+static int pr_drs(char *restrict const outbuf, const proc_t *restrict const pp){
+ long drs = 0;
+ if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", drs);
+}
+
+/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */
+static int pr_trs(char *restrict const outbuf, const proc_t *restrict const pp){
+ long trs = 0;
+ if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10;
+ return snprintf(outbuf, COLWID, "%ld", trs);
+}
+
+/* approximation to: kB of address space that could end up in swap */
+static int pr_swapable(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->vm_data + pp->vm_stack);
+}
+
+/* nasty old Debian thing */
+static int pr_size(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%ld", pp->size);
+}
+
+
+static int pr_minflt(char *restrict const outbuf, const proc_t *restrict const pp){
+ long flt = pp->min_flt;
+ if(include_dead_children) flt += pp->cmin_flt;
+ return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_majflt(char *restrict const outbuf, const proc_t *restrict const pp){
+ long flt = pp->maj_flt;
+ if(include_dead_children) flt += pp->cmaj_flt;
+ return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_lim(char *restrict const outbuf, const proc_t *restrict const pp){
+ if(pp->rss_rlim == RLIM_INFINITY){
+ outbuf[0] = 'x';
+ outbuf[1] = 'x';
+ outbuf[2] = '\0';
+ return 2;
+ }
+ return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10);
+}
+
+/* should print leading tilde ('~') if process is bound to the CPU */
+static int pr_psr(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->processor);
+}
+
+static int pr_rss(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
+}
+
+/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
+static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const pp){
+ unsigned long pmem = 0;
+ pmem = pp->vm_rss * 1000ULL / kb_main_total;
+ if (pmem > 999) pmem = 999;
+ return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
+}
+
+static int pr_lstart(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ t = time_of_boot + pp->start_time / Hertz;
+ return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
+}
+
+/* Unix98 specifies a STIME header for a column that shows the start
+ * time of the process, but does not specify a format or format specifier.
+ * From the general Unix98 rules, we know there must not be any spaces.
+ * Most systems violate that rule, though the Solaris documentation
+ * claims to print the column without spaces. (NOT!)
+ *
+ * So this isn't broken, but could be renamed to u98_std_stime,
+ * as long as it still shows as STIME when using the -f option.
+ */
+static int pr_stime(char *restrict const outbuf, const proc_t *restrict const pp){
+ struct tm *proc_time;
+ struct tm *our_time;
+ time_t t;
+ const char *fmt;
+ int tm_year;
+ int tm_yday;
+ our_time = localtime(&seconds_since_1970); /* not reentrant */
+ tm_year = our_time->tm_year;
+ tm_yday = our_time->tm_yday;
+ t = time_of_boot + pp->start_time / Hertz;
+ proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
+ fmt = "%H:%M"; /* 03:02 23:59 */
+ if(tm_yday != proc_time->tm_yday) fmt = "%b%d"; /* Jun06 Aug27 */
+ if(tm_year != proc_time->tm_year) fmt = "%Y"; /* 1991 2001 */
+ return strftime(outbuf, 42, fmt, proc_time);
+}
+
+static int pr_start(char *restrict const outbuf, const proc_t *restrict const pp){
+ time_t t;
+ char *str;
+ t = time_of_boot + pp->start_time / Hertz;
+ str = ctime(&t);
+ if(str[8]==' ') str[8]='0';
+ if(str[11]==' ') str[11]='0';
+ if((unsigned long)t+60*60*24 > seconds_since_1970)
+ return snprintf(outbuf, COLWID, "%8.8s", str+11);
+ return snprintf(outbuf, COLWID, " %6.6s", str+4);
+}
+
+
+#ifdef SIGNAL_STRING
+static int help_pr_sig(char *restrict const outbuf, const char *restrict const sig){
+ long len = 0;
+ len = strlen(sig);
+ if(wide_signals){
+ if(len>8) return snprintf(outbuf, COLWID, "%s", sig);
+ return snprintf(outbuf, COLWID, "00000000%s", sig);
+ }
+ if(len-strspn(sig,"0") > 8)
+ return snprintf(outbuf, COLWID, "<%s", sig+len-8);
+ return snprintf(outbuf, COLWID, "%s", sig+len-8);
+}
+#else
+static int help_pr_sig(unsigned long long sig){
+ if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig);
+ if(sig>>32) return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL);
+ return snprintf(outbuf, COLWID, "%08Lx", sig&0xffffffffLL);
+}
+#endif
+
+// This one is always thread-specific pending. (from Dragonfly BSD)
+static int pr_tsig(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->_sigpnd);
+}
+// This one is (wrongly?) thread-specific when printing thread lines,
+// but process-pending otherwise.
+static int pr_sig(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->signal);
+}
+static int pr_sigmask(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->blocked);
+}
+static int pr_sigignore(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->sigignore);
+}
+static int pr_sigcatch(char *restrict const outbuf, const proc_t *restrict const pp){
+ return help_pr_sig(outbuf, pp->sigcatch);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * internal terms: ruid euid suid fuid
+ * kernel vars: uid euid suid fsuid
+ * command args: ruid uid svuid n/a
+ */
+
+static int pr_egid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->egid);
+}
+static int pr_rgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->rgid);
+}
+static int pr_sgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->sgid);
+}
+static int pr_fgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->fgid);
+}
+
+static int pr_euid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->euid);
+}
+static int pr_ruid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->ruid);
+}
+static int pr_suid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->suid);
+}
+static int pr_fuid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->fuid);
+}
+
+// The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
+// requires that user and group names print as decimal numbers if there is
+// not enough room in the column, so tough luck if you don't like it.
+//
+// The UNIX and POSIX way to change column width is to rename it:
+// ps -o pid,user=CumbersomeUserNames -o comm
+// The easy way is to directly specify the desired width:
+// ps -o pid,user:19,comm
+//
+static int do_pr_name(char *restrict const outbuf, const char *restrict const name, unsigned u){
+ if(!user_is_number){
+ int rightward = OUTBUF_SIZE; /* max cells */
+ int len; /* real cells */
+
+ escape_str(outbuf, name, OUTBUF_SIZE, &rightward);
+ len = OUTBUF_SIZE-rightward;
+
+ if(len <= (int)max_rightward)
+ return len; /* returns number of cells */
+ }
+ return snprintf(outbuf, COLWID, "%u", u);
+}
+
+static int pr_ruser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->ruser, pp->ruid);
+}
+static int pr_euser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->euser, pp->euid);
+}
+static int pr_fuser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->fuser, pp->fuid);
+}
+static int pr_suser(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->suser, pp->suid);
+}
+
+static int pr_egroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->egroup, pp->egid);
+}
+static int pr_rgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->rgroup, pp->rgid);
+}
+static int pr_fgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->fgroup, pp->fgid);
+}
+static int pr_sgroup(char *restrict const outbuf, const proc_t *restrict const pp){
+ return do_pr_name(outbuf, pp->sgroup, pp->sgid);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+// TID tid LWP lwp SPID spid
+static int pr_thread(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->tid);
+}
+// thcount THCNT
+static int pr_nlwp(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->nlwp);
+}
+
+static int pr_sess(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%u", pp->session);
+}
+static int pr_tpgid(char *restrict const outbuf, const proc_t *restrict const pp){
+ return snprintf(outbuf, COLWID, "%d", pp->tpgid);
+}
+
+
+/* SGI uses "cpu" to print the processor ID with header "P" */
+static int pr_sgi_p(char *restrict const outbuf, const proc_t *restrict const pp){ /* FIXME */
+ if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor);
+ return snprintf(outbuf, COLWID, "*");
+}
+
+
+/****************** FLASK & seLinux security stuff **********************/
+// move the bulk of this to libproc sometime
+
+static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
+ char filename[48];
+ size_t len;
+ ssize_t num_read;
+ int fd;
+
+// wchan file is suitable for testing
+//snprintf(filename, sizeof filename, "/proc/%d/wchan", pp->tgid);
+snprintf(filename, sizeof filename, "/proc/%d/attr/current", pp->tgid);
+
+ fd = open(filename, O_RDONLY, 0);
+ if(likely(fd==-1)) goto fail;
+ num_read = read(fd, outbuf, 666);
+ close(fd);
+ if(unlikely(num_read<=0)) goto fail;
+ outbuf[num_read] = '\0';
+
+ len = 0;
+ while(outbuf[len]>' ' && outbuf[len]<='~') len++;
+ outbuf[len] = '\0';
+ if(len) return len;
+
+fail:
+ outbuf[0] = '-';
+ outbuf[1] = '\0';
+ return 1;
+}
+
+#if 0
+// This needs more study, considering:
+// 1. the static linking option (maybe disable this in that case)
+// 2. the -z and -Z option issue
+// 3. width of output
+static int pr_context(char *restrict const outbuf, const proc_t *restrict const pp){
+ static int (*ps_getpidcon)(pid_t pid, char **context) = 0;
+ static int tried_load = 0;
+ size_t len;
+ char *context;
+
+ if(!ps_getpidcon && !tried_load){
+ void *handle = dlopen("libselinux.so.1", RTLD_NOW);
+ if(handle){
+ dlerror();
+ ps_getpidcon = dlsym(handle, "getpidcon");
+ if(dlerror())
+ ps_getpidcon = 0;
+ }
+ tried_load++;
+ }
+ if(ps_getpidcon && !ps_getpidcon(pp->tgid, &context)){
+ size_t max_len = OUTBUF_SIZE-1;
+ len = strlen(context);
+ if(len > max_len) len = max_len;
+ memcpy(outbuf, context, len);
+ outbuf[len] = '\0';
+ free(context);
+ }else{
+ outbuf[0] = '-';
+ outbuf[1] = '\0';
+ len = 1;
+ }
+ return len;
+}
+#endif
+
+
+////////////////////////////// Test code /////////////////////////////////
+
+// like "args"
+static int pr_t_unlimited(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"[123456789-12345] <defunct>","ps","123456789-123456"};
+ (void)pp;
+ snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%3u]);
+ return strlen(outbuf);
+}
+static int pr_t_unlimited2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"unlimited", "[123456789-12345] <defunct>","ps","123456789-123456"};
+ (void)pp;
+ snprintf(outbuf, max_rightward+1, "%s", vals[lines_to_next_header%4u]);
+ return strlen(outbuf);
+}
+
+// like "etime"
+static int pr_t_right(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59","59:59"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
+}
+static int pr_t_right2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"999-23:59:59","99-23:59:59","9-23:59:59"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%3u]);
+}
+
+// like "tty"
+static int pr_t_left(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"tty7","pts/9999","iseries/vtty42","ttySMX0","3270/tty4"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%5u]);
+}
+static int pr_t_left2(char *restrict const outbuf, const proc_t *restrict const pp){
+ static const char *const vals[] = {"tty7","pts/9999","ttySMX0","3270/tty4"};
+ (void)pp;
+ return snprintf(outbuf, COLWID, "%s", vals[lines_to_next_header%4u]);
+}
+
+/***************************************************************************/
+/*************************** other stuff ***********************************/
+
+/*
+ * Old header specifications.
+ *
+ * short Up " PID TTY STAT TIME COMMAND"
+ * long l Pp " FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND
+ * user u up "USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND
+ * jobs j gPp " PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
+ * sig s p " UID PID SIGNAL BLOCKED IGNORED CATCHED STAT TTY TIME COMMAND
+ * vm v r " PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMAND
+ * m m r " PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND
+ * regs X p "NR PID STACK ESP EIP TMOUT ALARM STAT TTY TIME COMMAND
+ */
+
+/*
+ * Unix98 requires that the heading for tty is TT, though XPG4, Digital,
+ * and BSD use TTY. The Unix98 headers are:
+ * args,comm,etime,group,nice,pcpu,pgid
+ * pid,ppid,rgroup,ruser,time,tty,user,vsz
+ *
+ * BSD c: "command" becomes accounting name ("comm" or "ucomm")
+ * BSD n: "user" becomes "uid" and "wchan" becomes "nwchan" (number)
+ */
+
+/* Justification control for flags field. */
+#define USER CF_USER // left if text, right if numeric
+#define LEFT CF_LEFT
+#define RIGHT CF_RIGHT
+#define UNLIMITED CF_UNLIMITED
+#define WCHAN CF_WCHAN // left if text, right if numeric
+#define SIGNAL CF_SIGNAL // right in 9, or 16 if room
+#define PIDMAX CF_PIDMAX
+#define TO CF_PRINT_THREAD_ONLY
+#define PO CF_PRINT_PROCESS_ONLY
+#define ET CF_PRINT_EVERY_TIME
+#define AN CF_PRINT_AS_NEEDED // no idea
+
+/* short names to save space */
+#define MEM PROC_FILLMEM /* read statm */
+#define ARG PROC_FILLARG /* read cmdline (cleared if c option) */
+#define COM PROC_FILLCOM /* read cmdline (cleared if not -f option) */
+#define ENV PROC_FILLENV /* read environ */
+#define USR PROC_FILLUSR /* uid_t -> user names */
+#define GRP PROC_FILLGRP /* gid_t -> group names */
+#define WCH PROC_FILLWCHAN /* do WCHAN lookup */
+
+
+/* TODO
+ * pull out annoying BSD aliases into another table (to macro table?)
+ * add sorting functions here (to unify names)
+ */
+
+/* temporary hack -- mark new stuff grabbed from Debian ps */
+#define LNx LNX
+
+/* there are about 211 listed */
+
+/* Many of these are placeholders for unsupported options. */
+static const format_struct format_array[] = {
+/* code header print() sort() width need vendor flags */
+{"%cpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, BSD, ET|RIGHT}, /*pcpu*/
+{"%mem", "%MEM", pr_pmem, sr_nop, 4, 0, BSD, PO|RIGHT}, /*pmem*/
+{"_left", "LLLLLLLL", pr_t_left, sr_nop, 8, 0, TST, ET|LEFT},
+{"_left2", "L2L2L2L2", pr_t_left2, sr_nop, 8, 0, TST, ET|LEFT},
+{"_right", "RRRRRRRRRRR", pr_t_right, sr_nop, 11, 0, TST, ET|RIGHT},
+{"_right2", "R2R2R2R2R2R", pr_t_right2, sr_nop, 11, 0, TST, ET|RIGHT},
+{"_unlimited","U", pr_t_unlimited, sr_nop, 16, 0, TST, ET|UNLIMITED},
+{"_unlimited2","U2", pr_t_unlimited2, sr_nop, 16, 0, TST, ET|UNLIMITED},
+{"acflag", "ACFLG", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT}, /*acflg*/
+{"acflg", "ACFLG", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*acflag*/
+{"addr", "ADDR", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"addr_1", "ADDR", pr_nop, sr_nop, 1, 0, LNX, AN|LEFT},
+{"alarm", "ALARM", pr_alarm, sr_alarm, 5, 0, LNX, AN|RIGHT},
+{"argc", "ARGC", pr_nop, sr_nop, 4, 0, LNX, PO|RIGHT},
+{"args", "COMMAND", pr_args, sr_cmd, 27, ARG, U98, PO|UNLIMITED}, /*command*/
+{"atime", "TIME", pr_time, sr_nop, 8, 0, SOE, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"blocked", "BLOCKED", pr_sigmask, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigmask*/
+{"bnd", "BND", pr_nop, sr_nop, 1, 0, AIX, TO|RIGHT},
+{"bsdstart", "START", pr_bsdstart, sr_nop, 6, 0, LNX, ET|RIGHT},
+{"bsdtime", "TIME", pr_bsdtime, sr_nop, 6, 0, LNX, ET|RIGHT},
+{"c", "C", pr_c, sr_pcpu, 2, 0, SUN, ET|RIGHT},
+{"caught", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigcatch*/
+{"class", "CLS", pr_class, sr_sched, 3, 0, XXX, TO|LEFT},
+{"cls", "CLS", pr_class, sr_sched, 3, 0, HPU, TO|RIGHT}, /*says HPUX or RT*/
+{"cmaj_flt", "-", pr_nop, sr_cmaj_flt, 1, 0, LNX, AN|RIGHT},
+{"cmd", "CMD", pr_args, sr_cmd, 27, ARG, DEC, PO|UNLIMITED}, /*ucomm*/
+{"cmin_flt", "-", pr_nop, sr_cmin_flt, 1, 0, LNX, AN|RIGHT},
+{"cnswap", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT},
+{"comm", "COMMAND", pr_comm, sr_cmd, 15, COM, U98, PO|UNLIMITED}, /*ucomm*/
+{"command", "COMMAND", pr_args, sr_cmd, 27, ARG, XXX, PO|UNLIMITED}, /*args*/
+{"context", "CONTEXT", pr_context, sr_nop, 31, 0, LNX, ET|LEFT},
+{"cp", "CP", pr_cp, sr_pcpu, 3, 0, DEC, ET|RIGHT}, /*cpu*/
+{"cpu", "CPU", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
+{"cpuid", "CPUID", pr_psr, sr_nop, 5, 0, BSD, TO|RIGHT}, // OpenBSD: 8 wide!
+{"cputime", "TIME", pr_time, sr_nop, 8, 0, DEC, ET|RIGHT}, /*time*/
+{"cstime", "-", pr_nop, sr_cstime, 1, 0, LNX, AN|RIGHT},
+{"ctid", "CTID", pr_nop, sr_nop, 5, 0, SUN, ET|RIGHT}, // resource contracts?
+{"cursig", "CURSIG", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"cutime", "-", pr_nop, sr_cutime, 1, 0, LNX, AN|RIGHT},
+{"cwd", "CWD", pr_nop, sr_nop, 3, 0, LNX, AN|LEFT},
+{"drs", "DRS", pr_drs, sr_drs, 5, MEM, LNX, PO|RIGHT},
+{"dsiz", "DSIZ", pr_dsiz, sr_nop, 4, 0, LNX, PO|RIGHT},
+{"egid", "EGID", pr_egid, sr_egid, 5, 0, LNX, ET|RIGHT},
+{"egroup", "EGROUP", pr_egroup, sr_egroup, 8, GRP, LNX, ET|USER},
+{"eip", "EIP", pr_eip, sr_kstk_eip, 8, 0, LNX, TO|RIGHT},
+{"emul", "EMUL", pr_nop, sr_nop, 13, 0, BSD, PO|LEFT}, /* "FreeBSD ELF32" and such */
+{"end_code", "E_CODE", pr_nop, sr_end_code, 8, 0, LNx, PO|RIGHT},
+{"environ","ENVIRONMENT",pr_nop, sr_nop, 11, ENV, LNx, PO|UNLIMITED},
+{"esp", "ESP", pr_esp, sr_kstk_esp, 8, 0, LNX, TO|RIGHT},
+{"etime", "ELAPSED", pr_etime, sr_nop, 11, 0, U98, ET|RIGHT}, /* was 7 wide */
+{"euid", "EUID", pr_euid, sr_euid, 5, 0, LNX, ET|RIGHT},
+{"euser", "EUSER", pr_euser, sr_euser, 8, USR, LNX, ET|USER},
+{"f", "F", pr_flag, sr_flags, 1, 0, XXX, ET|RIGHT}, /*flags*/
+{"fgid", "FGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
+{"fgroup", "FGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
+{"flag", "F", pr_flag, sr_flags, 1, 0, DEC, ET|RIGHT},
+{"flags", "F", pr_flag, sr_flags, 1, 0, BSD, ET|RIGHT}, /*f*/ /* was FLAGS, 8 wide */
+{"fname", "COMMAND", pr_fname, sr_nop, 8, 0, SUN, PO|LEFT},
+{"fsgid", "FSGID", pr_fgid, sr_fgid, 5, 0, LNX, ET|RIGHT},
+{"fsgroup", "FSGROUP", pr_fgroup, sr_fgroup, 8, GRP, LNX, ET|USER},
+{"fsuid", "FSUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
+{"fsuser", "FSUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
+{"fuid", "FUID", pr_fuid, sr_fuid, 5, 0, LNX, ET|RIGHT},
+{"fuser", "FUSER", pr_fuser, sr_fuser, 8, USR, LNX, ET|USER},
+{"gid", "GID", pr_egid, sr_egid, 5, 0, SUN, ET|RIGHT},
+{"group", "GROUP", pr_egroup, sr_egroup, 8, GRP, U98, ET|USER},
+{"ignored", "IGNORED", pr_sigignore,sr_nop, 9, 0, BSD, TO|SIGNAL}, /*sigignore*/
+{"inblk", "INBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*inblock*/
+{"inblock", "INBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*inblk*/
+{"intpri", "PRI", pr_opri, sr_priority, 3, 0, HPU, TO|RIGHT},
+{"jid", "JID", pr_nop, sr_nop, 1, 0, SGI, PO|RIGHT},
+{"jobc", "JOBC", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"ktrace", "KTRACE", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"ktracep", "KTRACEP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"label", "LABEL", pr_context, sr_nop, 31, 0, SGI, ET|LEFT},
+{"lastcpu", "C", pr_psr, sr_nop, 3, 0, BSD, TO|RIGHT}, // DragonFly
+{"lim", "LIM", pr_lim, sr_rss_rlim, 5, 0, BSD, AN|RIGHT},
+{"login", "LOGNAME", pr_nop, sr_nop, 8, 0, BSD, AN|LEFT}, /*logname*/ /* double check */
+{"logname", "LOGNAME", pr_nop, sr_nop, 8, 0, XXX, AN|LEFT}, /*login*/
+{"longtname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
+{"lstart", "STARTED", pr_lstart, sr_nop, 24, 0, XXX, ET|RIGHT},
+{"luid", "LUID", pr_nop, sr_nop, 5, 0, LNX, ET|RIGHT}, /* login ID */
+{"luser", "LUSER", pr_nop, sr_nop, 8, USR, LNX, ET|USER}, /* login USER */
+{"lwp", "LWP", pr_thread, sr_tid, 5, 0, SUN, TO|PIDMAX|RIGHT},
+{"m_drs", "DRS", pr_drs, sr_drs, 5, MEM, LNx, PO|RIGHT},
+{"m_dt", "DT", pr_nop, sr_dt, 4, MEM, LNx, PO|RIGHT},
+{"m_lrs", "LRS", pr_nop, sr_lrs, 5, MEM, LNx, PO|RIGHT},
+{"m_resident", "RES", pr_nop, sr_resident, 5,MEM, LNx, PO|RIGHT},
+{"m_share", "SHRD", pr_nop, sr_share, 5, MEM, LNx, PO|RIGHT},
+{"m_size", "SIZE", pr_size, sr_size, 5, MEM, LNX, PO|RIGHT},
+{"m_swap", "SWAP", pr_nop, sr_nop, 5, 0, LNx, PO|RIGHT},
+{"m_trs", "TRS", pr_trs, sr_trs, 5, MEM, LNx, PO|RIGHT},
+{"maj_flt", "MAJFL", pr_majflt, sr_maj_flt, 6, 0, LNX, AN|RIGHT},
+{"majflt", "MAJFLT", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
+{"min_flt", "MINFL", pr_minflt, sr_min_flt, 6, 0, LNX, AN|RIGHT},
+{"minflt", "MINFLT", pr_minflt, sr_min_flt, 6, 0, XXX, AN|RIGHT},
+{"msgrcv", "MSGRCV", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
+{"msgsnd", "MSGSND", pr_nop, sr_nop, 6, 0, XXX, AN|RIGHT},
+{"mwchan", "MWCHAN", pr_nop, sr_nop, 6, WCH, BSD, TO|WCHAN}, /* mutex (FreeBSD) */
+{"ni", "NI", pr_nice, sr_nice, 3, 0, BSD, TO|RIGHT}, /*nice*/
+{"nice", "NI", pr_nice, sr_nice, 3, 0, U98, TO|RIGHT}, /*ni*/
+{"nivcsw", "IVCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nlwp", "NLWP", pr_nlwp, sr_nlwp, 4, 0, SUN, PO|RIGHT},
+{"nsignals", "NSIGS", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*nsigs*/
+{"nsigs", "NSIGS", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*nsignals*/
+{"nswap", "NSWAP", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nvcsw", "VCSW", pr_nop, sr_nop, 5, 0, XXX, AN|RIGHT},
+{"nwchan", "WCHAN", pr_nwchan, sr_nop, 6, 0, XXX, TO|RIGHT},
+{"opri", "PRI", pr_opri, sr_priority, 3, 0, SUN, TO|RIGHT},
+{"osz", "SZ", pr_nop, sr_nop, 2, 0, SUN, PO|RIGHT},
+{"oublk", "OUBLK", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT}, /*oublock*/
+{"oublock", "OUBLK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT}, /*oublk*/
+{"p_ru", "P_RU", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
+{"paddr", "PADDR", pr_nop, sr_nop, 6, 0, BSD, AN|RIGHT},
+{"pagein", "PAGEIN", pr_majflt, sr_maj_flt, 6, 0, XXX, AN|RIGHT},
+{"pcpu", "%CPU", pr_pcpu, sr_pcpu, 4, 0, U98, ET|RIGHT}, /*%cpu*/
+{"pending", "PENDING", pr_sig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /*sig*/
+{"pgid", "PGID", pr_pgid, sr_pgrp, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pgrp", "PGRP", pr_pgid, sr_pgrp, 5, 0, LNX, PO|PIDMAX|RIGHT},
+{"pid", "PID", pr_pid, sr_tgid, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pmem", "%MEM", pr_pmem, sr_nop, 4, 0, XXX, PO|RIGHT}, /*%mem*/
+{"poip", "-", pr_nop, sr_nop, 1, 0, BSD, AN|RIGHT},
+{"policy", "POL", pr_class, sr_sched, 3, 0, DEC, TO|LEFT},
+{"ppid", "PPID", pr_ppid, sr_ppid, 5, 0, U98, PO|PIDMAX|RIGHT},
+{"pri", "PRI", pr_pri, sr_nop, 3, 0, XXX, TO|RIGHT},
+{"pri_api", "API", pr_pri_api, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_bar", "BAR", pr_pri_bar, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_baz", "BAZ", pr_pri_baz, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"pri_foo", "FOO", pr_pri_foo, sr_nop, 3, 0, LNX, TO|RIGHT},
+{"priority", "PRI", pr_priority, sr_priority, 3, 0, LNX, TO|RIGHT},
+{"prmgrp", "PRMGRP", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
+{"prmid", "PRMID", pr_nop, sr_nop, 12, 0, HPU, PO|RIGHT},
+{"project", "PROJECT", pr_nop, sr_nop, 12, 0, SUN, PO|LEFT}, // see prm* andctid
+{"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT},
+{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT},
+{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT},
+{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT},
+{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT},
+{"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT},
+{"rgid", "RGID", pr_rgid, sr_rgid, 5, 0, XXX, ET|RIGHT},
+{"rgroup", "RGROUP", pr_rgroup, sr_rgroup, 8, GRP, U98, ET|USER}, /* was 8 wide */
+{"rlink", "RLINK", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"rss", "RSS", pr_rss, sr_rss, 5, 0, XXX, PO|RIGHT}, /* was 5 wide */
+{"rssize", "RSS", pr_rss, sr_vm_rss, 5, 0, DEC, PO|RIGHT}, /*rsz*/
+{"rsz", "RSZ", pr_rss, sr_vm_rss, 5, 0, BSD, PO|RIGHT}, /*rssize*/
+{"rtprio", "RTPRIO", pr_rtprio, sr_rtprio, 6, 0, BSD, TO|RIGHT},
+{"ruid", "RUID", pr_ruid, sr_ruid, 5, 0, XXX, ET|RIGHT},
+{"ruser", "RUSER", pr_ruser, sr_ruser, 8, USR, U98, ET|USER},
+{"s", "S", pr_s, sr_state, 1, 0, SUN, TO|LEFT}, /*stat,state*/
+{"sched", "SCH", pr_sched, sr_sched, 3, 0, AIX, TO|RIGHT},
+{"scnt", "SCNT", pr_nop, sr_nop, 4, 0, DEC, AN|RIGHT}, /* man page misspelling of scount? */
+{"scount", "SC", pr_nop, sr_nop, 4, 0, AIX, AN|RIGHT}, /* scnt==scount, DEC claims both */
+{"sess", "SESS", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT},
+{"session", "SESS", pr_sess, sr_session, 5, 0, LNX, PO|PIDMAX|RIGHT},
+{"sgi_p", "P", pr_sgi_p, sr_nop, 1, 0, LNX, TO|RIGHT}, /* "cpu" number */
+{"sgi_rss", "RSS", pr_rss, sr_nop, 4, 0, LNX, PO|LEFT}, /* SZ:RSS */
+{"sgid", "SGID", pr_sgid, sr_sgid, 5, 0, LNX, ET|RIGHT},
+{"sgroup", "SGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
+{"share", "-", pr_nop, sr_share, 1, MEM, LNX, PO|RIGHT},
+{"sid", "SID", pr_sess, sr_session, 5, 0, XXX, PO|PIDMAX|RIGHT}, /* Sun & HP */
+{"sig", "PENDING", pr_sig, sr_nop, 9, 0, XXX, ET|SIGNAL}, /*pending -- Dragonfly uses this for whole-proc and "tsig" for thread */
+{"sig_block", "BLOCKED", pr_sigmask, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_catch", "CATCHED", pr_sigcatch, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_ignore", "IGNORED",pr_sigignore, sr_nop, 9, 0, LNX, TO|SIGNAL},
+{"sig_pend", "SIGNAL", pr_sig, sr_nop, 9, 0, LNX, ET|SIGNAL},
+{"sigcatch", "CAUGHT", pr_sigcatch, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*caught*/
+{"sigignore", "IGNORED", pr_sigignore,sr_nop, 9, 0, XXX, TO|SIGNAL}, /*ignored*/
+{"sigmask", "BLOCKED", pr_sigmask, sr_nop, 9, 0, XXX, TO|SIGNAL}, /*blocked*/
+{"size", "SZ", pr_swapable, sr_swapable, 5, 0, SCO, PO|RIGHT},
+{"sl", "SL", pr_nop, sr_nop, 3, 0, XXX, AN|RIGHT},
+{"spid", "SPID", pr_thread, sr_tid, 5, 0, SGI, TO|PIDMAX|RIGHT},
+{"stackp", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*start_stack*/
+{"start", "STARTED", pr_start, sr_nop, 8, 0, XXX, ET|RIGHT},
+{"start_code", "S_CODE", pr_nop, sr_start_code, 8, 0, LNx, PO|RIGHT},
+{"start_stack", "STACKP", pr_stackp, sr_start_stack, 8, 0, LNX, PO|RIGHT}, /*stackp*/
+{"start_time", "START", pr_stime, sr_start_time, 5, 0, LNx, ET|RIGHT},
+{"stat", "STAT", pr_stat, sr_state, 4, 0, BSD, TO|LEFT}, /*state,s*/
+{"state", "S", pr_s, sr_state, 1, 0, XXX, TO|LEFT}, /*stat,s*/ /* was STAT */
+{"status", "STATUS", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"stime", "STIME", pr_stime, sr_stime, 5, 0, XXX, ET|RIGHT}, /* was 6 wide */
+{"suid", "SUID", pr_suid, sr_suid, 5, 0, LNx, ET|RIGHT},
+{"suser", "SUSER", pr_suser, sr_suser, 8, USR, LNx, ET|USER},
+{"svgid", "SVGID", pr_sgid, sr_sgid, 5, 0, XXX, ET|RIGHT},
+{"svgroup", "SVGROUP", pr_sgroup, sr_sgroup, 8, GRP, LNX, ET|USER},
+{"svuid", "SVUID", pr_suid, sr_suid, 5, 0, XXX, ET|RIGHT},
+{"svuser", "SVUSER", pr_suser, sr_suser, 8, USR, LNX, ET|USER},
+{"systime", "SYSTEM", pr_nop, sr_nop, 6, 0, DEC, ET|RIGHT},
+{"sz", "SZ", pr_sz, sr_nop, 5, 0, HPU, PO|RIGHT},
+{"taskid", "TASKID", pr_nop, sr_nop, 5, 0, SUN, TO|PIDMAX|RIGHT}, // is this a thread ID?
+{"tdev", "TDEV", pr_nop, sr_nop, 4, 0, XXX, AN|RIGHT},
+{"thcount", "THCNT", pr_nlwp, sr_nlwp, 5, 0, AIX, PO|RIGHT},
+{"tid", "TID", pr_thread, sr_tid, 5, 0, AIX, TO|PIDMAX|RIGHT},
+{"time", "TIME", pr_time, sr_nop, 8, 0, U98, ET|RIGHT}, /*cputime*/ /* was 6 wide */
+{"timeout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+{"tmout", "TMOUT", pr_nop, sr_nop, 5, 0, LNX, AN|RIGHT}, // 2.0.xx era
+{"tname", "TTY", pr_tty8, sr_tty, 8, 0, DEC, PO|LEFT},
+{"tpgid", "TPGID", pr_tpgid, sr_tpgid, 5, 0, XXX, PO|PIDMAX|RIGHT},
+{"trs", "TRS", pr_trs, sr_trs, 4, MEM, AIX, PO|RIGHT},
+{"trss", "TRSS", pr_trs, sr_trs, 4, MEM, BSD, PO|RIGHT}, /* 4.3BSD NET/2 */
+{"tsess", "TSESS", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
+{"tsession", "TSESS", pr_nop, sr_nop, 5, 0, DEC, PO|PIDMAX|RIGHT},
+{"tsid", "TSID", pr_nop, sr_nop, 5, 0, BSD, PO|PIDMAX|RIGHT},
+{"tsig", "PENDING", pr_tsig, sr_nop, 9, 0, BSD, ET|SIGNAL}, /* Dragonfly used this for thread-specific, and "sig" for whole-proc */
+{"tsiz", "TSIZ", pr_tsiz, sr_nop, 4, 0, BSD, PO|RIGHT},
+{"tt", "TT", pr_tty8, sr_tty, 8, 0, BSD, PO|LEFT},
+{"tty", "TT", pr_tty8, sr_tty, 8, 0, U98, PO|LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */ /* was 3 wide */
+{"tty4", "TTY", pr_tty4, sr_tty, 4, 0, LNX, PO|LEFT},
+{"tty8", "TTY", pr_tty8, sr_tty, 8, 0, LNX, PO|LEFT},
+{"u_procp", "UPROCP", pr_nop, sr_nop, 6, 0, DEC, AN|RIGHT},
+{"ucmd", "CMD", pr_comm, sr_cmd, 15, COM, DEC, PO|UNLIMITED}, /*ucomm*/
+{"ucomm", "COMMAND", pr_comm, sr_cmd, 15, COM, XXX, PO|UNLIMITED}, /*comm*/
+{"uid", "UID", pr_euid, sr_euid, 5, 0, XXX, ET|RIGHT},
+{"uid_hack", "UID", pr_euser, sr_euser, 8, USR, XXX, ET|USER},
+{"umask", "UMASK", pr_nop, sr_nop, 5, 0, DEC, AN|RIGHT},
+{"uname", "USER", pr_euser, sr_euser, 8, USR, DEC, ET|USER}, /* man page misspelling of user? */
+{"upr", "UPR", pr_nop, sr_nop, 3, 0, BSD, TO|RIGHT}, /*usrpri*/
+{"uprocp", "UPROCP", pr_nop, sr_nop, 8, 0, BSD, AN|RIGHT},
+{"user", "USER", pr_euser, sr_euser, 8, USR, U98, ET|USER}, /* BSD n forces this to UID */
+{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT},
+{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT}, /*upr*/
+{"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT}, // not sure about "C"
+{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT},
+{"vm_data", "DATA", pr_nop, sr_vm_data, 5, 0, LNx, PO|RIGHT},
+{"vm_exe", "EXE", pr_nop, sr_vm_exe, 5, 0, LNx, PO|RIGHT},
+{"vm_lib", "LIB", pr_nop, sr_vm_lib, 5, 0, LNx, PO|RIGHT},
+{"vm_lock", "LCK", pr_nop, sr_vm_lock, 3, 0, LNx, PO|RIGHT},
+{"vm_stack", "STACK", pr_nop, sr_vm_stack, 5, 0, LNx, PO|RIGHT},
+{"vsize", "VSZ", pr_vsz, sr_vsize, 6, 0, DEC, PO|RIGHT}, /*vsz*/
+{"vsz", "VSZ", pr_vsz, sr_vm_size, 6, 0, U98, PO|RIGHT}, /*vsize*/
+{"wchan", "WCHAN", pr_wchan, sr_wchan, 6, WCH, XXX, TO|WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
+{"wname", "WCHAN", pr_wname, sr_nop, 6, WCH, SGI, TO|WCHAN}, /* opposite of nwchan */
+{"xstat", "XSTAT", pr_nop, sr_nop, 5, 0, BSD, AN|RIGHT},
+{"zone", "ZONE", pr_context, sr_nop, 31, 0, SUN, ET|LEFT}, // Solaris zone == Linux context?
+{"zoneid", "ZONEID", pr_nop, sr_nop, 31, 0, SUN, ET|RIGHT},// Linux only offers context names
+{"~", "-", pr_nop, sr_nop, 1, 0, LNX, AN|RIGHT} /* NULL would ruin alphabetical order */
+};
+
+#undef USER
+#undef LEFT
+#undef RIGHT
+#undef UNLIMITED
+#undef WCHAN
+#undef SIGNAL
+#undef PIDMAX
+#undef PO
+#undef TO
+#undef AN
+#undef ET
+
+static const int format_array_count = sizeof(format_array)/sizeof(format_struct);
+
+
+/****************************** Macro formats *******************************/
+/* First X field may be NR, which is p->start_code>>26 printed with %2ld */
+/* That seems useless though, and Debian already killed it. */
+/* The ones marked "Digital" have the name defined, not just the data. */
+static const macro_struct macro_array[] = {
+{"DFMT", "pid,tname,state,cputime,cmd"}, /* Digital's default */
+{"DefBSD", "pid,tname,stat,bsdtime,args"}, /* Our BSD default */
+{"DefSysV", "pid,tname,time,cmd"}, /* Our SysV default */
+{"END_BSD", "state,tname,cputime,comm"}, /* trailer for O */
+{"END_SYS5", "state,tname,time,command"}, /* trailer for -O */
+{"F5FMT", "uname,pid,ppid,c,start,tname,time,cmd"}, /* Digital -f */
+
+{"FB_", "pid,tt,stat,time,command"}, /* FreeBSD default */
+{"FB_j", "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"}, /* FreeBSD j */
+{"FB_l", "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"}, /* FreeBSD l */
+{"FB_u", "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"}, /* FreeBSD u */
+{"FB_v", "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"}, /* FreeBSD v */
+
+{"FD_", "pid,tty,time,comm"}, /* Fictional Debian SysV default */
+{"FD_f", "user,pid,ppid,start_time,tty,time,comm"}, /* Fictional Debian -f */
+{"FD_fj", "user,pid,ppid,start_time,tty,time,pgid,sid,comm"}, /* Fictional Debian -jf */
+{"FD_j", "pid,tty,time,pgid,sid,comm"}, /* Fictional Debian -j */
+{"FD_l", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"}, /* Fictional Debian -l */
+{"FD_lj", "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */
+
+{"FL5FMT", "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"}, /* Digital -fl */
+
+{"FLASK_context", "pid,context,command"}, /* Flask Linux context, --context */
+
+{"HP_", "pid,tty,time,comm"}, /* HP default */
+{"HP_f", "user,pid,ppid,cpu,stime,tty,time,args"}, /* HP -f */
+{"HP_fl", "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"}, /* HP -fl */
+{"HP_l", "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"}, /* HP -l */
+
+{"J390", "pid,sid,pgrp,tname,atime,args"}, /* OS/390 -j */
+{"JFMT", "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"}, /* Digital j and -j */
+{"L5FMT", "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"}, /* Digital -l */
+{"LFMT", "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"}, /* Digital l */
+
+{"OL_X", "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"}, /* Old i386 Linux X */
+{"OL_j", "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"}, /* Old Linux j */
+{"OL_l", "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"}, /* Old Linux l */
+{"OL_m", "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */
+{"OL_s", "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"}, /* Old Linux s */
+{"OL_u", "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"}, /* Old Linux u */
+{"OL_v", "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"}, /* Old Linux v */
+
+{"RD_", "pid,tname,state,bsdtime,comm"}, /* Real Debian default */
+{"RD_f", "uid,pid,ppid,start_time,tname,bsdtime,args"}, /* Real Debian -f */
+{"RD_fj", "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"}, /* Real Debian -jf */
+{"RD_j", "pid,tname,state,bsdtime,pgid,sid,comm"}, /* Real Debian -j */
+{"RD_l", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"}, /* Real Debian -l */
+{"RD_lj", "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"}, /* Real Debian -jl */
+
+{"RUSAGE", "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */
+{"SCHED", "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"}, /* Digital -o "SCHED" */
+{"SFMT", "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"}, /* Digital s */
+
+{"Std_f", "uid_hack,pid,ppid,c,stime,tname,time,cmd"}, /* new -f */
+{"Std_fl", "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */
+{"Std_l", "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"}, /* new -l */
+
+{"THREAD", "user,pcpu,pri,scnt,wchan,usertime,systime"}, /* Digital -o "THREAD" */
+{"UFMT", "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"}, /* Digital u */
+{"VFMT", "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"}, /* Digital v */
+{"~", "~"} /* NULL would ruin alphabetical order */
+};
+
+static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);
+
+
+/*************************** AIX formats ********************/
+/* Convert AIX format codes to normal format specifiers. */
+static const aix_struct aix_array[] = {
+{'C', "pcpu", "%CPU"},
+{'G', "group", "GROUP"},
+{'P', "ppid", "PPID"},
+{'U', "user", "USER"},
+{'a', "args", "COMMAND"},
+{'c', "comm", "COMMAND"},
+{'g', "rgroup", "RGROUP"},
+{'n', "nice", "NI"},
+{'p', "pid", "PID"},
+{'r', "pgid", "PGID"},
+{'t', "etime", "ELAPSED"},
+{'u', "ruser", "RUSER"},
+{'x', "time", "TIME"},
+{'y', "tty", "TTY"},
+{'z', "vsz", "VSZ"},
+{'~', "~", "~"} /* NULL would ruin alphabetical order */
+};
+static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);
+
+
+/********************* sorting ***************************/
+/* Convert short sorting codes to normal format specifiers. */
+static const shortsort_struct shortsort_array[] = {
+{'C', "pcpu" },
+{'G', "tpgid" },
+{'J', "cstime" },
+/* {'K', "stime" }, */ /* conflict, system vs. start time */
+{'M', "maj_flt" },
+{'N', "cmaj_flt" },
+{'P', "ppid" },
+{'R', "resident" },
+{'S', "share" },
+{'T', "start_time" },
+{'U', "uid" }, /* euid */
+{'c', "cmd" },
+{'f', "flags" },
+{'g', "pgrp" },
+{'j', "cutime" },
+{'k', "utime" },
+{'m', "min_flt" },
+{'n', "cmin_flt" },
+{'o', "session" },
+{'p', "pid" },
+{'r', "rss" },
+{'s', "size" },
+{'t', "tty" },
+{'u', "user" },
+{'v', "vsize" },
+{'y', "priority" }, /* nice */
+{'~', "~" } /* NULL would ruin alphabetical order */
+};
+static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);
+
+
+/*********** print format_array **********/
+/* called by the parser in another file */
+void print_format_specifiers(void){
+ const format_struct *walk = format_array;
+ while(*(walk->spec) != '~'){
+ if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head);
+ walk++;
+ }
+}
+
+/************ comparison functions for bsearch *************/
+
+static int compare_format_structs(const void *a, const void *b){
+ return strcmp(((const format_struct*)a)->spec,((const format_struct*)b)->spec);
+}
+
+static int compare_macro_structs(const void *a, const void *b){
+ return strcmp(((const macro_struct*)a)->spec,((const macro_struct*)b)->spec);
+}
+
+/******** look up structs as needed by the sort & format parsers ******/
+
+const shortsort_struct *search_shortsort_array(const int findme){
+ const shortsort_struct *walk = shortsort_array;
+ while(walk->desc != '~'){
+ if(walk->desc == findme) return walk;
+ walk++;
+ }
+ return NULL;
+}
+
+const aix_struct *search_aix_array(const int findme){
+ const aix_struct *walk = aix_array;
+ while(walk->desc != '~'){
+ if(walk->desc == findme) return walk;
+ walk++;
+ }
+ return NULL;
+}
+
+const format_struct *search_format_array(const char *findme){
+ format_struct key;
+ key.spec = findme;
+ return bsearch(&key, format_array, format_array_count,
+ sizeof(format_struct), compare_format_structs
+ );
+}
+
+const macro_struct *search_macro_array(const char *findme){
+ macro_struct key;
+ key.spec = findme;
+ return bsearch(&key, macro_array, macro_array_count,
+ sizeof(macro_struct), compare_macro_structs
+ );
+}
+
+static unsigned int active_cols; /* some multiple of screen_cols */
+
+/***** Last chance, avoid needless trunctuation. */
+static void check_header_width(void){
+ format_node *walk = format_list;
+ unsigned int total = 0;
+ int was_normal = 0;
+ unsigned int i = 0;
+ unsigned int sigs = 0;
+ while(walk){
+ switch((walk->flags) & CF_JUST_MASK){
+ default:
+ total += walk->width;
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case CF_SIGNAL:
+ sigs++;
+ total += walk->width;
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case CF_UNLIMITED: /* could chop this a bit */
+ if(walk->next) total += walk->width;
+ else total += 3; /* not strlen(walk->name) */
+ total += was_normal;
+ was_normal = 1;
+ break;
+ case 0: /* AIX */
+ total += walk->width;
+ was_normal = 0;
+ break;
+ }
+ walk = walk->next;
+ }
+ for(;;){
+ i++;
+ active_cols = screen_cols * i;
+ if(active_cols>=total) break;
+ if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
+ }
+ wide_signals = (total+sigs*7 <= active_cols);
+}
+
+
+/********** show one process (NULL proc prints header) **********/
+
+//#define SPACE_AMOUNT page_size
+#define SPACE_AMOUNT 144
+
+static char *saved_outbuf;
+
+void show_one_proc(const proc_t *restrict const p, const format_node *restrict fmt){
+ /* unknown: maybe set correct & actual to 1, remove +/- 1 below */
+ int correct = 0; /* screen position we should be at */
+ int actual = 0; /* screen position we are at */
+ int amount = 0; /* amount of text that this data is */
+ int leftpad = 0; /* amount of space this column _could_ need */
+ int space = 0; /* amount of space we actually need to print */
+ int dospace = 0; /* previous column determined that we need a space */
+ int legit = 0; /* legitimately stolen extra space */
+ int sz = 0; /* real size of data in outbuffer */
+ int tmpspace = 0;
+ char *restrict const outbuf = saved_outbuf;
+ static int did_stuff = 0; /* have we ever printed anything? */
+
+ if(unlikely(-1==(long)p)){ /* true only once, at the end */
+ if(did_stuff) return;
+ /* have _never_ printed anything, but might need a header */
+ if(!--lines_to_next_header){
+ lines_to_next_header = header_gap;
+ show_one_proc(NULL,fmt);
+ }
+ /* fprintf(stderr, "No processes available.\n"); */ /* legal? */
+ exit(1);
+ }
+ if(likely(p)){ /* not header, maybe we should call ourselves for it */
+ if(unlikely(!--lines_to_next_header)){
+ lines_to_next_header = header_gap;
+ show_one_proc(NULL,fmt);
+ }
+ }
+ did_stuff = 1;
+ if(unlikely(active_cols>(int)OUTBUF_SIZE)) fprintf(stderr,"Fix bigness error.\n");
+
+ /* print row start sequence */
+ for(;;){
+ legit = 0;
+ /* set width suggestion which might be ignored */
+// if(likely(fmt->next)) max_rightward = fmt->width;
+// else max_rightward = active_cols-((correct>actual) ? correct : actual);
+
+ if(likely(fmt->next)){
+ max_rightward = fmt->width;
+ tmpspace = 0;
+ }else{
+ tmpspace = correct-actual;
+ if (tmpspace<1){
+ tmpspace = dospace;
+ max_rightward = active_cols-actual-tmpspace;
+ }else{
+ max_rightward = active_cols - ( (correct>actual) ? correct : actual );
+ }
+ }
+ max_leftward = fmt->width + actual - correct; /* TODO check this */
+
+// fprintf(stderr, "cols: %d, max_rightward: %d, max_leftward: %d, actual: %d, correct: %d\n",
+// active_cols, max_rightward, max_leftward, actual, correct);
+
+ /* prepare data and calculate leftpad */
+ if(likely(p) && likely(fmt->pr)) amount = (*fmt->pr)(outbuf,p);
+ else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
+
+ switch((fmt->flags) & CF_JUST_MASK){
+ case 0: /* for AIX, assigned outside this file */
+ leftpad = 0;
+ break;
+ case CF_LEFT: /* bad */
+ leftpad = 0;
+ break;
+ case CF_RIGHT: /* OK */
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ break;
+ case CF_SIGNAL:
+ /* if the screen is wide enough, use full 16-character output */
+ if(wide_signals){
+ leftpad = 16 - amount;
+ legit = 7;
+ }else{
+ leftpad = 9 - amount;
+ }
+ if(leftpad < 0) leftpad = 0;
+ break;
+ case CF_USER: /* bad */
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ if(!user_is_number) leftpad = 0;
+ break;
+ case CF_WCHAN: /* bad */
+ if(wchan_is_number){
+ leftpad = fmt->width - amount;
+ if(leftpad < 0) leftpad = 0;
+ break;
+ }else{
+ if ((active_cols-actual-tmpspace)<1)
+ outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
+ leftpad = 0;
+ break;
+ }
+ case CF_UNLIMITED:
+ {
+ if(active_cols-actual-tmpspace < 1)
+ outbuf[1] = '\0'; /* oops, we (mostly) lose this column... */
+ leftpad = 0;
+ break;
+ }
+ default:
+ fprintf(stderr, "bad alignment code\n");
+ break;
+ }
+ /* At this point:
+ *
+ * correct from previous column
+ * actual from previous column
+ * amount not needed (garbage due to chopping)
+ * leftpad left padding for this column alone (not make-up or gap)
+ * space not needed (will recalculate now)
+ * dospace if we require space between this and the prior column
+ * legit space we were allowed to steal, and thus did steal
+ */
+ space = correct - actual + leftpad;
+ if(space<1) space=dospace;
+ if(unlikely(space>SPACE_AMOUNT)) space=SPACE_AMOUNT; // only so much available
+
+ /* real size -- don't forget in 'amount' is number of cells */
+ sz = strlen(outbuf);
+
+ /* print data, set x position stuff */
+ if(unlikely(!fmt->next)){
+ /* Last column. Write padding + data + newline all together. */
+ outbuf[sz] = '\n';
+ fwrite(outbuf-space, space+sz+1, 1, stdout);
+ break;
+ }
+ /* Not the last column. Write padding + data together. */
+ fwrite(outbuf-space, space+sz, 1, stdout);
+ actual += space+amount;
+ correct += fmt->width;
+ correct += legit; /* adjust for SIGNAL expansion */
+ if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
+ correct++;
+ dospace = 1;
+ }else{
+ dospace = 0;
+ }
+ fmt = fmt->next;
+ /* At this point:
+ *
+ * correct screen position we should be at
+ * actual screen position we are at
+ * amount not needed
+ * leftpad not needed
+ * space not needed
+ * dospace if have determined that we need a space next time
+ * legit not needed
+ */
+ }
+}
+
+
+#ifdef TESTING
+static void sanity_check(void){
+ format_struct *fs = format_array;
+ while((fs->spec)[0] != '~'){
+ if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec);
+ fs++;
+ }
+}
+#endif
+
+
+void init_output(void){
+ int outbuf_pages;
+ char *outbuf;
+
+ switch(page_size){
+ case 65536: page_shift = 16; break;
+ case 32768: page_shift = 15; break;
+ case 16384: page_shift = 14; break;
+ case 8192: page_shift = 13; break;
+ default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
+ case 4096: page_shift = 12; break;
+ case 2048: page_shift = 11; break;
+ case 1024: page_shift = 10; break;
+ }
+
+ // add page_size-1 to round up
+ outbuf_pages = (OUTBUF_SIZE+SPACE_AMOUNT+page_size-1)/page_size;
+ outbuf = mmap(
+ 0,
+ page_size * (outbuf_pages+1), // 1 more, for guard page at high addresses
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0
+ );
+ memset(outbuf, ' ', SPACE_AMOUNT);
+ if(SPACE_AMOUNT==page_size) mprotect(outbuf, page_size, PROT_READ);
+ mprotect(outbuf + page_size*outbuf_pages, page_size, PROT_NONE); // gaurd page
+ saved_outbuf = outbuf + SPACE_AMOUNT;
+ // available space: page_size*outbuf_pages-SPACE_AMOUNT
+
+ seconds_since_1970 = time(NULL);
+ time_of_boot = seconds_since_1970 - seconds_since_boot;
+
+ meminfo();
+
+ check_header_width();
+}
--- /dev/null
+#!/bin/sh
+#
+# Wow, using $* causes great pain with: ps "pid,user pcpu,pmem"
+# The "$@" won't break that into 2 arguments.
+#
+LD_LIBRARY_PATH=../proc exec ./ps "$@"
--- /dev/null
+/*
+ * Copyright 1998-2003 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/* Ought to have debug print stuff like this:
+ * #define Print(fmt, args...) printf("Debug: " fmt, ## args)
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "../proc/version.h"
+
+#define ARG_GNU 0
+#define ARG_END 1
+#define ARG_PGRP 2
+#define ARG_SYSV 3
+#define ARG_PID 4
+#define ARG_BSD 5
+#define ARG_FAIL 6
+#define ARG_SESS 7
+
+static int w_count = 0;
+
+static int ps_argc; /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg; /* index into ps_argv */
+static char *flagptr; /* current location in ps_argv[thisarg] */
+static int not_pure_unix = 0; /* set by BSD and GNU options */
+static int force_bsd = 0; /* set when normal parsing fails */
+
+#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x))\
+ return "The " x " option is exclusive."
+
+
+/********** utility functions **********/
+
+/*
+ * Both "-Oppid" and "-O ppid" should be legal, though Unix98
+ * does not require it. BSD and Digital Unix allow both.
+ * Return the argument or NULL;
+ */
+static const char *get_opt_arg(void){
+ if(*(flagptr+1)){ /* argument is part of ps_argv[thisarg] */
+ not_pure_unix = 1;
+ return flagptr+1;
+ }
+ if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */
+ /* argument follows ps_argv[thisarg] */
+ if(*(ps_argv[thisarg+1]) == '\0') return NULL;
+ return ps_argv[++thisarg];
+}
+
+/********** parse lists (of UID, tty, GID, PID...) **********/
+
+static const char *parse_pid(char *str, sel_union *ret){
+ char *endp;
+ unsigned long num;
+ static const char pidrange[] = "Process ID out of range.";
+ static const char pidsyntax[] = "Process ID list syntax error.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0') return pidsyntax;
+ if(num<1) return pidrange;
+ if(num > 0x7fffffffUL) return pidrange;
+ ret->pid = num;
+ return 0;
+}
+
+static const char *parse_uid(char *str, sel_union *ret){
+ struct passwd *passwd_data;
+ char *endp;
+ unsigned long num;
+ static const char uidrange[] = "User ID out of range.";
+ static const char uidexist[] = "User name does not exist.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0'){ /* hmmm, try as login name */
+ passwd_data = getpwnam(str);
+ if(!passwd_data) return uidexist;
+ num = passwd_data->pw_uid;
+ }
+ if(num > 0xfffffffeUL) return uidrange;
+ ret->uid = num;
+ return 0;
+}
+
+static const char *parse_gid(char *str, sel_union *ret){
+ struct group *group_data;
+ char *endp;
+ unsigned long num;
+ static const char gidrange[] = "Group ID out of range.";
+ static const char gidexist[] = "Group name does not exist.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0'){ /* hmmm, try as login name */
+ group_data = getgrnam(str);
+ if(!group_data) return gidexist;
+ num = group_data->gr_gid;
+ }
+ if(num > 0xfffffffeUL) return gidrange;
+ ret->gid = num;
+ return 0;
+}
+
+static const char *parse_cmd(char *str, sel_union *ret){
+ strncpy(ret->cmd, str, sizeof ret->cmd); // strncpy pads to end
+ return 0;
+}
+
+static const char *parse_tty(char *str, sel_union *ret){
+ struct stat sbuf;
+ static const char missing[] = "TTY could not be found.";
+ static const char not_tty[] = "List member was not a TTY.";
+ char path[4096];
+ if(str[0]=='/'){
+ if(stat(str, &sbuf) >= 0) goto found_it;
+ return missing;
+ }
+#define lookup(p) \
+ snprintf(path,4096,p,str); \
+ if(stat(path, &sbuf) >= 0) goto found_it
+
+ lookup("/dev/pts/%s"); /* New Unix98 ptys go first */
+ lookup("/dev/%s");
+ lookup("/dev/tty%s");
+ lookup("/dev/pty%s");
+ lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */
+ if(!strcmp(str,"-")){ /* "-" means no tty (from AIX) */
+ ret->tty = 0; /* processes w/o tty */
+ return 0;
+ }
+ if(!strcmp(str,"?")){ /* "?" means no tty, which bash eats (Reno BSD?) */
+ ret->tty = 0; /* processes w/o tty */
+ return 0;
+ }
+ if(!*(str+1) && (stat(str,&sbuf)>=0)){ /* Kludge! Assume bash ate '?'. */
+ ret->tty = 0; /* processes w/o tty */
+ return 0;
+ }
+#undef lookup
+ return missing;
+found_it:
+ if(!S_ISCHR(sbuf.st_mode)) return not_tty;
+ ret->tty = sbuf.st_rdev;
+ return 0;
+}
+
+/*
+ * Used to parse lists in a generic way. (function pointers)
+ */
+static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){
+ selection_node *node;
+ char *buf; /* temp copy of arg to hack on */
+ char *sep_loc; /* separator location: " \t," */
+ char *walk;
+ int items;
+ int need_item;
+ const char *err; /* error code that could or did happen */
+ /*** prepare to operate ***/
+ node = malloc(sizeof(selection_node));
+ node->u = malloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */
+ node->n = 0;
+ buf = malloc(strlen(arg)+1);
+ strcpy(buf, arg);
+ /*** sanity check and count items ***/
+ need_item = 1; /* true */
+ items = 0;
+ walk = buf;
+ err = "Improper list.";
+ do{
+ switch(*walk){
+ case ' ': case ',': case '\t': case '\0':
+ if(need_item) goto parse_error;
+ need_item=1;
+ break;
+ default:
+ if(need_item) items++;
+ need_item=0;
+ }
+ } while (*++walk);
+ if(need_item) goto parse_error;
+ node->n = items;
+ /*** actually parse the list ***/
+ walk = buf;
+ while(items--){
+ sep_loc = strpbrk(walk," ,\t");
+ if(sep_loc) *sep_loc = '\0';
+ if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error;
+ walk = sep_loc + 1; /* point to next item, if any */
+ }
+ free(buf);
+ node->next = selection_list;
+ selection_list = node;
+ return NULL;
+parse_error:
+ free(buf);
+ free(node->u);
+ free(node);
+ return err;
+}
+
+/***************** parse SysV options, including Unix98 *****************/
+static const char *parse_sysv_option(void){
+ const char *arg;
+ const char *err;
+
+ flagptr = ps_argv[thisarg];
+ while(*++flagptr){
+ // Find any excuse to ignore stupid Unix98 misfeatures.
+ //
+ // This list of options is ONLY for those defined by the
+ // "IEEE Std 1003.1, 2004 Edition", "ISO/IEC 9945:2003",
+ // or "Version 2 of the Single Unix Specification".
+ //
+ // It may be time to re-think the existance of this list.
+ // In the meantime, please do not add to it. The list is
+ // intended to ONLY contain flags defined by the POSIX and UNIX
+ // standards published by The Open Group, IEEE, and ISO.
+ if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1; // dude, -Z ain't in POSIX
+
+ switch(*flagptr){
+ case 'A':
+ trace("-A selects all processes.\n");
+ all_processes = 1;
+ break;
+ case 'C': /* end */
+ trace("-C select by process name.\n"); /* Why only HP/UX and us? */
+ arg=get_opt_arg();
+ if(!arg) return "List of command names must follow -C.";
+ err=parse_list(arg, parse_cmd);
+ if(err) return err;
+ selection_list->typecode = SEL_COMM;
+ return NULL; /* can't have any more options */
+ case 'F': /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */
+ trace("-F does fuller listing\n");
+ format_modifiers |= FM_F;
+ format_flags |= FF_Uf;
+ unix_f_option = 1; /* does this matter? */
+ break;
+ case 'G': /* end */
+ trace("-G select by RGID (supports names)\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of real groups must follow -G.";
+ err=parse_list(arg, parse_gid);
+ if(err) return err;
+ selection_list->typecode = SEL_RGID;
+ return NULL; /* can't have any more options */
+ case 'H': /* another nice HP/UX feature */
+ trace("-H Process hierarchy (like ASCII art forest option)\n");
+ forest_type = 'u';
+ break;
+#if 0
+ case 'J': // specify list of job IDs in hex (IRIX) -- like HP "-R" maybe?
+ trace("-J select by job ID\n"); // want a JID ("jid") for "-j" too
+ arg=get_opt_arg();
+ if(!arg) return "List of jobs must follow -J.";
+ err=parse_list(arg, parse_jid);
+ if(err) return err;
+ selection_list->typecode = SEL_JID;
+ return NULL; /* can't have any more options */
+#endif
+ case 'L': /* */
+ /* In spite of the insane 2-level thread system, Sun appears to
+ * have made this option Linux-compatible. If a process has N
+ * threads, ps will produce N lines of output. (not N+1 lines)
+ * Zombies are the only exception, with NLWP==0 and 1 output line.
+ * SCO UnixWare uses -L too.
+ */
+ trace("-L Print LWP (thread) info.\n");
+ thread_flags |= TF_U_L;
+// format_modifiers |= FM_L;
+ break;
+ case 'M': // typically the SE Linux context
+ trace("-M Print security label for Mandatory Access Control.\n");
+ format_modifiers |= FM_M;
+ break;
+ case 'N':
+ trace("-N negates.\n");
+ negate_selection = 1;
+ break;
+ case 'O': /* end */
+ trace("-O is preloaded -o.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format or sort specification must follow -O.";
+ defer_sf_option(arg, SF_U_O);
+ return NULL; /* can't have any more options */
+ case 'P': /* SunOS 5 "psr" or unknown HP/UX feature */
+ trace("-P adds columns of PRM info (HP-UX), PSR (SunOS), or capabilities (IRIX)\n");
+ format_modifiers |= FM_P;
+ break;
+#if 0
+ case 'R': // unknown HP/UX feature, like IRIX "-J" maybe?
+ trace("-R select by PRM group\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of PRM groups must follow -R.";
+ err=parse_list(arg, parse_prm);
+ if(err) return err;
+ selection_list->typecode = SEL_PRM;
+ return NULL; /* can't have any more options */
+#endif
+ case 'T':
+ /* IRIX 6.5 docs suggest POSIX threads get shown individually.
+ * This would make -T be like -L, -m, and m. (but an extra column)
+ * Testing (w/ normal processes) shows 1 line/process, not 2.
+ * Also, testing shows PID==SPID for all normal processes.
+ */
+ trace("-T adds strange SPID column (old sproc() threads?)\n");
+ thread_flags |= TF_U_T;
+// format_modifiers |= FM_T;
+ break;
+ case 'U': /* end */
+ trace("-U select by RUID (supports names).\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of real groups must follow -U.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_RUID;
+ return NULL; /* can't have any more options */
+ case 'V': /* single */
+ trace("-V prints version.\n");
+ exclusive("-V");
+ display_version();
+ exit(0);
+ // This must be verified against SVR4-MP. (UnixWare or Powermax)
+ // Leave it undocumented until that problem is solved.
+ case 'Z': /* full Mandatory Access Control level info */
+ trace("-Z shows full MAC info\n");
+ format_modifiers |= FM_M;
+ break;
+ case 'a':
+ trace("-a select all with a tty, but omit session leaders.\n");
+ simple_select |= SS_U_a;
+ break;
+ case 'c':
+ /* HP-UX and SunOS 5 scheduling info modifier */
+ trace("-c changes scheduling info.\n");
+ format_modifiers |= FM_c;
+ break;
+ case 'd':
+ trace("-d select all, but omit session leaders.\n");
+ simple_select |= SS_U_d;
+ break;
+ case 'e':
+ trace("-e selects all processes.\n");
+ all_processes = 1;
+ break;
+ case 'f':
+ trace("-f does full listing\n");
+ format_flags |= FF_Uf;
+ unix_f_option = 1; /* does this matter? */
+ break;
+ case 'g': /* end */
+ trace("-g selects by session leader OR by group name\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of session leaders OR effective group names must follow -g.";
+ err=parse_list(arg, parse_pid);
+ if(!err){
+ selection_list->typecode = SEL_SESS;
+ return NULL; /* can't have any more options */
+ }
+ err=parse_list(arg, parse_gid);
+ if(!err){
+ selection_list->typecode = SEL_EGID;
+ return NULL; /* can't have any more options */
+ }
+ return "List of session leaders OR effective group IDs was invalid.";
+ case 'j':
+ trace("-j jobs format.\n");
+ /* old Debian used RD_j and Digital uses JFMT */
+ if(sysv_j_format) format_flags |= FF_Uj;
+ else format_modifiers |= FM_j;
+ break;
+ case 'l':
+ trace("-l long format.\n");
+ format_flags |= FF_Ul;
+ break;
+ case 'm':
+ trace("-m shows threads.\n");
+ /* note that AIX shows 2 lines for a normal process */
+ thread_flags |= TF_U_m;
+ break;
+ case 'n': /* end */
+ trace("-n sets namelist file.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Alternate System.map file must follow -n.";
+ namelist_file = arg;
+ return NULL; /* can't have any more options */
+ case 'o': /* end */
+ /* Unix98 has gross behavior regarding this. From the following: */
+ /* ps -o pid,nice=NICE,tty=TERMINAL,comm */
+ /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm" */
+ /* Yes, the second column has the name "NICE,tty=TERMINAL,comm" */
+ /* This parser looks for any excuse to ignore that braindamage. */
+ trace("-o user-defined format.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format specification must follow -o.";
+ not_pure_unix |= defer_sf_option(arg, SF_U_o);
+ return NULL; /* can't have any more options */
+ case 'p': /* end */
+ trace("-p select by PID.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of process IDs must follow -p.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL; /* can't have any more options */
+#if 0
+ case 'r':
+ trace("-r some Digital Unix thing about warnings...\n");
+ trace(" or SCO's option to chroot() for new /proc and /dev.\n");
+ return "The -r option is reserved.";
+ break;
+#endif
+ case 's': /* end */
+ trace("-s Select processes belonging to the sessions given.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of session IDs must follow -s.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_SESS;
+ return NULL; /* can't have any more options */
+ case 't': /* end */
+ trace("-t select by tty.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of terminals (pty, tty...) must follow -t.";
+ err=parse_list(arg, parse_tty);
+ if(err) return err;
+ selection_list->typecode = SEL_TTY;
+ return NULL; /* can't have any more options */
+ case 'u': /* end */
+ trace("-u select by user ID (the EUID?) (supports names).\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of users must follow -u.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_EUID;
+ return NULL; /* can't have any more options */
+ case 'w':
+ trace("-w wide output.\n");
+ w_count++;
+ break;
+ case 'x': /* behind personality until "ps -ax" habit is uncommon */
+ if(personality & PER_SVR4_x){
+ // Same as -y, but for System V Release 4 MP
+ trace("-x works like Sun Solaris & SCO Unixware -y option\n");
+ format_modifiers |= FM_y;
+ break;
+ }
+ if(personality & PER_HPUX_x){
+ trace("-x extends the command line\n");
+ w_count += 2;
+ unix_f_option = 1;
+ break;
+ }
+ return "Must set personality to get -x option.";
+ case 'y': /* Sun's -l hack (also: Irix "lnode" resource control info) */
+ trace("-y Print lnone info in UID/USER column or do Sun -l hack.\n");
+ format_modifiers |= FM_y;
+ break;
+#if 0
+ // This must be verified against SVR4-MP (UnixWare or Powermax)
+ case 'z': /* alias of Mandatory Access Control level info */
+ trace("-z shows aliased MAC info\n");
+ format_modifiers |= FM_M;
+ break;
+ // Solaris 10 does this
+ case 'z': /* select by zone */
+ trace("-z secects by zone\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of zones (contexts, labels, whatever?) must follow -z.";
+ err=parse_list(arg, parse_zone);
+ if(err) return err;
+ selection_list->typecode = SEL_ZONE;
+ return NULL; /* can't have any more options */
+#endif
+ case '-':
+ return "Embedded '-' among SysV options makes no sense.";
+ break;
+ case '\0':
+ return "Please report the \"SysV \\0 can't happen\" bug.";
+ break;
+ default:
+ return "Unsupported SysV option.";
+ } /* switch */
+ } /* while */
+ return NULL;
+}
+
+/************************* parse BSD options **********************/
+static const char *parse_bsd_option(void){
+ const char *arg;
+ const char *err;
+
+ flagptr = ps_argv[thisarg]; /* assume we _have_ a '-' */
+ if(flagptr[0]=='-'){
+ if(!force_bsd) return "Can't happen! Problem #1.";
+ }else{
+ flagptr--; /* off beginning, will increment before use */
+ if(personality & PER_FORCE_BSD){
+ if(!force_bsd) return "Can't happen! Problem #2.";
+ }else{
+ if(force_bsd) return "2nd chance parse failed, not BSD or SysV.";
+ }
+ }
+
+ while(*++flagptr){
+ switch(*flagptr){
+ case '0' ... '9': /* end */
+ trace("0..9 Old BSD-style select by process ID\n");
+ arg=flagptr;
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL; /* can't have any more options */
+#if 0
+ case 'A':
+ /* maybe this just does a larger malloc() ? */
+ trace("A Increases the argument space (Digital Unix)\n");
+ return "Option A is reserved.";
+ break;
+ case 'C':
+ /* should divide result by 1-(e**(foo*log(bar))) */
+ trace("C Use raw CPU time for %%CPU instead of decaying ave\n");
+ return "Option C is reserved.";
+ break;
+#endif
+ case 'H': // The FreeBSD way (NetBSD:s OpenBSD:k FreeBSD:H -- NIH???)
+ trace("H Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n");
+ thread_flags |= TF_B_H;
+ //format_modifiers |= FM_L; // FIXME: determine if we need something like this
+ break;
+ case 'L': /* single */
+ trace("L List all format specifiers\n");
+ exclusive("L");
+ print_format_specifiers();
+ exit(0);
+ case 'M': // undocumented for now: these are proliferating!
+ trace("M MacOS X thread display, like AIX/Tru64\n");
+ thread_flags |= TF_B_m;
+ break;
+ case 'N': /* end */
+ trace("N Specify namelist file\n");
+ arg=get_opt_arg();
+ if(!arg) return "Alternate System.map file must follow N.";
+ namelist_file = arg;
+ return NULL; /* can't have any more options */
+ case 'O': /* end */
+ trace("O Like o + defaults, add new columns after PID. Also sort.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format or sort specification must follow O.";
+ defer_sf_option(arg, SF_B_O);
+ return NULL; /* can't have any more options */
+ break;
+ case 'S':
+ trace("S include dead kids in sum\n");
+ include_dead_children = 1;
+ break;
+ case 'T':
+ trace("T Select all processes on this terminal\n");
+ /* put our tty on a tiny list */
+ {
+ selection_node *node;
+ node = malloc(sizeof(selection_node));
+ node->u = malloc(sizeof(sel_union));
+ node->u[0].tty = cached_tty;
+ node->typecode = SEL_TTY;
+ node->n = 1;
+ node->next = selection_list;
+ selection_list = node;
+ }
+ break;
+ case 'U': /* end */
+ trace("U Select processes for specified users.\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of users must follow U.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_EUID;
+ return NULL; /* can't have any more options */
+ case 'V': /* single */
+ trace("V show version info\n");
+ exclusive("V");
+ display_version();
+ exit(0);
+ case 'W':
+ trace("W N/A get swap info from ... not /dev/drum.\n");
+ return "Obsolete W option not supported. (You have a /dev/drum?)";
+ break;
+ case 'X':
+ trace("X Old Linux i386 register format\n");
+ format_flags |= FF_LX;
+ break;
+ case 'Z': /* FreeBSD does MAC like SGI's Irix does it */
+ trace("Z Print security label for Mandatory Access Control.\n");
+ format_modifiers |= FM_M;
+ break;
+ case 'a':
+ trace("a Select all w/tty, including other users\n");
+ simple_select |= SS_B_a;
+ break;
+ case 'c':
+ trace("c true command name\n");
+ bsd_c_option = 1;
+ break;
+ case 'e':
+ trace("e environment\n");
+ bsd_e_option = 1;
+ break;
+ case 'f':
+ trace("f ASCII art forest\n");
+ forest_type = 'b';
+ break;
+ case 'g':
+ trace("g _all_, even group leaders!.\n");
+ simple_select |= SS_B_g;
+ break;
+ case 'h':
+ trace("h Repeat header... yow.\n");
+ if(header_type) return "Only one heading option may be specified.";
+ if(personality & PER_BSD_h) header_type = HEAD_MULTI;
+ else header_type = HEAD_NONE;
+ break;
+ case 'j':
+ trace("j job control format\n");
+ format_flags |= FF_Bj;
+ break;
+ case 'k':
+ // OpenBSD: don't hide "kernel threads" -- like the swapper?
+ // trace("k Print LWP (thread) info.\n"); // was: Use /vmcore as c-dumpfile\n");
+
+ // NetBSD, and soon (?) FreeBSD: sort-by-keyword
+ trace("k Specify sorting keywords.\n");
+ arg=get_opt_arg();
+ if(!arg) return "Long sort specification must follow 'k'.";
+ defer_sf_option(arg, SF_G_sort);
+ return NULL; /* can't have any more options */
+ case 'l':
+ trace("l Display long format\n");
+ format_flags |= FF_Bl;
+ break;
+ case 'm':
+ trace("m all threads, sort on mem use, show mem info\n");
+ if(personality & PER_OLD_m){
+ format_flags |= FF_Lm;
+ break;
+ }
+ if(personality & PER_BSD_m){
+ defer_sf_option("pmem", SF_B_m);
+ break;
+ }
+ thread_flags |= TF_B_m;
+ break;
+ case 'n':
+ trace("n Numeric output for WCHAN, and USER replaced by UID\n");
+ wchan_is_number = 1;
+ user_is_number = 1;
+ /* TODO add tty_is_number too? */
+ break;
+ case 'o': /* end */
+ trace("o Specify user-defined format\n");
+ arg=get_opt_arg();
+ if(!arg) return "Format specification must follow o.";
+ defer_sf_option(arg, SF_B_o);
+ return NULL; /* can't have any more options */
+ case 'p': /* end */
+ trace("p Select by process ID\n");
+ arg=get_opt_arg();
+ if(!arg) return "List of process IDs must follow p.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL; /* can't have any more options */
+ case 'r':
+ trace("r Select running processes\n");
+ running_only = 1;
+ break;
+ case 's':
+ trace("s Display signal format\n");
+ format_flags |= FF_Bs;
+ break;
+ case 't': /* end */
+ trace("t Select by tty.\n");
+ /* List of terminals (tty, pty...) _should_ follow t. */
+ arg=get_opt_arg();
+ if(!arg){
+ /* Wow, obsolete BSD syntax. Put our tty on a tiny list. */
+ selection_node *node;
+ node = malloc(sizeof(selection_node));
+ node->u = malloc(sizeof(sel_union));
+ node->u[0].tty = cached_tty;
+ node->typecode = SEL_TTY;
+ node->n = 1;
+ node->next = selection_list;
+ selection_list = node;
+ return NULL;
+ }
+ err=parse_list(arg, parse_tty);
+ if(err) return err;
+ selection_list->typecode = SEL_TTY;
+ return NULL; /* can't have any more options */
+ case 'u':
+ trace("u Display user-oriented\n");
+ format_flags |= FF_Bu;
+ break;
+ case 'v':
+ trace("v Display virtual memory\n");
+ format_flags |= FF_Bv;
+ break;
+ case 'w':
+ trace("w wide output\n");
+ w_count++;
+ break;
+ case 'x':
+ trace("x Select processes without controlling ttys\n");
+ simple_select |= SS_B_x;
+ break;
+ case '-':
+ return "Embedded '-' among BSD options makes no sense.";
+ break;
+ case '\0':
+ return "Please report the \"BSD \\0 can't happen\" bug.";
+ break;
+ default:
+ return "Unsupported option (BSD syntax)";
+ } /* switch */
+ } /* while */
+ return NULL;
+}
+
+/*************** gnu long options **********************/
+
+/*
+ * Return the argument or NULL
+ */
+static const char *grab_gnu_arg(void){
+ switch(*flagptr){ /* argument is part of ps_argv[thisarg] */
+ default:
+ return NULL; /* something bad */
+ case '=': case ':':
+ if(*++flagptr) return flagptr; /* found it */
+ return NULL; /* empty '=' or ':' */
+ case '\0': /* try next argv[] */
+ ;
+ }
+ if(thisarg+2 > ps_argc) return NULL; /* there is nothing left */
+ /* argument follows ps_argv[thisarg] */
+ if(*(ps_argv[thisarg+1]) == '\0') return NULL;
+ return ps_argv[++thisarg];
+}
+
+typedef struct gnu_table_struct {
+ const char *name; /* long option name */
+ const void *jump; /* See gcc extension info. :-) */
+} gnu_table_struct;
+
+static int compare_gnu_table_structs(const void *a, const void *b){
+ return strcmp(((const gnu_table_struct*)a)->name,((const gnu_table_struct*)b)->name);
+}
+
+/* Option arguments are after ':', after '=', or in argv[n+1] */
+static const char *parse_gnu_option(void){
+ const char *arg;
+ const char *err;
+ char *s;
+ size_t sl;
+ char buf[16];
+ gnu_table_struct findme = { buf, NULL};
+ gnu_table_struct *found;
+ static const gnu_table_struct gnu_table[] = {
+ {"Group", &&case_Group}, /* rgid */
+ {"User", &&case_User}, /* ruid */
+ {"cols", &&case_cols},
+ {"columns", &&case_columns},
+ {"context", &&case_context},
+ {"cumulative", &&case_cumulative},
+ {"deselect", &&case_deselect}, /* -N */
+ {"forest", &&case_forest}, /* f -H */
+ {"format", &&case_format},
+ {"group", &&case_group}, /* egid */
+ {"header", &&case_header},
+ {"headers", &&case_headers},
+ {"heading", &&case_heading},
+ {"headings", &&case_headings},
+ {"help", &&case_help},
+ {"info", &&case_info},
+ {"lines", &&case_lines},
+ {"no-header", &&case_no_header},
+ {"no-headers", &&case_no_headers},
+ {"no-heading", &&case_no_heading},
+ {"no-headings", &&case_no_headings},
+ {"noheader", &&case_noheader},
+ {"noheaders", &&case_noheaders},
+ {"noheading", &&case_noheading},
+ {"noheadings", &&case_noheadings},
+ {"pid", &&case_pid},
+ {"ppid", &&case_ppid},
+ {"rows", &&case_rows},
+ {"sid", &&case_sid},
+ {"sort", &&case_sort},
+ {"tty", &&case_tty},
+ {"user", &&case_user}, /* euid */
+ {"version", &&case_version},
+ {"width", &&case_width},
+ };
+ const int gnu_table_count = sizeof(gnu_table)/sizeof(gnu_table_struct);
+
+ s = ps_argv[thisarg]+2;
+ sl = strcspn(s,":=");
+ if(sl > 15) return "Unknown gnu long option.";
+ strncpy(buf, s, sl);
+ buf[sl] = '\0';
+ flagptr = s+sl;
+
+ found = bsearch(&findme, gnu_table, gnu_table_count,
+ sizeof(gnu_table_struct), compare_gnu_table_structs
+ );
+
+ if(!found) return "Unknown gnu long option.";
+
+ goto *(found->jump); /* See gcc extension info. :-) */
+
+ case_Group:
+ trace("--Group\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of real groups must follow --Group.";
+ err=parse_list(arg, parse_gid);
+ if(err) return err;
+ selection_list->typecode = SEL_RGID;
+ return NULL;
+ case_User:
+ trace("--User\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of real users must follow --User.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_RUID;
+ return NULL;
+ case_cols:
+ case_width:
+ case_columns:
+ trace("--cols\n");
+ arg = grab_gnu_arg();
+ if(arg && *arg){
+ long t;
+ char *endptr;
+ t = strtol(arg, &endptr, 0);
+ if(!*endptr && (t>0) && (t<2000000000)){
+ screen_cols = (int)t;
+ return NULL;
+ }
+ }
+ return "Number of columns must follow --cols, --width, or --columns.";
+ case_cumulative:
+ trace("--cumulative\n");
+ if(s[sl]) return "Option --cumulative does not take an argument.";
+ include_dead_children = 1;
+ return NULL;
+ case_deselect:
+ trace("--deselect\n");
+ if(s[sl]) return "Option --deselect does not take an argument.";
+ negate_selection = 1;
+ return NULL;
+ case_no_header:
+ case_no_headers:
+ case_no_heading:
+ case_no_headings:
+ case_noheader:
+ case_noheaders:
+ case_noheading:
+ case_noheadings:
+ trace("--noheaders\n");
+ if(s[sl]) return "Option --no-heading does not take an argument.";
+ if(header_type) return "Only one heading option may be specified.";
+ header_type = HEAD_NONE;
+ return NULL;
+ case_header:
+ case_headers:
+ case_heading:
+ case_headings:
+ trace("--headers\n");
+ if(s[sl]) return "Option --heading does not take an argument.";
+ if(header_type) return "Only one heading option may be specified.";
+ header_type = HEAD_MULTI;
+ return NULL;
+ case_forest:
+ trace("--forest\n");
+ if(s[sl]) return "Option --forest does not take an argument.";
+ forest_type = 'g';
+ return NULL;
+ case_format:
+ trace("--format\n");
+ arg=grab_gnu_arg();
+ if(!arg) return "Format specification must follow --format.";
+ defer_sf_option(arg, SF_G_format);
+ return NULL;
+ case_group:
+ trace("--group\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of effective groups must follow --group.";
+ err=parse_list(arg, parse_gid);
+ if(err) return err;
+ selection_list->typecode = SEL_EGID;
+ return NULL;
+ case_help:
+ trace("--help\n");
+ exclusive("--help");
+ fwrite(help_message,1,strlen(help_message),stdout);
+ exit(0);
+ return NULL;
+ case_info:
+ trace("--info\n");
+ exclusive("--info");
+ self_info();
+ exit(0);
+ return NULL;
+ case_pid:
+ trace("--pid\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of process IDs must follow --pid.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PID;
+ return NULL;
+ case_ppid:
+ trace("--ppid\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of process IDs must follow --ppid.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_PPID;
+ return NULL;
+ case_rows:
+ case_lines:
+ trace("--rows\n");
+ arg = grab_gnu_arg();
+ if(arg && *arg){
+ long t;
+ char *endptr;
+ t = strtol(arg, &endptr, 0);
+ if(!*endptr && (t>0) && (t<2000000000)){
+ screen_rows = (int)t;
+ return NULL;
+ }
+ }
+ return "Number of rows must follow --rows or --lines.";
+ case_sid:
+ trace("--sid\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "Some sid thing(s) must follow --sid.";
+ err=parse_list(arg, parse_pid);
+ if(err) return err;
+ selection_list->typecode = SEL_SESS;
+ return NULL;
+ case_sort:
+ trace("--sort\n");
+ arg=grab_gnu_arg();
+ if(!arg) return "Long sort specification must follow --sort.";
+ defer_sf_option(arg, SF_G_sort);
+ return NULL;
+ case_tty:
+ trace("--tty\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of ttys must follow --tty.";
+ err=parse_list(arg, parse_tty);
+ if(err) return err;
+ selection_list->typecode = SEL_TTY;
+ return NULL;
+ case_user:
+ trace("--user\n");
+ arg = grab_gnu_arg();
+ if(!arg) return "List of effective users must follow --user.";
+ err=parse_list(arg, parse_uid);
+ if(err) return err;
+ selection_list->typecode = SEL_EUID;
+ return NULL;
+ case_version:
+ trace("--version\n");
+ exclusive("--version");
+ display_version();
+ exit(0);
+ return NULL;
+ case_context:
+ trace("--context\n");
+ format_flags |= FF_Fc;
+ return NULL;
+}
+
+/*************** process trailing PIDs **********************/
+static const char *parse_trailing_pids(void){
+ selection_node *pidnode; /* pid */
+ selection_node *grpnode; /* process group */
+ selection_node *sidnode; /* session */
+ char **argp; /* pointer to pointer to text of PID */
+ const char *err; /* error code that could or did happen */
+ int i;
+
+ i = ps_argc - thisarg; /* how many trailing PIDs, SIDs, PGRPs?? */
+ argp = ps_argv + thisarg;
+ thisarg = ps_argc - 1; /* we must be at the end now */
+
+ pidnode = malloc(sizeof(selection_node));
+ pidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+ pidnode->n = 0;
+
+ grpnode = malloc(sizeof(selection_node));
+ grpnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+ grpnode->n = 0;
+
+ sidnode = malloc(sizeof(selection_node));
+ sidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+ sidnode->n = 0;
+
+ while(i--){
+ char *data;
+ data = *(argp++);
+ switch(*data){
+ default: err = parse_pid( data, pidnode->u + pidnode->n++); break;
+ case '-': err = parse_pid(++data, grpnode->u + grpnode->n++); break;
+ case '+': err = parse_pid(++data, sidnode->u + sidnode->n++); break;
+ }
+ if(err) return err; /* the node gets freed with the list */
+ }
+
+ if(pidnode->n){
+ pidnode->next = selection_list;
+ selection_list = pidnode;
+ selection_list->typecode = SEL_PID;
+ } /* else free both parts */
+
+ if(grpnode->n){
+ grpnode->next = selection_list;
+ selection_list = grpnode;
+ selection_list->typecode = SEL_PGRP;
+ } /* else free both parts */
+
+ if(sidnode->n){
+ sidnode->next = selection_list;
+ selection_list = sidnode;
+ selection_list->typecode = SEL_SESS;
+ } /* else free both parts */
+
+ return NULL;
+}
+
+/************** misc stuff ***********/
+
+static void reset_parser(void){
+ w_count = 0;
+}
+
+static int arg_type(const char *str){
+ int tmp = str[0];
+ if((tmp>='a') && (tmp<='z')) return ARG_BSD;
+ if((tmp>='A') && (tmp<='Z')) return ARG_BSD;
+ if((tmp>='0') && (tmp<='9')) return ARG_PID;
+ if(tmp=='+') return ARG_SESS;
+ if(tmp!='-') return ARG_FAIL;
+ tmp = str[1];
+ if((tmp>='a') && (tmp<='z')) return ARG_SYSV;
+ if((tmp>='A') && (tmp<='Z')) return ARG_SYSV;
+ if((tmp>='0') && (tmp<='9')) return ARG_PGRP;
+ if(tmp!='-') return ARG_FAIL;
+ tmp = str[2];
+ if((tmp>='a') && (tmp<='z')) return ARG_GNU;
+ if((tmp>='A') && (tmp<='Z')) return ARG_GNU;
+ if(tmp=='\0') return ARG_END;
+ return ARG_FAIL;
+}
+
+/* First assume sysv, because that is the POSIX and Unix98 standard. */
+static const char *parse_all_options(void){
+ const char *err = NULL;
+ int at;
+ while(++thisarg < ps_argc){
+ trace("parse_all_options calling arg_type for \"%s\"\n", ps_argv[thisarg]);
+ at = arg_type(ps_argv[thisarg]);
+ trace("ps_argv[thisarg] is %s\n", ps_argv[thisarg]);
+ if(at != ARG_SYSV) not_pure_unix = 1;
+ switch(at){
+ case ARG_GNU:
+ err = parse_gnu_option();
+ break;
+ case ARG_SYSV:
+ if(!force_bsd){ /* else go past case ARG_BSD */
+ err = parse_sysv_option();
+ break;
+ case ARG_BSD:
+ if(force_bsd && !(personality & PER_FORCE_BSD)) return "way bad";
+ }
+ prefer_bsd_defaults = 1;
+ err = parse_bsd_option();
+ break;
+ case ARG_PGRP:
+ case ARG_SESS:
+ case ARG_PID:
+ prefer_bsd_defaults = 1;
+ err = parse_trailing_pids();
+ break;
+ case ARG_END:
+ case ARG_FAIL:
+ trace(" FAIL/END on [%s]\n",ps_argv[thisarg]);
+ return "Garbage option.";
+ break;
+ default:
+ printf(" ? %s\n",ps_argv[thisarg]);
+ return "Something broke.";
+ } /* switch */
+ if(err) return err;
+ } /* while */
+ return NULL;
+}
+
+static void choose_dimensions(void){
+ if(w_count && (screen_cols<132)) screen_cols=132;
+ if(w_count>1) screen_cols=OUTBUF_SIZE;
+ /* perhaps --html and --null should set unlimited width */
+}
+
+static const char *thread_option_check(void){
+ if(!thread_flags){
+ thread_flags = TF_show_proc;
+ return NULL;
+ }
+
+ if(forest_type){
+ return "Thread display conflicts with forest display.";
+ }
+ //thread_flags |= TF_no_forest;
+
+ if((thread_flags&TF_B_H) && (thread_flags&(TF_B_m|TF_U_m)))
+ return "Thread flags conflict; can't use H with m or -m.";
+ if((thread_flags&TF_B_m) && (thread_flags&TF_U_m))
+ return "Thread flags conflict; can't use both m and -m.";
+ if((thread_flags&TF_U_L) && (thread_flags&TF_U_T))
+ return "Thread flags conflict; can't use both -L and -T.";
+
+ if(thread_flags&TF_B_H) thread_flags |= (TF_show_proc|TF_loose_tasks);
+ if(thread_flags&(TF_B_m|TF_U_m)) thread_flags |= (TF_show_proc|TF_show_task|TF_show_both);
+
+ if(thread_flags&(TF_U_T|TF_U_L)){
+ if(thread_flags&(TF_B_m|TF_U_m|TF_B_H)){
+ // Got a thread style, so format modification is a requirement?
+ // Maybe -T/-L has H thread style though. (sorting interaction?)
+ //return "Huh? Tell procps-feedback@lists.sf.net what you expected.";
+ thread_flags |= TF_must_use;
+ }else{
+ // using -L/-T thread style, so format from elsewhere is OK
+ thread_flags |= TF_show_task; // or like the H option?
+ //thread_flags |= TF_no_sort;
+ }
+ }
+
+ return NULL;
+}
+
+int arg_parse(int argc, char *argv[]){
+ const char *err = NULL;
+ const char *err2 = NULL;
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+
+ if(personality & PER_FORCE_BSD) goto try_bsd;
+
+ err = parse_all_options();
+ if(err) goto try_bsd;
+ err = thread_option_check();
+ if(err) goto try_bsd;
+ err = process_sf_options(!not_pure_unix);
+ if(err) goto try_bsd;
+ err = select_bits_setup();
+ if(err) goto try_bsd;
+
+ choose_dimensions();
+ return 0;
+
+try_bsd:
+ trace("--------- now try BSD ------\n");
+
+ reset_global();
+ reset_parser();
+ reset_sortformat();
+ format_flags = 0;
+ ps_argc = argc;
+ ps_argv = argv;
+ thisarg = 0;
+ /* no need to reset flagptr */
+ not_pure_unix=1;
+ force_bsd=1;
+ prefer_bsd_defaults=1;
+ if(!( (PER_OLD_m|PER_BSD_m) & personality )) /* if default m setting... */
+ personality |= PER_OLD_m; /* Prefer old Linux over true BSD. */
+ /* Do not set PER_FORCE_BSD! It is tested below. */
+
+ err2 = parse_all_options();
+ if(err2) goto total_failure;
+ err2 = thread_option_check();
+ if(err2) goto total_failure;
+ err2 = process_sf_options(!not_pure_unix);
+ if(err2) goto total_failure;
+ err2 = select_bits_setup();
+ if(err2) goto total_failure;
+
+ // Feel a need to patch this out? First of all, read the FAQ.
+ // Second of all, talk to me. Without this warning, people can
+ // get seriously confused. Ask yourself if users would freak out
+ // about "ps -aux" suddenly changing behavior if a user "x" were
+ // added to the system.
+ //
+ // Also, a "-x" option is coming. It's already there in fact,
+ // for some non-default personalities. So "ps -ax" will parse
+ // as SysV options... and you're screwed if you've been patching
+ // out the friendly warning. Cut-over is likely to be in 2005.
+ if(!(personality & PER_FORCE_BSD))
+ fprintf(stderr, "Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html\n");
+ // Remember: contact albert@users.sf.net or procps-feedback@lists.sf.net
+ // if you should feel tempted. Be damn sure you understand all
+ // the issues. The same goes for other stuff too, BTW. Please ask.
+ // I'm happy to justify various implementation choices.
+
+ choose_dimensions();
+ return 0;
+
+total_failure:
+ reset_parser();
+ if(personality & PER_FORCE_BSD) fprintf(stderr, "ERROR: %s\n", err2);
+ else fprintf(stderr, "ERROR: %s\n", err);
+ fwrite(help_message,1,strlen(help_message),stderr);
+ exit(1);
+ /* return 1; */ /* useless */
+}
--- /dev/null
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" Man page for ps.
+.\" Quick hack conversion by Albert Cahalan, 1998.
+.\" Licensed under version 2 of the Gnu General Public License.
+.\"
+.TH PS 1 "July 28, 2004" "Linux" "Linux User's Manual"
+.\"
+.\" To render this page:
+.\" groff -t -b -man -X -P-resolution -P100 -Tps ps.1 &
+.\" groff -t -b -man -X -TX100 ps.1 &
+.\" tbl ps.1 | troff -Ww -man -z
+.\" groff -t -man -Tps ps.1 | ps2pdf - - > ps.pdf
+.\"
+.\" The '70s called. They want their perfect justification,
+.\" hyphenation, and double-spaced sentences back.
+.na
+.nh
+.if n .ss 12 0
+.\"
+.\" See /usr/share/groff/current/tmac/an-old.tmac for what these do.
+.\" Setting them to zero provides extra space, but only do that for
+.\" plain text output. PostScript and such will remain indented.
+.if n .nr IN 0n
+.if n .nr an-prevailing-indent 0n
+.\"
+.\"
+.\" ColSize is used for the format spec table.
+.\" It's the left margin, minus the right, minus
+.\" the space needed for the 1st two columns.
+.\" Making it messy: inches, ens, points, scaled points...
+.\"
+.nr ColSize ((\n(.lu-\n(.iu/\n(.Hu-20u)n)
+.\"
+.\" This is for command options
+.nr OptSize (16u)
+.\"
+.\" l=\n(.l
+.\" i=\n(.i
+.\" o=\n(.o
+.\" H=\n(.H
+.\" s=\n(.s
+.\" ColSize=\n[ColSize]
+.\"
+.\" Macro for easy option formatting: .opt \-x
+.de opt
+. TP \\n[OptSize]
+. BI \\$*
+..
+.\"
+.SH NAME
+ps \- report a snapshot of the current processes.
+.SH SYNOPSIS
+\fBps\fR [\fIoptions\fR]
+.PP
+.PP
+.SH DESCRIPTION
+.B ps
+displays information about a selection of the active processes.
+If you want a repetitive update of the selection and the
+displayed information, use\ \fItop\fR(1) instead.
+.P
+This version of \fBps\fR accepts several kinds of options:
+.PD 0
+.IP 1 4
+UNIX options, which may be grouped and must be preceded by a dash.
+.IP 2 4
+BSD options, which may be grouped and must not be used with a dash.
+.IP 3 4
+GNU long options, which are preceded by two dashes.
+.PD
+.PP
+Options of different types may be freely mixed, but conflicts can appear.
+There are some synonymous options, which are functionally identical, due
+to the many standards and \fBps\fR implementations that this \fBps\fR is
+compatible with.
+.P
+Note that "\fBps\ \-aux\fR" is distinct from "\fBps\ aux\fR".
+The POSIX and UNIX standards require that "\fBps\ \-aux\fR" print all
+processes owned by a user named "x", as well as printing all processes
+that would be selected by the \fB\-a\fR option. If the user named "x" does
+not exist, this \fBps\fR may interpret the command as "\fBps\ aux\fR"
+instead and print a warning. This behavior is intended to aid in
+transitioning old scripts and habits. It is fragile, subject to change,
+and thus should not be relied upon.
+.P
+By default, \fBps\fR selects all processes
+with the same effective user ID (euid=EUID) as the current user
+and
+associated with the same terminal as the invoker.
+It displays the process ID (pid=PID),
+the terminal associated with the process (tname=TTY),
+the cumulated CPU time in [dd\-]hh:mm:ss format (time=TIME),
+and the executable name (ucmd=CMD).
+Output is unsorted by default.
+.P
+The use of BSD\-style options will add process state (stat=STAT) to the
+default display and show the command args (args=COMMAND) instead of the
+executable name. You can override this with the \fBPS_FORMAT\fR
+environment variable. The use of BSD\-style options will also change the
+process selection to include processes on other terminals (TTYs) that
+are owned by you; alternately, this may be described as setting the
+selection to be the set of all processes filtered to exclude
+processes owned by other users or not on a terminal. These effects
+are not considered when options are described as being "identical" below,
+so \fB\-M\fR will be considered identical to \fBZ\fR and so on.
+.P
+Except as described below, process selection options are additive.
+The default selection is discarded, and then the selected processes
+are added to the set of processes to be displayed.
+A\ process will thus be shown if it meets any of the given
+selection criteria.
+.PP
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH "EXAMPLES"
+.TP 3
+To see every process on the system using standard syntax:
+.B ps\ \-e
+.br
+.B ps\ \-ef
+.br
+.B ps\ \-eF
+.br
+.B ps\ \-ely
+.TP
+To see every process on the system using BSD syntax:
+.B ps\ ax
+.br
+.B ps\ axu
+.TP
+To print a process tree:
+.B ps\ -ejH
+.br
+.B ps\ axjf
+.TP
+To get info about threads:
+.B ps\ -eLf
+.br
+.B ps\ axms
+.TP
+To get security info:
+.B ps\ -eo euser,ruser,suser,fuser,f,comm,label
+.br
+.B ps\ axZ
+.br
+.B ps\ -eM
+.TP
+To see every process running as root (real\ &\ effective\ ID) in user format:
+.B ps\ \-U\ root\ \-u\ root\ u
+.TP
+To see every process with a user\-defined format:
+.B ps\ \-eo\ pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm
+.br
+.B ps\ axo\ stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
+.br
+.B ps\ \-eopid,tt,user,fname,tmout,f,wchan
+.TP
+Print only the process IDs of syslogd:
+.B ps\ \-C\ syslogd\ \-o\ pid=
+.TP
+Print only the name of PID 42:
+.B ps\ \-p\ 42\ \-o\ comm=
+.PP
+.PP
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH "SIMPLE PROCESS SELECTION"
+.opt \-A
+Select all processes. Identical to \fB\-e\fR.
+
+.opt \-N
+Select all processes except those that fulfill the specified conditions.
+(negates the selection) Identical to \fB\-\-deselect\fR.
+
+.opt T
+Select all processes associated with this terminal. Identical to the
+\fBt\fR option without any argument.
+
+.opt \-a
+Select all processes except both session leaders (see \fIgetsid\fR(2)) and
+processes not associated with a terminal.
+
+.opt a
+Lift the BSD\-style "only yourself" restriction, which is imposed upon
+the set of all processes when some BSD\-style (without\ "\-") options
+are used or when the \fBps\fR personality setting is BSD\-like.
+The set of processes selected in this manner is
+in addition to the set of processes selected by other means.
+An alternate description is that this option causes \fBps\fR to
+list all processes with a terminal (tty),
+or to list all processes when used together with the \fBx\fR option.
+
+.opt \-d
+Select all processes except session leaders.
+
+.opt \-e
+Select all processes. Identical to \fB\-A\fR.
+
+.\" Current "g" behavior: add in the session leaders, which would
+.\" be excluded in the sunos4 personality. Supposed "g" behavior:
+.\" add in the group leaders -- at least according to the SunOS 4
+.\" man page on the FreeBSD site. Uh oh. I think I had tested SunOS
+.\" though, so maybe the code is correct.
+.opt g
+Really all, even session leaders. This flag is obsolete and may be
+discontinued in a future release. It is normally implied by the \fBa\fR flag,
+and is only useful when operating in the sunos4 personality.
+
+.opt r
+Restrict the selection to only running processes.
+
+.opt x
+Lift the BSD\-style "must have a tty" restriction, which is imposed upon
+the set of all processes when some BSD\-style (without\ "\-") options
+are used or when the \fBps\fR personality setting is BSD\-like.
+The set of processes selected in this manner is
+in addition to the set of processes selected by other means.
+An alternate description is that this option causes \fBps\fR to
+list all processes owned by you (same EUID as \fBps\fR),
+or to list all processes when used together with the \fBa\fR option.
+
+.opt \-\-deselect
+Select all processes except those that fulfill the specified conditions.
+(negates the selection) Identical to \fB\-N\fR.
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "PROCESS SELECTION BY LIST"
+These options accept a single argument in the form of a blank\-separated
+or comma\-separated list. They can be used multiple times.
+For\ example:\ \fBps\ \-p\ "1\ 2"\ \-p\ 3,4\fR
+.P
+.opt \-C \ cmdlist
+Select by command name.
+.br
+This selects the processes whose executable name is given in
+\fIcmdlist\fR.
+
+.opt \-G \ grplist
+Select by real group ID (RGID) or name.
+.br
+This selects the processes whose real group name or ID is in the
+\fIgrplist\fR list. The real group ID identifies the group of the user
+who created the process, see \fIgetgid\fR(2).
+
+.opt U \ userlist
+Select by effective user ID (EUID) or name.
+.br
+This selects the processes whose effective user name
+or ID is in \fIuserlist\fR.
+The effective user\ ID describes the user whose file
+access permissions are used by the process
+(see\ \fIgeteuid\fR(2)).
+Identical to \fB\-u\fR and\ \fB\-\-user\fR.
+
+.opt \-U \ userlist
+select by real user ID (RUID) or name.
+.br
+It selects the processes whose real user name or ID is in the
+\fIuserlist\fR list.
+The real user ID identifies the user who created the process,
+see\ \fIgetuid\fR(2).
+
+.opt \-g \ grplist
+Select by session OR by effective group name.
+.br
+Selection by session is specified by many standards,
+but selection by effective group is the logical behavior that
+several other operating systems use.
+This \fBps\fR will select by session when the list
+is completely numeric (as\ sessions\ are).
+Group ID numbers will work only when some group names are also specified.
+See the \fB\-s\fR and \fB\-\-group\fR options.
+
+.opt p \ pidlist
+Select by process ID. Identical to \fB\-p\fR and\ \fB\-\-pid\fR.
+
+.opt \-p \ pidlist
+Select by PID.
+.br
+This selects the processes whose process ID numbers appear in
+\fIpidlist\fR. Identical to \fBp\fR and\ \fB\-\-pid\fR.
+
+.opt \-s \ sesslist
+Select by session ID.
+.br
+This selects the processes
+with a session ID specified in\ \fIsesslist\fR.
+
+.opt t \ ttylist
+Select by tty. Nearly identical to \fB\-t\fR and \fB\-\-tty\fR,
+but can also be used with an empty \fIttylist\fR to indicate
+the terminal associated with \fBps\fR.
+Using the \fBT\fR option is considered cleaner than using \fBT\fR with
+an\ empty\ \fIttylist\fR.
+
+.opt \-t \ ttylist
+Select by tty.
+.br
+This selects the processes associated with the terminals
+given in \fIttylist\fR.
+Terminals (ttys, or screens for text output) can be specified in several
+forms: /dev/ttyS1, ttyS1, S1.
+A\ plain "\-" may be used to select processes not attached to any terminal.
+
+.opt \-u \ userlist
+Select by effective user ID (EUID) or name.
+.br
+This selects the processes whose effective user name or ID is in
+\fIuserlist\fR. The effective user ID describes the user whose file
+access permissions are used by the process (see\ \fIgeteuid\fR(2)).
+Identical to \fBU\fR and \fB\-\-user\fR.
+
+.opt \-\-Group \ grplist
+Select by real group ID (RGID) or name. Identical to \fB\-G\fR.
+
+.opt \-\-User \ userlist
+Select by real user ID (RUID) or name. Identical to \fB\-U\fR.
+
+.opt \-\-group \ grplist
+Select by effective group ID (EGID) or name.
+.br
+This selects the processes whose effective group name or ID is in
+\fIgrouplist\fR. The effective group ID describes the group whose file
+access permissions are used by the process (see\ \fIgeteuid\fR(2)).
+The \fB\-g\fR option is often an alternative to\ \fB\-\-group\fR.
+
+.opt \-\-pid \ pidlist
+Select by process\ ID. Identical to \fB\-p\fR\ and\ \fBp\fR.
+
+.opt \-\-ppid \ pidlist
+Select by parent process\ ID.
+This selects the processes
+with a parent\ process\ ID in \fRpidlist\fR.
+That\ is, it selects processes that are children
+of those listed in \fRpidlist\fR.
+
+.opt \-\-sid \ sesslist
+Select by session\ ID. Identical to\ \fB\-s\fR.
+
+.opt \-\-tty \ ttylist
+Select by terminal. Identical to \fB\-t\fR and\ \fBt\fR.
+
+.opt \-\-user \ userlist
+Select by effective user ID (EUID) or name.
+Identical to \fB\-u\fR and\ \fBU\fR.
+
+.opt \-\fI123\fR
+Identical to \fB\-\-sid\ \fI123\fR.
+
+.opt \fI123\fR
+Identical to \fB\-\-pid\ \fI123\fR.
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "OUTPUT FORMAT CONTROL"
+These options are used to choose the information displayed by \fBps\fR.
+The output may differ by personality.
+.PP
+
+.opt \-F
+extra full format. See the \fB\-f\fR option, which \fB\-F\fR implies.
+
+.opt \-O \ format
+is like \fB\-o\fR, but preloaded with some default columns.
+Identical to \fB\-o\ pid,\fIformat\fB,state,tname,time,command\fR
+or \fB\-o\ pid,\fIformat\fB,tname,time,cmd\fR, see\ \fB\-o\fR\ below.
+
+.opt O \ format
+is preloaded \fBo\fR (overloaded).
+.br
+The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
+format with some common fields predefined) or can be used to specify
+sort order. Heuristics are used to determine the behavior of this
+option. To ensure that the desired behavior is obtained (sorting or
+formatting), specify the option in some other way
+(e.g. with \fB\-O\fR or \fB\-\-sort\fR).
+When used as a formatting option, it is identical to \fB\-O\fR, with the
+BSD\ personality.
+
+.opt \-M
+Add a column of security data. Identical to \fBZ\fR. (for\ SE\ Linux)
+
+.opt X
+Register format.
+
+.opt Z
+Add a column of security data. Identical to \fB\-M\fR. (for\ SE\ Linux)
+
+.opt \-c
+Show different scheduler information for the \fB\-l\fR option.
+
+.opt \-f
+does full\-format listing. This option can be combined with many
+other UNIX\-style options to add additional columns. It also causes
+the command arguments to be printed. When used with \fB\-L\fR, the
+NLWP (number of threads) and LWP (thread ID) columns will be added.
+See the \fBc\fR option, the format keyword \fBargs\fR, and the
+format keyword \fBcomm\fR.
+
+.opt j
+BSD job control format.
+
+.opt \-j
+jobs format
+
+.opt l
+display BSD long format.
+
+.opt \-l
+long format. The \fB\-y\fR option is often useful with this.
+
+.opt o \ format
+specify user\-defined format. Identical to \fB\-o\fR and
+\fB\-\-format\fR.
+
+.opt \-o \ format
+user\-defined format.
+.br
+\fIformat\fR is a single argument in the form of a
+blank\-separated or comma\-separated list, which offers
+a way to specify individual output columns.
+The recognized keywords are described in the \fBSTANDARD FORMAT
+SPECIFIERS\fR section below.
+Headers may be
+renamed (\fBps\ \-o\ pid,ruser=RealUser\ \-o\ comm=Command\fR) as desired.
+If all column headers are empty (\fBps\ \-o\ pid=\ \-o\ comm=\fR) then the
+header line will not be output. Column width will increase as
+needed for wide headers; this may be used to widen up columns
+such as WCHAN (\fBps\ \-o\ pid,wchan=WIDE\-WCHAN\-COLUMN\ \-o\ comm\fR).
+Explicit width control (\fBps\ opid,wchan:42,cmd\fR) is offered too.
+The behavior of \fBps\ \-o\ pid=X,comm=Y\fR varies with personality;
+output may be one column named "X,comm=Y" or two columns
+named "X" and "Y". Use multiple \fB\-o\fR options when in doubt.
+Use the \fBPS_FORMAT\fR environment variable to specify a default
+as desired; DefSysV and DefBSD are macros that may be used to
+choose the default UNIX or BSD columns.
+
+.opt s
+display signal format
+
+.opt u
+display user\-oriented format
+
+.opt v
+display virtual memory format
+
+.opt \-y
+Do not show flags; show rss in place of addr.
+This option can only be used with \fB\-l\fR.
+
+.opt \-\-format \ format
+user\-defined format. Identical to \fB\-o\fR and \fBo\fR.
+
+.opt \-\-context
+Display security context format. (for\ SE\ Linux)
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "OUTPUT MODIFIERS"
+
+.\" .TP
+.\" .B C
+.\" use raw CPU time for %CPU instead of decaying average
+
+.opt \-H
+show process hierarchy (forest)
+
+.opt N \ namelist
+Specify namelist file. Identical to \fB\-n\fR, see \fB\-n\fR above.
+
+.opt O \ order
+Sorting order. (overloaded)
+.br
+The BSD \fBO\fR option can act like \fB\-O\fR (user\-defined output
+format with some common fields predefined) or can be used to specify
+sort order. Heuristics are used to determine the behavior of this
+option. To ensure that the desired behavior is obtained (sorting or
+formatting), specify the option in some other way (e.g. with \fB\-O\fR
+or \fB\-\-sort\fR).
+
+For sorting, obsolete BSD \fBO\fR option syntax is
+\fBO\fR[\fB+\fR|\fB\-\fR]\fIk1\fR[,[\fB+\fR|\fB\-\fR]\fIk2\fR[,...]].
+It orders the processes listing according to the multilevel sort specified by
+the sequence of one\-letter short keys \fIk1\fR, \fIk2\fR, ... described
+in the \fBOBSOLETE SORT KEYS\fR section below.
+The\ "+" is currently optional,
+merely re\-iterating the default direction on a key,
+but may help to distinguish an \fBO\fR sort from an \fBO\fR format.
+The\ "\-" reverses direction only on the key it precedes.
+
+.opt S
+Sum up some information, such as CPU usage, from dead child processes
+into their parent. This is useful for examining a system where a
+parent process repeatedly forks off short\-lived children to do work.
+
+.opt c
+Show the true command name. This is derived from the name of the
+executable file, rather than from the argv value. Command arguments
+and any modifications to them (see\ \fIsetproctitle\fR(3)) are
+thus not shown. This option
+effectively turns the \fBargs\fR format keyword into the \fBcomm\fR
+format keyword; it is useful with the \fB\-f\fR format option and with
+the various BSD\-style format options, which all normally
+display the command arguments.
+See the \fB\-f\fR option, the format keyword \fBargs\fR, and the
+format keyword \fBcomm\fR.
+
+.opt e
+Show the environment after the command.
+
+.opt f
+ASCII\-art process hierarchy (forest)
+
+.opt h
+No header. (or, one header per screen in the BSD personality)
+.br
+The \fBh\fR option is problematic. Standard BSD \fBps\fR uses
+this option to print a header on each page of output, but older
+Linux \fBps\fR uses this option to totally disable the header.
+This version of \fBps\fR follows the Linux usage of not printing
+the header unless the BSD personality has been selected, in which
+case it prints a header on each page of output. Regardless of the
+current personality, you can use the long options \fB\-\-headers\fR
+and \fB\-\-no\-headers\fR to enable printing headers each page or
+disable headers entirely, respectively.
+
+.opt k \ spec
+specify sorting order. Sorting syntax is
+[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]]
+Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section.
+The\ "+" is optional since default direction is increasing numerical or
+lexicographic order. Identical to \fB\-\-sort\fR. Examples:
+.br
+\fBps\ jaxkuid,\-ppid,+pid\fR
+.br
+\fBps\ axk\ comm\ o\ comm,args\fR
+.br
+\fBps\ kstart_time\ \-ef\fR
+
+.opt \-n \ namelist
+set namelist file. Identical to \fBN\fR.
+.br
+The namelist file is needed for a proper WCHAN display, and must match
+the current Linux kernel exactly for correct output.
+Without this option, the default search path for the namelist is:
+
+ $PS_SYSMAP
+.br
+ $PS_SYSTEM_MAP
+.br
+ /proc/*/wchan
+.br
+ /boot/System.map\-\`uname\ \-r\`
+.br
+ /boot/System.map
+.br
+ /lib/modules/\`uname\ \-r\`/System.map
+.br
+ /usr/src/linux/System.map
+.br
+ /System.map
+
+.opt n
+Numeric output for WCHAN and USER. (including all types of UID and GID)
+
+.opt \-w
+Wide output. Use this option twice for unlimited width.
+
+.opt w
+Wide output. Use this option twice for unlimited width.
+
+.opt \-\-cols \ n
+set screen width
+
+.opt \-\-columns \ n
+set screen width
+
+.opt \-\-cumulative
+include some dead child process data (as a sum with the parent)
+
+.opt \-\-forest
+ASCII art process tree
+
+.opt \-\-headers
+repeat header lines, one per page of output
+
+.opt \-\-no\-headers
+print no header line at all
+
+.opt \-\-lines \ n
+set screen height
+
+.opt \-\-rows \ n
+set screen height
+
+.opt \-\-sort \ spec
+specify sorting order. Sorting syntax is
+[\fB+\fR|\fB\-\fR]\fIkey\fR[,[\fB+\fR|\fB\-\fR]\fIkey\fR[,...]]
+Choose a multi\-letter key from the \fBSTANDARD FORMAT SPECIFIERS\fR section.
+The\ "+" is optional since default direction is increasing numerical or
+lexicographic order. Identical to\ \fBk\fR.
+For example: \fBps\ jax\ \-\-sort=uid,\-ppid,+pid\fR
+
+.opt \-\-width \ n
+set screen width
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "THREAD DISPLAY"
+.PD 0
+
+.opt H
+Show threads as if they were processes
+
+.opt \-L
+Show threads, possibly with LWP and NLWP columns
+
+.opt \-T
+Show threads, possibly with SPID column
+
+.opt m
+Show threads after processes
+
+.opt \-m
+Show threads after processes
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH "OTHER INFORMATION"
+.PD 0
+
+.opt L
+List all format specifiers.
+
+.opt \-V
+Print the procps version.
+
+.opt V
+Print the procps version.
+
+.opt \-\-help
+Print a help message.
+
+.opt \-\-info
+Print debugging info.
+
+.opt \-\-version
+Print the procps version.
+
+.\" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.PD
+.PP
+.SH NOTES
+This \fBps\fR works by reading the virtual files in\ /proc.
+This \fBps\fR does not need to be setuid kmem or have any privileges to run.
+Do not give this \fBps\fR any special permissions.
+
+This \fBps\fR needs access to namelist data for proper WCHAN display.
+For kernels prior to 2.6, the System.map file must be installed.
+
+CPU usage is currently expressed as the percentage of time spent
+running during the entire lifetime of a process.
+This is not ideal, and\ it does not conform to the
+standards that \fBps\fR otherwise conforms\ to.
+CPU\ usage is unlikely to add up to exactly\ 100%.
+
+The SIZE and RSS fields don't count some parts of a process including the
+page tables, kernel stack, struct thread_info, and struct task_struct.
+This is usually at least 20\ KiB of memory that is always resident.
+SIZE is the virtual size of the process (code+data+stack).
+
+Processes marked <defunct> are dead processes (so\-called\ "zombies") that
+remain because their parent has not destroyed them properly. These processes
+will be destroyed by \fIinit\fR(8) if the parent process exits.
+
+
+.SH "PROCESS FLAGS"
+The sum of these values is displayed in the "F" column,
+which is provided by the \fBflags\fR output specifier.
+.PD 0
+.TP 5
+1
+forked but didn't exec
+.TP
+4
+used super\-user privileges
+.PD
+.PP
+.SH "PROCESS STATE CODES"
+Here are the different values that the \fBs\fR, \fBstat\fR and
+\fBstate\fR output specifiers (header\ "STAT"\ or\ "S") will display to
+describe the state of a process.
+.PD 0
+.TP 5
+D
+Uninterruptible sleep (usually\ IO)
+.TP
+R
+Running or runnable (on\ run\ queue)
+.TP
+S
+Interruptible sleep (waiting for an event to complete)
+.TP
+T
+Stopped, either by a job control signal or because it is being traced.
+.TP
+W
+paging (not valid since the 2.6.xx kernel)
+.TP
+X
+dead (should never be seen)
+.TP
+Z
+Defunct ("zombie") process, terminated but not reaped by its parent.
+.PD
+.PP
+For BSD formats and when the \fBstat\fR keyword is used, additional
+characters may be displayed:
+.PD 0
+.TP 5
+<
+high\-priority (not nice to other users)
+.TP
+N
+low\-priority (nice to other users)
+.TP
+L
+has pages locked into memory (for real\-time and custom\ IO)
+.TP
+s
+is a session leader
+.TP
+l
+is multi-threaded (using CLONE_THREAD, like NPTL pthreads\ do)
+.TP
++
+is in the foreground process group
+.PD
+.PP
+.PP
+.SH "OBSOLETE SORT KEYS"
+These keys are used by the BSD \fBO\fR option (when it is used for
+sorting). The GNU \fB\-\-sort\fR option doesn't use these keys, but the
+specifiers described below in the \fBSTANDARD FORMAT SPECIFIERS\fR
+section. Note that the values used in sorting are the internal
+values \fBps\fR uses and not the "cooked" values used in some of
+the output format fields (e.g. sorting on tty will sort into
+device number, not according to the terminal name displayed).
+Pipe \fBps\fR output into the \fIsort\fR(1) command if you want
+to sort the cooked values.
+
+.TS
+l l lw(3i).
+\fBKEY LONG DESCRIPTION\fR
+c cmd simple name of executable
+C pcpu cpu utilization
+f flags flags as in long format F field
+g pgrp process group ID
+G tpgid controlling tty process group ID
+j cutime cumulative user time
+J cstime cumulative system time
+k utime user time
+m min_flt number of minor page faults
+M maj_flt number of major page faults
+n cmin_flt cumulative minor page faults
+N cmaj_flt cumulative major page faults
+o session session ID
+p pid process ID
+P ppid parent process ID
+r rss resident set size
+R resident resident pages
+s size memory size in kilobytes
+S share amount of shared pages
+t tty the device number of the controlling tty
+T start_time time process was started
+U uid user ID number
+u user user name
+v vsize total VM size in kB
+y priority kernel scheduling priority
+.\"K stime system time (conflict, system vs. start time)
+.TE
+.PP
+.PP
+.SH "AIX FORMAT DESCRIPTORS"
+This \fBps\fR supports AIX format descriptors, which work somewhat like the
+formatting codes of \fIprintf\fR(1) and \fIprintf\fR(3). For example, the normal
+default output can be produced with this: \fBps\ \-eo\ "%p\ %y\ %x\ %c"\fR.
+The\ \fBNORMAL\fR codes are described in the next section.
+.TS
+l l l.
+\fBCODE NORMAL HEADER\fR
+%C pcpu %CPU
+%G group GROUP
+%P ppid PPID
+%U user USER
+%a args COMMAND
+%c comm COMMAND
+%g rgroup RGROUP
+%n nice NI
+%p pid PID
+%r pgid PGID
+%t etime ELAPSED
+%u ruser RUSER
+%x time TIME
+%y tty TTY
+%z vsz VSZ
+.TE
+
+.SH "STANDARD FORMAT SPECIFIERS"
+Here are the different keywords that may be used to control the output
+format (e.g. with option \fB\-o\fR) or to sort the selected processes
+with the GNU\-style \fB\-\-sort\fR option.
+
+For example: \fBps\ \-eo\ pid,user,args\ \-\-sort\ user\fR
+
+This version of \fBps\fR tries to recognize most of the keywords used in
+other implementations of \fBps\fR.
+
+The following user\-defined format specifiers may contain
+spaces: \fBargs\fR, \fBcmd\fR, \fBcomm\fR, \fBcommand\fR, \fBfname\fR,
+\fBucmd\fR, \fBucomm\fR,
+\fBlstart\fR, \fBbsdstart\fR, \fBstart\fR.
+
+Some keywords may not be available for sorting.
+
+.\" #######################################################################
+.\" lB1 lB1 lB1 lB1 s s s
+.\" lB1 l1 l1 l1 s s s.
+.\"
+.\" lB1 lB1 lBw(5.5i)
+.\" lB1 l1 l.
+.\"
+.TS
+expand;
+lB1 lB1 lBw(\n[ColSize]n)
+lB1 l1 l.
+CODE HEADER DESCRIPTION
+
+%cpu %CPU T{
+cpu utilization of the process in "##.#" format. Currently, it is the CPU time
+used divided by the time the process has been running (cputime/realtime
+ratio), expressed as a percentage. It will not add up to 100% unless you
+are lucky. (alias\ \fBpcpu\fR).
+T}
+
+%mem %MEM T{
+ratio of the process's resident set size to the physical memory on
+the machine, expressed as a percentage. (alias\ \fBpmem\fR).
+T}
+
+args COMMAND T{
+command with all its arguments as a string. Modifications to the arguments
+may be shown. The output in this column may contain spaces.
+A\ process marked <defunct> is partly dead, waiting
+to be fully destroyed by its parent. Sometimes the process args
+will be unavailable; when this happens, \fBps\fR will instead
+print the executable name in brackets.
+(alias\ \fBcmd\fR,\ \fBcommand\fR). See also the \fBcomm\fR format
+keyword, the \fB\-f\fR option, and the \fBc\fR option.
+.br
+When specified last, this column will extend to the edge of the display.
+If \fBps\fR can not determine display width, as when output is redirected
+(piped) into a file or another command, the output width is undefined.
+(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on)
+The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may
+be used to exactly determine the width in this case.
+The \fBw\fR or \fB\-w\fR option may be also be used to adjust width.
+T}
+
+blocked BLOCKED T{
+mask of the blocked signals, see \fIsignal\fR(7).
+According to the width of the field,
+a\ 32\-bit or 64\-bit mask in hexadecimal format is displayed.
+(alias\ \fBsig_block\fR,\ \fBsigmask\fR).
+T}
+
+bsdstart START T{
+time the command started. If the process was started less
+than 24 hours ago, the output format is "\ HH:MM",
+else it is "mmm\ dd"
+(where mmm is the three letters of the month).
+See also \fBlstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR.
+T}
+
+bsdtime TIME T{
+accumulated cpu time, user\ +\ system. The display format is usually
+"MMM:SS", but can be shifted to the right if the process used more than 999
+minutes of cpu time.
+T}
+
+c C T{
+processor utilization. Currently, this is the integer value of
+the percent usage over the lifetime of the process. (see\ \fB%cpu\fR).
+T}
+
+caught CAUGHT T{
+mask of the caught signals, see \fIsignal\fR(7). According to the
+width of the field, a 32 or 64 bits mask in hexadecimal format is
+displayed. (alias\ \fBsig_catch\fR,\ \fBsigcatch\fR).
+T}
+
+class CLS T{
+scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBcls\fR).
+Field's possible values are:
+.br
+\- not reported
+.br
+TS SCHED_OTHER
+.br
+FF SCHED_FIFO
+.br
+RR SCHED_RR
+.br
+B SCHED_BATCH
+.br
+ISO SCHED_ISO
+.br
+IDL SCHED_IDLE
+.br
+? unknown value
+T}
+
+cls CLS T{
+scheduling class of the process. (alias\ \fBpolicy\fR,\ \fBclass\fR).
+Field's possible values are:
+.br
+\- not reported
+.br
+TS SCHED_OTHER
+.br
+FF SCHED_FIFO
+.br
+RR SCHED_RR
+.br
+B SCHED_BATCH
+.br
+ISO SCHED_ISO
+.br
+IDL SCHED_IDLE
+.br
+? unknown value
+T}
+
+cmd CMD T{
+see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcommand\fR).
+T}
+
+comm COMMAND T{
+command name (only\ the executable\ name). Modifications to the command
+name will not be shown. A\ process marked <defunct> is partly dead, waiting
+to be fully destroyed by its parent. The output in this
+column may contain spaces. (alias\ \fBucmd\fR,\ \fBucomm\fR).
+See also the \fBargs\fR format
+keyword, the \fB\-f\fR option, and the \fBc\fR option.
+.br
+When specified last, this column will extend to the edge of the display.
+If \fBps\fR can not determine display width, as when output is redirected
+(piped) into a file or another command, the output width is undefined.
+(it may be 80, unlimited, determined by the \fBTERM\fR variable, and so on)
+The \fBCOLUMNS\fR environment variable or \fB\-\-cols\fR option may
+be used to exactly determine the width in this case.
+The \fBw\fR or \fB\-w\fR option may be also be used to adjust width.
+T}
+
+command COMMAND T{
+see \fBargs\fR. (alias\ \fBargs\fR,\ \fBcmd\fR).
+T}
+
+cp CP T{
+per\-mill (tenths of a percent) CPU usage. (see\ \fB%cpu\fR).
+T}
+
+cputime TIME T{
+cumulative CPU time, "[dd\-]hh:mm:ss" format. (alias\ \fBtime\fR).
+T}
+
+egid EGID T{
+effective group ID number of the process as a decimal integer.
+(alias\ \fBgid\fR).
+T}
+
+egroup EGROUP T{
+effective group ID of the process. This will be the textual group ID,
+if it can be obtained and the field width permits, or a decimal
+representation otherwise. (alias\ \fBgroup\fR).
+T}
+
+eip EIP T{
+instruction pointer.
+T}
+
+esp ESP T{
+stack pointer.
+T}
+
+etime ELAPSED T{
+elapsed time since the process was started,
+in\ the form\ [[dd\-]hh:]mm:ss.
+T}
+
+euid EUID T{
+effective user\ ID. (alias\ \fBuid\fR).
+T}
+
+euser EUSER T{
+effective user\ name. This will be the textual
+user\ ID, if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+The\ \fBn\fR\ option can be used
+to force the decimal representation.
+(alias\ \fBuname\fR,\ \fBuser\fR).
+T}
+
+f F T{
+flags associated with the process, see the \fBPROCESS FLAGS\fR section.
+(alias\ \fBflag\fR,\ \fBflags\fR).
+T}
+
+fgid FGID T{
+filesystem access group\ ID. (alias\ \fBfsgid\fR).
+T}
+
+fgroup FGROUP T{
+filesystem access group\ ID.
+This will be the textual user\ ID, if\ it can be obtained
+and the field width permits,
+or\ a\ decimal representation otherwise.
+(alias\ \fBfsgroup\fR).
+T}
+
+flag F T{
+see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflags\fR).
+T}
+
+flags F T{
+see\ \fBf\fR. (alias\ \fBf\fR,\ \fBflag\fR).
+T}
+
+fname COMMAND T{
+first 8 bytes of the base name of the process's executable file.
+The output in this column may contain spaces.
+T}
+
+fuid FUID T{
+filesystem access user\ ID. (alias\ \fBfsuid\fR).
+T}
+
+fuser FUSER T{
+filesystem access user\ ID. This will be the textual user\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+gid GID T{
+see\ \fBegid\fR. (alias\ \fBegid\fR).
+T}
+
+group GROUP T{
+see\ \fBegroup\fR. (alias\ \fBegroup\fR).
+T}
+
+ignored IGNORED T{
+mask of the ignored signals, see \fIsignal\fR(7). According to the
+width of the field, a\ 32\-bit or 64\-bit mask in hexadecimal format
+is displayed. (alias \fBsig_ignore\fR, \fBsigignore\fR).
+T}
+
+label LABEL T{
+security label, most commonly used for SE\ Linux context data.
+This is for the \fIMandatory Access Control\fR ("MAC") found on
+high\-security systems.
+T}
+
+lstart STARTED T{
+time the command started.
+See also \fBbsdstart\fR, \fBstart\fR, \fBstart_time\fR, and \fBstime\fR.
+T}
+
+lwp LWP T{
+lwp (light weight process, or thread) ID of the lwp being reported.
+(alias\ \fBspid\fR,\ \fBtid\fR).
+T}
+
+ni NI T{
+nice value. This ranges from 19 (nicest) to \-20 (not\ nice to\ others),
+see\ \fInice\fR(1). (alias\ \fBnice\fR).
+T}
+
+nice NI T{
+see\ \fBni\fR. (alias\ \fBni\fR).
+T}
+
+nlwp NLWP T{
+number of lwps (threads) in the process. (alias\ \fBthcount\fR).
+T}
+
+nwchan WCHAN T{
+address of the kernel function where the process is sleeping
+(use \fBwchan\fR if you want the kernel function name).
+Running tasks will display a dash ('\-') in this column.
+T}
+
+pcpu %CPU T{
+see\ \fB%cpu\fR. (alias\ \fB%cpu\fR).
+T}
+
+pending PENDING T{
+mask of the pending signals. See\ \fIsignal\fR(7). Signals pending on
+the process are distinct from signals pending on individual threads.
+Use the \fBm\fR option or the \fB\-m\fR option to see both.
+According to the width of the field, a\ 32\-bit or 64\-bit mask in
+hexadecimal format is displayed. (alias\ \fBsig\fR).
+T}
+
+pgid PGID T{
+process group\ ID or, equivalently, the process\ ID of the
+process group leader. (alias\ \fBpgrp\fR).
+T}
+
+pgrp PGRP T{
+see\ \fBpgid\fR. (alias\ \fBpgid\fR).
+T}
+
+pid PID T{
+process\ ID number of the process.
+T}
+
+pmem %MEM T{
+see\ \fB%mem\fR. (alias\ \fB%mem\fR).
+T}
+
+policy POL T{
+scheduling class of the process. (alias\ \fBclass\fR,\ \fBcls\fR).
+Possible values are:
+.br
+\- not reported
+.br
+TS SCHED_OTHER
+.br
+FF SCHED_FIFO
+.br
+RR SCHED_RR
+.br
+B SCHED_BATCH
+.br
+ISO SCHED_ISO
+.br
+IDL SCHED_IDLE
+.br
+? unknown value
+T}
+
+ppid PPID T{
+parent process ID.
+T}
+
+psr PSR T{
+processor that process is currently assigned to.
+T}
+
+rgid RGID T{
+real group ID.
+T}
+
+rgroup RGROUP T{
+real group name. This will be the textual group\ ID, if\ it can be
+obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+rss RSS T{
+resident set size, the non\-swapped physical memory that
+a task has used (in\ kiloBytes).
+(alias\ \fBrssize\fR,\ \fBrsz\fR).
+T}
+
+rssize RSS T{
+see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrsz\fR).
+T}
+
+rsz RSZ T{
+see\ \fBrss\fR. (alias\ \fBrss\fR,\ \fBrssize\fR).
+T}
+
+rtprio RTPRIO T{
+realtime priority.
+T}
+
+ruid RUID T{
+real user\ ID.
+T}
+
+ruser RUSER T{
+real user\ ID. This will be the textual user\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+s S T{
+minimal state display (one\ character).
+See\ section \fBPROCESS STATE CODES\fR for the different values.
+See\ also \fBstat\fR if you want additional
+information displayed. (alias\ \fBstate\fR).
+T}
+
+sched SCH T{
+scheduling policy of the process. The policies SCHED_OTHER (SCHED_NORMAL),
+SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_ISO, and SCHED_IDLE are respectively
+displayed as 0,\ 1,\ 2,\ 3,\ 4,\ and\ 5.
+T}
+
+sess SESS T{
+session\ ID or, equivalently, the process\ ID of the session\ leader.
+(alias\ \fBsession\fR,\ \fBsid\fR).
+T}
+
+sgi_p P T{
+processor that the process is currently executing on.
+Displays "*" if the process is not currently running or runnable.
+T}
+
+sgid SGID T{
+saved group\ ID.
+(alias\ \fBsvgid\fR).
+T}
+
+sgroup SGROUP T{
+saved group\ name. This will be the textual group\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+T}
+
+sid SID T{
+see\ \fBsess\fR. (alias\ \fBsess\fR,\ \fBsession\fR).
+T}
+
+sig PENDING T{
+see\ \fBpending\fR. (alias\ \fBpending\fR,\ \fBsig_pend\fR).
+T}
+
+sigcatch CAUGHT T{
+see\ \fBcaught\fR. (alias\ \fBcaught\fR,\ \fBsig_catch\fR).
+T}
+
+sigignore IGNORED T{
+see\ \fBignored\fR. (alias\ \fBignored\fR,\ \fBsig_ignore\fR).
+T}
+
+sigmask BLOCKED T{
+see\ \fBblocked\fR. (alias\ \fBblocked\fR,\ \fBsig_block\fR).
+T}
+
+size SZ T{
+approximate amount of swap space that would be required
+if the process were to dirty all writable pages and then
+be swapped out.
+This number is very\ rough!
+T}
+
+spid SPID T{
+see \fBlwp\fR. (alias\ \fBlwp\fR,\ \fBtid\fR).
+T}
+
+stackp STACKP T{
+address of the bottom (start) of stack for the process.
+T}
+
+start STARTED T{
+time the command started.
+If the process was started less than 24 hours ago,
+the output format is "HH:MM:SS",
+else it is "\ \ mmm\ dd"
+(where mmm is a three\-letter month\ name).
+See also \fBlstart\fR, \fBbsdstart\fR, \fBstart_time\fR, and \fBstime\fR.
+T}
+
+start_time START T{
+starting time or date of the process.
+Only the year will be displayed if the process was not
+started the same year \fBps\fR was invoked,
+or\ "mmmdd" if\ it was not started the same day,
+or\ "HH:MM" otherwise.
+See also \fBbsdstart\fR, \fBstart\fR, \fBlstart\fR, and \fBstime\fR.
+T}
+
+stat STAT T{
+multi\-character process state.
+See\ section \fBPROCESS STATE CODES\fR
+for the different values meaning.
+See also \fBs\fR and \fBstate\fR if you just want
+the first character displayed.
+T}
+
+state S T{
+see\ \fBs\fR. (alias\ \fBs\fR).
+T}
+
+suid SUID T{
+saved user\ ID. (alias\ \fBsvuid\fR).
+T}
+
+suser SUSER T{
+saved user name. This will be the textual user\ ID,
+if\ it can be obtained and the field width permits,
+or\ a\ decimal representation otherwise.
+(alias\ \fBsvuser\fR).
+T}
+
+svgid SVGID T{
+see\ \fBsgid\fR. (alias\ \fBsgid\fR).
+T}
+
+svuid SVUID T{
+see\ \fBsuid\fR. (alias\ \fBsuid\fR).
+T}
+
+sz SZ T{
+size in physical pages of the core image of the process.
+This includes text, data, and stack space.
+Device mappings are currently excluded; this is subject to change.
+See \fBvsz\fR and \fBrss\fR.
+T}
+
+thcount THCNT T{
+see \fBnlwp\fR. (alias\ \fBnlwp\fR).
+number of kernel threads owned by the process.
+T}
+
+tid TID T{
+see\ \fBlwp\fR. (alias\ \fBlwp\fR).
+T}
+
+time TIME T{
+cumulative CPU\ time, "[dd\-]hh:mm:ss" format. (alias\ \fBcputime\fR).
+T}
+
+tname TTY T{
+controlling tty (terminal).
+(alias\ \fBtt\fR,\ \fBtty\fR).
+T}
+
+tpgid TPGID T{
+ID of the foreground process group on the tty (terminal) that
+the process is connected to, or \-1 if the process is not connected
+to a tty.
+T}
+
+tt TT T{
+controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtty\fR).
+T}
+
+tty TT T{
+controlling tty (terminal). (alias\ \fBtname\fR,\ \fBtt\fR).
+T}
+
+ucmd CMD T{
+see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucomm\fR).
+T}
+
+ucomm COMMAND T{
+see \fBcomm\fR. (alias\ \fBcomm\fR,\ \fBucmd\fR).
+T}
+
+uid UID T{
+see \fBeuid\fR. (alias\ \fBeuid\fR).
+T}
+
+uname USER T{
+see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuser\fR).
+T}
+
+user USER T{
+see \fBeuser\fR. (alias\ \fBeuser\fR,\ \fBuname\fR).
+T}
+
+vsize VSZ T{
+see \fBvsz\fR. (alias\ \fBvsz\fR).
+T}
+
+vsz VSZ T{
+virtual memory size of the process in KiB (1024\-byte\ units).
+Device mappings are currently excluded; this is subject to change.
+(alias\ \fBvsize\fR).
+T}
+
+wchan WCHAN T{
+name of the kernel function in which the process is sleeping,
+a\ "\-"\ if the process is running,
+or a "*"\ if the process is multi\-threaded and
+\fBps\fR is not displaying threads.
+T}
+.TE
+.\" #######################################################################
+.PP
+.PP
+.SH "ENVIRONMENT VARIABLES"
+The following environment variables could affect \fBps\fR:
+.TP 3
+.B COLUMNS
+Override default display width.
+.TP
+.B LINES
+Override default display height.
+.TP
+.B PS_PERSONALITY
+Set to one of posix, old, linux, bsd, sun, digital...
+(see\ section\ \fBPERSONALITY\fR\ below).
+.TP
+.B CMD_ENV
+Set to one of posix, old, linux, bsd, sun, digital...
+(see\ section\ \fBPERSONALITY\fR\ below).
+.TP
+.B I_WANT_A_BROKEN_PS
+Force obsolete command line interpretation.
+.TP
+.B LC_TIME
+Date format.
+.TP
+.B PS_COLORS
+Not currently supported.
+.TP
+.B PS_FORMAT
+Default output format override. You may set this to a format
+string of the type used for the \fB\-o\fR option.
+The \fBDefSysV\fR and \fBDefBSD\fR values are particularly useful.
+.TP
+.B PS_SYSMAP
+Default namelist (System.map) location.
+.TP
+.B PS_SYSTEM_MAP
+Default namelist (System.map) location.
+.TP
+.B POSIXLY_CORRECT
+Don't find excuses to ignore bad "features".
+.TP
+.B POSIX2
+When set to "on", acts as \fBPOSIXLY_CORRECT\fR.
+.TP
+.B UNIX95
+Don't find excuses to ignore bad "features".
+.TP
+.B _XPG
+Cancel \fBCMD_ENV\fI=irix\fR non\-standard behavior.
+.PP
+In general, it\ is a bad idea to set these variables.
+The one exception is \fBCMD_ENV\fR or \fBPS_PERSONALITY\fR,
+which could be set to Linux for normal systems.
+Without that setting,
+\fBps\fR follows the useless and bad parts of the Unix98 standard.
+.PP
+.PP
+.SH "PERSONALITY"
+.TS
+l l.
+390 like the S/390 OpenEdition \fBps\fR
+aix like AIX \fBps\fR
+bsd like FreeBSD \fBps\fR (totally\ non\-standard)
+compaq like Digital Unix \fBps\fR
+debian like the old Debian \fBps\fR
+digital like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR
+gnu like the old Debian \fBps\fR
+hp like HP\-UX \fBps\fR
+hpux like HP\-UX \fBps\fR
+irix like Irix \fBps\fR
+linux ***** RECOMMENDED *****
+old like the original Linux \fBps\fR (totally\ non\-standard)
+os390 like OS/390 Open Edition \fBps\fR
+posix standard
+s390 like OS/390 Open Edition \fBps\fR
+sco like SCO \fBps\fR
+sgi like Irix \fBps\fR
+solaris2 like Solaris 2+ (SunOS 5) \fBps\fR
+sunos4 like SunOS 4 (Solaris 1) \fBps\fR (totally\ non\-standard)
+svr4 standard
+sysv standard
+tru64 like Tru64 (was Digital\ Unix, was OSF/1) \fBps\fR
+unix standard
+unix95 standard
+unix98 standard
+.TE
+.PP
+.PP
+.SH "SEE ALSO"
+\fItop\fR(1), \fIpgrep\fR(1), \fIpstree\fR(1), \fIproc\fR(5).
+.PP
+.PP
+.SH STANDARDS
+This \fBps\fR conforms to:
+.PP
+.PD 0
+.IP 1 4
+Version 2 of the Single Unix Specification
+.IP 2 4
+The Open Group Technical Standard Base Specifications, Issue\ 6
+.IP 3 4
+IEEE Std 1003.1, 2004\ Edition
+.IP 4 4
+X/Open System Interfaces Extension [UP\ XSI]
+.IP 5 4
+ISO/IEC 9945:2003
+.PD
+.PP
+.SH AUTHOR
+\fBps\fR was originally written by Branko Lankester <lankeste@fwi.uva.nl>. Michael
+K. Johnson <johnsonm@redhat.com> re\-wrote it significantly to use the proc
+filesystem, changing a few things in the process. Michael Shields
+<mjshield@nyx.cs.du.edu> added the pid\-list feature. Charles Blake
+<cblake@bbn.com> 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
+<albert@users.sf.net> rewrote ps for full Unix98 and BSD support, along with
+some ugly hacks for obsolete and foreign syntax.
+
+Please send bug reports to <procps\-feedback@lists.sf.net>.
+No\ subscription is required or suggested.
--- /dev/null
+-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
+
--- /dev/null
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "../proc/readproc.h"
+#include "../proc/procps.h"
+
+//#define process_group_leader(p) ((p)->pgid == (p)->tgid)
+//#define some_other_user(p) ((p)->euid != cached_euid)
+#define has_our_euid(p) ((unsigned)(p)->euid == (unsigned)cached_euid)
+#define on_our_tty(p) ((unsigned)(p)->tty == (unsigned)cached_tty)
+#define running(p) (((p)->state=='R')||((p)->state=='D'))
+#define session_leader(p) ((p)->session == (p)->tgid)
+#define without_a_tty(p) (!(p)->tty)
+
+static unsigned long select_bits = 0;
+
+/***** prepare select_bits for use */
+const char *select_bits_setup(void){
+ int switch_val = 0;
+ /* don't want a 'g' screwing up simple_select */
+ if(!simple_select && !prefer_bsd_defaults){
+ select_bits = 0xaa00; /* the STANDARD selection */
+ return NULL;
+ }
+ /* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */
+ if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) )
+ switch_val = simple_select|SS_B_g;
+ else
+ switch_val = simple_select;
+ switch(switch_val){
+ /* UNIX options */
+ case SS_U_a | SS_U_d: select_bits = 0x3f3f; break; /* 3333 or 3f3f */
+ case SS_U_a: select_bits = 0x0303; break; /* 0303 or 0f0f */
+ case SS_U_d: select_bits = 0x3333; break;
+ /* SunOS 4 only (others have 'g' enabled all the time) */
+ case 0: select_bits = 0x0202; break;
+ case SS_B_a: select_bits = 0x0303; break;
+ case SS_B_x : select_bits = 0x2222; break;
+ case SS_B_x | SS_B_a: select_bits = 0x3333; break;
+ /* General BSD options */
+ case SS_B_g : select_bits = 0x0a0a; break;
+ case SS_B_g | SS_B_a: select_bits = 0x0f0f; break;
+ case SS_B_g | SS_B_x : select_bits = 0xaaaa; break;
+ case SS_B_g | SS_B_x | SS_B_a: /* convert to -e instead of using 0xffff */
+ all_processes = 1;
+ simple_select = 0;
+ break;
+ default:
+ return "Process selection options conflict.";
+ break;
+ }
+ return NULL;
+}
+
+/***** selected by simple option? */
+static int table_accept(proc_t *buf){
+ unsigned proc_index;
+ proc_index = (has_our_euid(buf) <<0)
+ | (session_leader(buf) <<1)
+ | (without_a_tty(buf) <<2)
+ | (on_our_tty(buf) <<3);
+ return (select_bits & (1<<proc_index));
+}
+
+/***** selected by some kind of list? */
+static int proc_was_listed(proc_t *buf){
+ selection_node *sn = selection_list;
+ int i;
+ if(!sn) return 0;
+ while(sn){
+ switch(sn->typecode){
+ default:
+ printf("Internal error in ps! Please report this bug.\n");
+
+#define return_if_match(foo,bar) \
+ i=sn->n; while(i--) \
+ if((unsigned)(buf->foo) == (unsigned)(*(sn->u+i)).bar) \
+ return 1
+
+ break; case SEL_RUID: return_if_match(ruid,uid);
+ break; case SEL_EUID: return_if_match(euid,uid);
+ break; case SEL_SUID: return_if_match(suid,uid);
+ break; case SEL_FUID: return_if_match(fuid,uid);
+
+ break; case SEL_RGID: return_if_match(rgid,gid);
+ break; case SEL_EGID: return_if_match(egid,gid);
+ break; case SEL_SGID: return_if_match(sgid,gid);
+ break; case SEL_FGID: return_if_match(fgid,gid);
+
+ break; case SEL_PGRP: return_if_match(pgrp,pid);
+ break; case SEL_PID : return_if_match(tgid,pid);
+ break; case SEL_PPID: return_if_match(ppid,ppid);
+ break; case SEL_TTY : return_if_match(tty,tty);
+ break; case SEL_SESS: return_if_match(session,pid);
+
+ break; case SEL_COMM: i=sn->n; while(i--)
+ if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 15 )) return 1;
+
+
+
+#undef return_if_match
+
+ }
+ sn = sn->next;
+ }
+ return 0;
+}
+
+
+/***** This must satisfy Unix98 and as much BSD as possible */
+int want_this_proc(proc_t *buf){
+ int accepted_proc = 1; /* assume success */
+ /* elsewhere, convert T to list, U sets x implicitly */
+
+ /* handle -e -A */
+ if(all_processes) goto finish;
+
+ /* use table for -a a d g x */
+ if((simple_select || !selection_list))
+ if(table_accept(buf)) goto finish;
+
+ /* search lists */
+ if(proc_was_listed(buf)) goto finish;
+
+ /* fail, fall through to loose ends */
+ accepted_proc = 0;
+
+ /* do r N */
+finish:
+ if(running_only && !running(buf)) accepted_proc = 0;
+ if(negate_selection) return !accepted_proc;
+ return accepted_proc;
+}
--- /dev/null
+/*
+ * Copyright 1998-2004 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "common.h"
+
+static sf_node *sf_list = NULL; /* deferred sorting and formatting */
+static int broken; /* use gross Unix98 parsing? */
+static int have_gnu_sort = 0; /* if true, "O" must be format */
+static int already_parsed_sort = 0; /* redundantly set in & out of fn */
+static int already_parsed_format = 0;
+
+
+/**************** Parse single format specifier *******************/
+static format_node *do_one_spec(const char *spec, const char *override){
+ const format_struct *fs;
+ const macro_struct *ms;
+
+ fs = search_format_array(spec);
+ if(fs){
+ int w1, w2;
+ format_node *thisnode;
+ thisnode = malloc(sizeof(format_node));
+ if(fs->flags & CF_PIDMAX){
+ w1 = (int)get_pid_digits();
+ w2 = strlen(fs->head);
+ if(w2>w1) w1=w2; // FIXME w/ separate header/body column sizing
+ }else{
+ w1 = fs->width;
+ }
+ if(override){
+ w2 = strlen(override);
+ thisnode->width = (w1>w2)?w1:w2;
+ thisnode->name = malloc(strlen(override)+1);
+ strcpy(thisnode->name, override);
+ }else{
+ thisnode->width = w1;
+ thisnode->name = malloc(strlen(fs->head)+1);
+ strcpy(thisnode->name, fs->head);
+ }
+ thisnode->pr = fs->pr;
+ thisnode->need = fs->need;
+ thisnode->vendor = fs->vendor;
+ thisnode->flags = fs->flags;
+ thisnode->next = NULL;
+ return thisnode;
+ }
+
+ /* That failed, so try it as a macro. */
+ ms = search_macro_array(spec);
+ if(ms){
+ format_node *list = NULL;
+ format_node *newnode;
+ const char *walk;
+ int dist;
+ char buf[16]; /* trust strings will be short (from above, not user) */
+ walk = ms->head;
+ while(*walk){
+ dist = strcspn(walk, ", ");
+ strncpy(buf,walk,dist);
+ buf[dist] = '\0';
+ newnode = do_one_spec(buf,override); /* call self, assume success */
+ newnode->next = list;
+ list = newnode;
+ walk += dist;
+ if(*walk) walk++;
+ }
+ return list;
+ }
+ return NULL; /* bad, spec not found */
+}
+
+
+/************ must wrap user format in default *************/
+static void O_wrap(sf_node *sfn, int otype){
+ format_node *fnode;
+ format_node *endp;
+ const char *trailer;
+
+ trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
+
+ fnode = do_one_spec("pid",NULL);
+ if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
+ endp = sfn->f_cooked; while(endp->next) endp = endp->next; /* find end */
+ endp->next = fnode;
+
+ fnode = do_one_spec(trailer,NULL);
+ if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
+ endp = fnode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->f_cooked;
+ sfn->f_cooked = fnode;
+}
+
+/******************************************************************
+ * Used to parse option AIX field descriptors.
+ * Put each completed format_node onto the list starting at ->f_cooked
+ */
+static const char *aix_format_parse(sf_node *sfn){
+ char *buf; /* temp copy of arg to hack on */
+ char *walk;
+ int items;
+
+ /*** sanity check and count items ***/
+ items = 0;
+ walk = sfn->sf;
+ /* state machine */ {
+ int c;
+ initial:
+ c = *walk++;
+ if(c=='%') goto get_desc;
+ if(!c) goto looks_ok;
+ /* get_text: */
+ items++;
+ get_more_text:
+ c = *walk++;
+ if(c=='%') goto get_desc;
+ if(c) goto get_more_text;
+ goto looks_ok;
+ get_desc:
+ items++;
+ c = *walk++;
+ if(c) goto initial;
+ return "Improper AIX field descriptor.";
+ looks_ok:
+ ;
+ }
+
+ /*** sanity check passed ***/
+ buf = malloc(strlen(sfn->sf)+1);
+ strcpy(buf, sfn->sf);
+ walk = sfn->sf;
+
+ while(items--){
+ format_node *fnode; /* newly allocated */
+ format_node *endp; /* for list manipulation */
+
+ if(*walk == '%'){
+ const aix_struct *aix;
+ walk++;
+ if(*walk == '%') goto double_percent;
+ aix = search_aix_array(*walk);
+ walk++;
+ if(!aix){
+ free(buf);
+ return "Unknown AIX field descriptor.";
+ }
+ fnode = do_one_spec(aix->spec, aix->head);
+ if(!fnode){
+ free(buf);
+ return "AIX field descriptor processing bug.";
+ }
+ } else {
+ int len;
+ len = strcspn(walk, "%");
+ memcpy(buf,walk,len);
+ if(0){
+double_percent:
+ len = 1;
+ buf[0] = '%';
+ }
+ buf[len] = '\0';
+ walk += len;
+ fnode = malloc(sizeof(format_node));
+ fnode->width = len;
+ fnode->name = malloc(len+1);
+ strcpy(fnode->name, buf);
+ fnode->pr = NULL; /* checked for */
+ fnode->need = 0;
+ fnode->vendor = AIX;
+ fnode->flags = CF_PRINT_EVERY_TIME;
+ fnode->next = NULL;
+ }
+
+ endp = fnode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->f_cooked;
+ sfn->f_cooked = fnode;
+ }
+ free(buf);
+ already_parsed_format = 1;
+ return NULL;
+}
+
+/***************************************************************
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ * Put each completed format_node onto the list starting at ->f_cooked
+ */
+static const char *format_parse(sf_node *sfn){
+ char *buf; /* temp copy of arg to hack on */
+ char *sep_loc; /* separator location: " \t,\n" */
+ char *walk;
+ const char *err; /* error code that could or did happen */
+ format_node *fnode;
+ int items;
+ int need_item;
+ static char errbuf[80]; /* for variable-text error message */
+
+ /*** prepare to operate ***/
+ buf = malloc(strlen(sfn->sf)+1);
+ strcpy(buf, sfn->sf);
+
+ /*** sanity check and count items ***/
+ need_item = 1; /* true */
+ items = 0;
+ walk = buf;
+ do{
+ switch(*walk){
+ case ' ': case ',': case '\t': case '\n': case '\0':
+ /* Linux extension: allow \t and \n as delimiters */
+ if(need_item){
+ free(buf);
+ goto improper;
+ }
+ need_item=1;
+ break;
+ case '=':
+ if(broken) goto out;
+ /* fall through */
+ default:
+ if(need_item) items++;
+ need_item=0;
+ }
+ } while (*++walk);
+out:
+ if(!items){
+ free(buf);
+ goto empty;
+ }
+#ifdef STRICT_LIST
+ if(need_item){ /* can't have trailing deliminator */
+ free(buf);
+ goto improper;
+ }
+#else
+ if(need_item){ /* allow 1 trailing deliminator */
+ *--walk='\0'; /* remove the trailing deliminator */
+ }
+#endif
+ /*** actually parse the list ***/
+ walk = buf;
+ while(items--){
+ format_node *endp;
+ char *equal_loc;
+ char *colon_loc;
+ sep_loc = strpbrk(walk," ,\t\n");
+ /* if items left, then sep_loc is not in header override */
+ if(items && sep_loc) *sep_loc = '\0';
+ equal_loc = strpbrk(walk,"=");
+ if(equal_loc){ /* if header override */
+ *equal_loc = '\0';
+ equal_loc++;
+ }
+ colon_loc = strpbrk(walk,":");
+ if(colon_loc){ /* if width override */
+ *colon_loc = '\0';
+ colon_loc++;
+ if(strspn(colon_loc,"0123456789") != strlen(colon_loc) || *colon_loc=='0' || !*colon_loc){
+ free(buf);
+ goto badwidth;
+ }
+ }
+ fnode = do_one_spec(walk,equal_loc);
+ if(!fnode){
+ if(!*errbuf){ /* if didn't already create an error string */
+ snprintf(
+ errbuf,
+ sizeof(errbuf),
+ "Unknown user-defined format specifier \"%s\".",
+ walk
+ );
+ }
+ free(buf);
+ goto unknown;
+ }
+ if(colon_loc){
+ if(fnode->next){
+ free(buf);
+ goto notmacro;
+ }
+ // FIXME: enforce signal width to 8, 9, or 16 (grep: SIGNAL wide_signals)
+ fnode->width = atoi(colon_loc); // already verified to be a number
+ }
+ endp = fnode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->f_cooked;
+ sfn->f_cooked = fnode;
+ walk = sep_loc + 1; /* point to next item, if any */
+ }
+ free(buf);
+ already_parsed_format = 1;
+ return NULL;
+
+ /* errors may cause a retry looking for AIX format codes */
+ if(0) unknown: err=errbuf;
+ if(0) empty: err="Empty format list.";
+ if(0) improper: err="Improper format list.";
+ if(0) badwidth: err="Column widths must be unsigned decimal numbers.";
+ if(0) notmacro: err="Can't set width for a macro (multi-column) format specifier.";
+ if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn);
+ return err;
+}
+
+/**************** Parse single sort specifier *******************/
+static sort_node *do_one_sort_spec(const char *spec){
+ const format_struct *fs;
+ int reverse = 0;
+ if(*spec == '-'){
+ reverse = 1;
+ spec++;
+ } else if(*spec == '+'){
+ spec++;
+ }
+ fs = search_format_array(spec);
+ if(fs){
+ sort_node *thisnode;
+ thisnode = malloc(sizeof(sort_node));
+ thisnode->sr = fs->sr;
+ thisnode->need = fs->need;
+ thisnode->reverse = reverse;
+ thisnode->next = NULL;
+ return thisnode;
+ }
+ return NULL; /* bad, spec not found */
+}
+
+
+/**************************************************************
+ * Used to parse long sorting options.
+ * Put each completed sort_node onto the list starting at ->s_cooked
+ */
+static const char *long_sort_parse(sf_node *sfn){
+ char *buf; /* temp copy of arg to hack on */
+ char *sep_loc; /* separator location: " \t,\n" */
+ char *walk;
+ sort_node *snode;
+ int items;
+ int need_item;
+
+ /*** prepare to operate ***/
+ buf = malloc(strlen(sfn->sf)+1);
+ strcpy(buf, sfn->sf);
+
+ /*** sanity check and count items ***/
+ need_item = 1; /* true */
+ items = 0;
+ walk = buf;
+ do{
+ switch(*walk){
+ case ' ': case ',': case '\t': case '\n': case '\0':
+ if(need_item){
+ free(buf);
+ return "Improper sort list";
+ }
+ need_item=1;
+ break;
+ default:
+ if(need_item) items++;
+ need_item=0;
+ }
+ } while (*++walk);
+ if(!items){
+ free(buf);
+ return "Empty sort list.";
+ }
+#ifdef STRICT_LIST
+ if(need_item){ /* can't have trailing deliminator */
+ free(buf);
+ return "Improper sort list.";
+ }
+#else
+ if(need_item){ /* allow 1 trailing deliminator */
+ *--walk='\0'; /* remove the trailing deliminator */
+ }
+#endif
+ /*** actually parse the list ***/
+ walk = buf;
+ while(items--){
+ sort_node *endp;
+ sep_loc = strpbrk(walk," ,\t\n");
+ if(sep_loc) *sep_loc = '\0';
+ snode = do_one_sort_spec(walk);
+ if(!snode){
+ free(buf);
+ return "Unknown sort specifier.";
+ }
+ endp = snode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->s_cooked;
+ sfn->s_cooked = snode;
+ walk = sep_loc + 1; /* point to next item, if any */
+ }
+ free(buf);
+ already_parsed_sort = 1;
+ return NULL;
+}
+
+
+
+
+
+
+/************ pre-parse short sorting option *************/
+/* Errors _must_ be detected so that the "O" option can try to
+ * reparse as formatting codes.
+ */
+static const char *verify_short_sort(const char *arg){
+ const char all[] = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
+ char checkoff[256];
+ int i;
+ const char *walk;
+ int tmp;
+ if(strspn(arg,all) != strlen(arg)) return "Bad sorting code.";
+ for(i=256; i--;) checkoff[i] = 0;
+ walk = arg;
+ for(;;){
+ tmp = *walk;
+ switch(tmp){
+ case '\0':
+ return NULL; /* looks good */
+ case '+':
+ case '-':
+ tmp = *(walk+1);
+ if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code.";
+ break;
+ case 'P':
+ if(forest_type) return "PPID sort and forest output conflict.";
+ /* fall through */
+ default:
+ if(checkoff[tmp]) return "Bad sorting code."; /* repeated */
+ /* ought to check against already accepted sort options */
+ checkoff[tmp] = 1;
+ break;
+ }
+ walk++;
+ }
+}
+
+
+
+/************ parse short sorting option *************/
+static const char *short_sort_parse(sf_node *sfn){
+ int direction = 0;
+ const char *walk;
+ int tmp;
+ sort_node *snode;
+ sort_node *endp;
+ const struct shortsort_struct *ss;
+ walk = sfn->sf;
+ for(;;){
+ tmp = *walk;
+ switch(tmp){
+ case '\0':
+ already_parsed_sort = 1;
+ return NULL;
+ case '+':
+ direction = 0;
+ break;
+ case '-':
+ direction = 1;
+ break;
+ default:
+ ss = search_shortsort_array(tmp);
+ if(!ss) return "Unknown sort specifier.";
+ snode = do_one_sort_spec(ss->spec);
+ if(!snode) return "Unknown sort specifier.";
+ snode->reverse = direction;
+ endp = snode; while(endp->next) endp = endp->next; /* find end */
+ endp->next = sfn->s_cooked;
+ sfn->s_cooked = snode;
+ direction = 0;
+ break;
+ }
+ walk++;
+ }
+}
+
+/******************* high-level below here *********************/
+
+
+/*
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ * Recursion is to preserve original order.
+ */
+static const char *parse_O_option(sf_node *sfn){
+ const char *err; /* error code that could or did happen */
+
+ if(sfn->next){
+ err = parse_O_option(sfn->next);
+ if(err) return err;
+ }
+
+ switch(sfn->sf_code){
+ case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/
+ err = format_parse(sfn);
+ if(!err) already_parsed_format = 1;
+ break;
+ case SF_U_O: /*** format ***/
+ /* Can have -l -f f u... set already_parsed_format like DEC does */
+ if(already_parsed_format) return "option -O can not follow other format options.";
+ err = format_parse(sfn);
+ if(err) return err;
+ already_parsed_format = 1;
+ O_wrap(sfn,'u'); /* must wrap user format in default */
+ break;
+ case SF_B_O: /*** both ***/
+ if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options.";
+ else err = verify_short_sort(sfn->sf);
+ if(!err){ /* success as sorting code */
+ short_sort_parse(sfn);
+ already_parsed_sort = 1;
+ return NULL;
+ }
+ if(already_parsed_format){
+ err = "option O is neither first format nor sort order.";
+ break;
+ }
+ if(!format_parse(sfn)){ /* if success as format code */
+ already_parsed_format = 1;
+ O_wrap(sfn,'b'); /* must wrap user format in default */
+ return NULL;
+ }
+ break;
+ case SF_G_sort: case SF_B_m: /*** sort ***/
+ if(already_parsed_sort) err = "Multiple sort options.";
+ else err = long_sort_parse(sfn);
+ already_parsed_sort = 1;
+ break;
+ default: /*** junk ***/
+ return "Bug: parse_O_option got weirdness!";
+ }
+ return err; /* could be NULL */
+}
+
+
+/************ Main parser calls this to save lists for later **********/
+/* store data for later and return 1 if arg looks non-standard */
+int defer_sf_option(const char *arg, int source){
+ sf_node *sfn;
+ char buf[16];
+ int dist;
+ const format_struct *fs;
+ int need_item = 1;
+
+ sfn = malloc(sizeof(sf_node));
+ sfn->sf = malloc(strlen(arg)+1);
+ strcpy(sfn->sf, arg);
+ sfn->sf_code = source;
+ sfn->s_cooked = NULL;
+ sfn->f_cooked = NULL;
+ sfn->next = sf_list;
+ sf_list = sfn;
+
+ if(source == SF_G_sort) have_gnu_sort = 1;
+
+ /* Now try to find an excuse to ignore broken Unix98 parsing. */
+ if(source != SF_U_o) return 1; /* Wonderful! Already non-Unix98. */
+ do{
+ switch(*arg){
+ case ' ': case ',': case '\0': /* no \t\n\r support in Unix98 */
+ if(need_item) return 1; /* something wrong */
+ need_item=1;
+ break;
+ case '=':
+ if(need_item) return 1; /* something wrong */
+ return 0; /* broken Unix98 parsing is required */
+ default:
+ if(!need_item) break;
+ need_item=0;
+ dist = strcspn(arg,", =");
+ if(dist>15) return 1; /* something wrong, sort maybe? */
+ strncpy(buf,arg,dist); /* no '\0' on end */
+ buf[dist] = '\0'; /* fix that problem */
+ fs = search_format_array(buf);
+ if(!fs) return 1; /* invalid spec, macro or sort maybe? */
+ if(fs->vendor) return 1; /* Wonderful! Legal non-Unix98 spec. */
+ }
+ } while (*++arg);
+
+ return 0; /* boring, Unix98 is no change */
+}
+
+/***** Since ps is not long-lived, the memory leak can be ignored. ******/
+void reset_sortformat(void){
+ sf_list = NULL; /* deferred sorting and formatting */
+ format_list = NULL; /* digested formatting options */
+ sort_list = NULL; /* digested sorting options (redundant?) */
+ have_gnu_sort = 0;
+ already_parsed_sort = 0;
+ already_parsed_format = 0;
+}
+
+
+/***** Search format_list for findme, then insert putme after findme. ****/
+static int fmt_add_after(const char *findme, format_node *putme){
+ format_node *walk;
+ if(!strcmp(format_list->name, findme)){
+ putme->next = format_list->next;
+ format_list->next = putme;
+ return 1; /* success */
+ }
+ walk = format_list;
+ while(walk->next){
+ if(!strcmp(walk->next->name, findme)){
+ putme->next = walk->next->next;
+ walk->next->next = putme;
+ return 1; /* success */
+ }
+ walk = walk->next;
+ }
+ return 0; /* fail */
+}
+
+/******* Search format_list for findme, then delete it. ********/
+static int fmt_delete(const char *findme){
+ format_node *walk;
+ format_node *old;
+ if(!strcmp(format_list->name, findme)){
+ old = format_list;
+ format_list = format_list->next;
+ free(old);
+ return 1; /* success */
+ }
+ walk = format_list;
+ while(walk->next){
+ if(!strcmp(walk->next->name, findme)){
+ old = walk->next;
+ walk->next = walk->next->next;
+ free(old);
+ return 1; /* success */
+ }
+ walk = walk->next;
+ }
+ return 0; /* fail */
+}
+
+
+/************ Build a SysV format backwards. ***********/
+#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn)
+static const char *generate_sysv_list(void){
+ format_node *fn;
+ if((format_modifiers & FM_y) && !(format_flags & FF_Ul))
+ return "Modifier -y without format -l makes no sense.";
+ if(prefer_bsd_defaults){
+ if(format_flags) PUSH("cmd");
+ else PUSH("args");
+ PUSH("bsdtime");
+ if(!(format_flags & FF_Ul)) PUSH("stat");
+ }else{
+ if(format_flags & FF_Uf) PUSH("cmd");
+ else PUSH("ucmd");
+ PUSH("time");
+ }
+ PUSH("tname"); /* Unix98 says "TTY" here, yet "tty" produces "TT". */
+ if(format_flags & FF_Uf) PUSH("stime");
+ /* avoid duplicate columns from -FP and -Fly */
+ if(format_modifiers & FM_F){
+ /* if -FP take the Sun-style column instead (sorry about "sgi_p") */
+ if(!(format_modifiers & FM_P)) PUSH("psr"); /* should be ENG */
+ /* if -Fly take the ADDR-replacement RSS instead */
+ if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss");
+ }
+ if(format_flags & FF_Ul){
+ PUSH("wchan");
+ }
+ /* since FM_y adds RSS anyway, don't do this hack when that is true */
+ if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){
+ if(personality & PER_IRIX_l){ /* add "rss" then ':' here */
+ PUSH("sgi_rss");
+ fn = malloc(sizeof(format_node));
+ fn->width = 1;
+ fn->name = malloc(2);
+ strcpy(fn->name, ":");
+ fn->pr = NULL; /* checked for */
+ fn->need = 0;
+ fn->vendor = AIX; /* yes, for SGI weirdness */
+ fn->flags = CF_PRINT_EVERY_TIME;
+ fn->next = format_list;
+ format_list=fn;
+ }
+ }
+ if((format_modifiers & FM_F) || (format_flags & FF_Ul)){
+ PUSH("sz");
+ }
+ if(format_flags & FF_Ul){
+ if(format_modifiers & FM_y) PUSH("rss");
+ else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p");
+ else PUSH("addr_1");
+ }
+ if(format_modifiers & FM_c){
+ PUSH("pri"); PUSH("class");
+ }else if(format_flags & FF_Ul){
+ PUSH("ni");
+ if(personality & PER_IRIX_l) PUSH("priority");
+ else /* is this good? */ PUSH("opri");
+ }
+
+ // FIXME TODO XXX -- this is a serious problem
+ // These somehow got flipped around.
+ // The bug is in procps-3.1.1, procps-990211, prior too?
+ if((thread_flags & TF_U_L) && (format_flags & FF_Uf)) PUSH("nlwp");
+ if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
+
+ if(format_modifiers & FM_P) PUSH("psr");
+ if(thread_flags & TF_U_L) PUSH("lwp");
+ if(format_modifiers & FM_j){
+ PUSH("sid");
+ PUSH("pgid");
+ }
+ if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
+ if(thread_flags & TF_U_T) PUSH("spid");
+ PUSH("pid");
+ if(format_flags & FF_Uf){
+ if(personality & PER_SANE_USER) PUSH("user");
+ else PUSH("uid_hack");
+ }else if(format_flags & FF_Ul){
+ PUSH("uid");
+ }
+ if(format_flags & FF_Ul){
+ PUSH("s");
+ if(!(format_modifiers & FM_y)) PUSH("f");
+ }
+ if(format_modifiers & FM_M){
+ PUSH("label"); /* Mandatory Access Control */
+ }
+ return NULL;
+}
+
+
+/**************************************************************************
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ */
+const char *process_sf_options(int localbroken){
+ sf_node *sf_walk;
+
+ if(personality & PER_BROKEN_o) localbroken = 1;
+ if(personality & PER_GOOD_o) localbroken = 0;
+ broken = localbroken;
+ if(sf_list){
+ const char *err;
+ err = parse_O_option(sf_list);
+ if(err) return err;
+ }
+
+ if(format_list) printf("Bug: must reset the list first!\n");
+
+ /* merge formatting info of sf_list into format_list here */
+ sf_walk = sf_list;
+ while(sf_walk){
+ format_node *fmt_walk;
+ fmt_walk = sf_walk->f_cooked;
+ sf_walk->f_cooked = NULL;
+ while(fmt_walk){ /* put any nodes onto format_list in opposite way */
+ format_node *travler;
+ travler = fmt_walk;
+ fmt_walk = fmt_walk->next;
+ travler->next = format_list;
+ format_list = travler;
+ }
+ sf_walk = sf_walk->next;
+ }
+
+ /* merge sorting info of sf_list into sort_list here */
+ sf_walk = sf_list;
+ while(sf_walk){
+ sort_node *srt_walk;
+ srt_walk = sf_walk->s_cooked;
+ sf_walk->s_cooked = NULL;
+ while(srt_walk){ /* put any nodes onto sort_list in opposite way */
+ sort_node *travler;
+ travler = srt_walk;
+ srt_walk = srt_walk->next;
+ travler->next = sort_list;
+ sort_list = travler;
+ }
+ sf_walk = sf_walk->next;
+ }
+
+ // Get somebody to explain how -L/-T is supposed to interact
+ // with sorting. Do the threads remain grouped, with sorting
+ // by process, or do the threads get sorted by themselves?
+ if(sort_list && (thread_flags&TF_no_sort)){
+ return "Tell procps-feedback@lists.sf.net what you expected.";
+ }
+
+ // If nothing else, try to use $PS_FORMAT before the default.
+ if(!format_flags && !format_modifiers && !format_list){
+ char *tmp;
+ tmp = getenv("PS_FORMAT"); /* user override kills default */
+ if(tmp && *tmp){
+ const char *err;
+ sf_node sfn;
+ if(thread_flags&TF_must_use) return "Tell procps-feedback@sf.net what you want. (-L/-T, -m/m/H, and $PS_FORMAT)";
+ sfn.sf = tmp;
+ sfn.f_cooked = NULL;
+ err = format_parse(&sfn);
+ if(!err){
+ format_node *fmt_walk;
+ fmt_walk = sfn.f_cooked;
+ while(fmt_walk){ /* put any nodes onto format_list in opposite way */
+ format_node *travler;
+ travler = fmt_walk;
+ fmt_walk = fmt_walk->next;
+ travler->next = format_list;
+ format_list = travler;
+ }
+ return NULL;
+ }
+ // FIXME: prove that this won't be hit on valid bogus-BSD options
+ fprintf(stderr, "Warning: $PS_FORMAT ignored. (%s)\n", err);
+ }
+ }
+
+ if(format_list){
+ if(format_flags) return "Conflicting format options.";
+ if(format_modifiers) return "Can't use output modifiers with user-defined output";
+ if(thread_flags&TF_must_use) return "-L/-T with H/m/-m and -o/-O/o/O is nonsense";
+ return NULL;
+ }
+
+ do{
+ const char *spec;
+ switch(format_flags){
+
+ default: return "Conflicting format options.";
+
+ /* These can be NULL, which enables SysV list generation code. */
+ case 0: spec=NULL; break;
+ case FF_Uf | FF_Ul: spec=sysv_fl_format; break;
+ case FF_Uf: spec=sysv_f_format; break;
+ case FF_Ul: spec=sysv_l_format; break;
+
+ /* These are NOT REACHED for normal -j processing. */
+ case FF_Uj: spec=sysv_j_format; break; /* Debian & Digital */
+ case FF_Uj | FF_Ul: spec="RD_lj"; break; /* Debian */
+ case FF_Uj | FF_Uf: spec="RD_fj"; break; /* Debian */
+
+ /* These are true BSD options. */
+ case FF_Bj: spec=bsd_j_format; break;
+ case FF_Bl: spec=bsd_l_format; break;
+ case FF_Bs: spec=bsd_s_format; break;
+ case FF_Bu: spec=bsd_u_format; break;
+ case FF_Bv: spec=bsd_v_format; break;
+
+ /* These are old Linux options. Option m is overloaded. */
+ case FF_LX: spec="OL_X"; break;
+ case FF_Lm: spec="OL_m"; break;
+
+ /* This is the sole FLASK security option. */
+ case FF_Fc: spec="FLASK_context"; break;
+
+ } /* end switch(format_flags) */
+
+ // not just for case 0, since sysv_l_format and such may be NULL
+ if(!spec) return generate_sysv_list();
+
+ do{
+ format_node *fmt_walk;
+ fmt_walk = do_one_spec(spec, NULL); /* use override "" for no headers */
+ while(fmt_walk){ /* put any nodes onto format_list in opposite way */
+ format_node *travler;
+ travler = fmt_walk;
+ fmt_walk = fmt_walk->next;
+ travler->next = format_list;
+ format_list = travler;
+ }
+ }while(0);
+ }while(0);
+
+ do{
+ format_node *fn;
+ if(format_modifiers & FM_j){
+ fn = do_one_spec("pgid", NULL);
+ if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
+ return "Internal error, no PID or PPID for -j option.";
+ fn = do_one_spec("sid", NULL);
+ if(!fmt_add_after("PGID", fn)) return "Lost my PGID!";
+ }
+ if(format_modifiers & FM_y){
+ /* TODO: check for failure to do something, and complain if so */
+ fmt_delete("F");
+ fn = do_one_spec("rss", NULL);
+ if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
+ }
+ if(format_modifiers & FM_c){
+ fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C");
+ fmt_delete("NI");
+ fn = do_one_spec("class", NULL);
+ if(!fmt_add_after("PRI", fn))
+ return "Internal error, no PRI for -c option.";
+ fmt_delete("PRI"); /* we want a different one */
+ fn = do_one_spec("pri", NULL);
+ if(!fmt_add_after("CLS", fn)) return "Lost my CLS!";
+ }
+ if(thread_flags & TF_U_T){
+ fn = do_one_spec("spid", NULL);
+ if(!fmt_add_after("PID", fn) && (thread_flags&TF_must_use))
+ return "-T with H/-m/m but no PID for SPID to follow";
+ }
+ if(thread_flags & TF_U_L){
+ fn = do_one_spec("lwp", NULL);
+ if(fmt_add_after("SID", fn)) goto did_lwp;
+ if(fmt_add_after("SESS", fn)) goto did_lwp;
+ if(fmt_add_after("PGID", fn)) goto did_lwp;
+ if(fmt_add_after("PGRP", fn)) goto did_lwp;
+ if(fmt_add_after("PPID", fn)) goto did_lwp;
+ if(fmt_add_after("PID", fn)) goto did_lwp;
+ if(thread_flags&TF_must_use)
+ return "-L with H/-m/m but no PID/PGID/SID/SESS for NLWP to follow";
+did_lwp:
+ fn = do_one_spec("nlwp", NULL);
+ fmt_add_after("%CPU", fn);
+ }
+ if(format_modifiers & FM_M){ // Mandatory Access Control, IRIX style
+ fn = do_one_spec("label", NULL);
+ fn->next=format_list;
+ format_list=fn;
+ }
+ /* Do personality-specific translations not covered by format_flags.
+ * Generally, these only get hit when personality overrides unix output.
+ * That (mostly?) means the Digital and Debian personalities.
+ */
+ if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){
+ fn = do_one_spec("sgi_p", NULL);
+ if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
+ }
+ if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){
+ fn = do_one_spec("user", NULL);
+ if(fmt_add_after("UID", fn)) fmt_delete("UID");
+ }
+ }while(0);
+
+ return NULL;
+}
+
--- /dev/null
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for pwdx
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Copyright 2004 Nicholas Miell.
+.\" Based on the pmap(1) man page by Albert Cahalan.
+.\"
+.TH PWDX 1 "September 8, 2004" "Linux" "Linux User's Manual"
+.SH NAME
+pwdx \- report current working directory of a process
+
+.SH SYNOPSIS
+.nf
+pwdx pids...
+pwdx -V
+.fi
+
+.SH DESCRIPTION
+The pwdx command reports the current working directory of a process or
+processes.
+
+.SH "GENERAL OPTIONS"
+.TS
+l l l.
+-V show version Displays version of program.
+.TE
+
+.SH "SEE ALSO"
+ps(1) pgrep(1)
+
+.SH STANDARDS
+No standards apply, but pwdx looks an awful lot like a SunOS command.
+
+.SH AUTHOR
+Nicholas Miell <nmiell@gmail.com> wrote pwdx in 2004. Please send bug
+reports to <procps-feedback@lists.sf.net>.
--- /dev/null
+// Copyright 2004 Nicholas Miell
+//
+// This file may be used subject to the terms and conditions of the
+// GNU Library General Public License Version 2 as published by the
+// Free Software Foundation.This program is distributed in the hope
+// that it will be useful, but WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE. See the GNU Library General Public License for more
+// details.
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "proc/version.h"
+
+static void die(const char *msg) NORETURN;
+static void die(const char *msg)
+{
+ fputs(msg, stderr);
+ exit(1);
+}
+
+static void version(void) NORETURN;
+static void version(void)
+{
+ printf("pwdx (%s)\n", procps_version);
+ exit(0);
+}
+
+int main(int argc, char* argv[])
+{
+ char buf[PATH_MAX+1];
+ regex_t re;
+ int i;
+
+ if (argc < 2)
+ die("Usage: pwdx pid...\n");
+
+ // Allowed on the command line:
+ //
+ // --version
+ // -V
+ // /proc/nnnn
+ // nnnn
+ //
+ // where nnnn is any number that doesn't begin with 0.
+ //
+ // If --version or -V are present, further arguments are ignored
+ // completely.
+
+ regcomp(&re, "^((/proc/+)?[1-9][0-9]*|-V|--version)$",
+ REG_EXTENDED|REG_NOSUB);
+
+ for (i = 1; i < argc; i++) {
+ if (regexec(&re, argv[i], 0, NULL, 0) != 0) {
+ snprintf(buf, sizeof buf, "pwdx: invalid process id: %s\n", argv[i]);
+ die(buf);
+ }
+ if (!strcmp("-V", argv[i]) || !strcmp("--version", argv[i]))
+ version();
+ }
+
+ regfree(&re);
+
+ for (i = 1; i < argc; i++) {
+ char * s = buf;
+ int len;
+
+ // At this point, all arguments are in the form /proc/nnnn
+ // or nnnn, so a simple check based on the first char is
+ // possible
+ if (argv[i][0] != '/')
+ snprintf(buf, sizeof buf, "/proc/%s/cwd", argv[i]);
+ else
+ snprintf(buf, sizeof buf, "%s/cwd", argv[i]);
+
+ // buf contains /proc/nnnn/cwd symlink name on entry, the
+ // target of that symlink on return
+ if ((len = readlink(buf, buf, PATH_MAX)) < 0) {
+ s = strerror(errno == ENOENT ? ESRCH : errno);
+ } else {
+ buf[len] = 0;
+ }
+
+ printf("%s: %s\n", argv[i], s);
+ }
+
+ return 0;
+}
--- /dev/null
+'\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for skill and snice.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan, converted to a man page by
+.\" Michael K. Johnson
+.\"
+.TH SKILL 1 "March 12, 1999" "Linux" "Linux User's Manual"
+.SH NAME
+skill, snice \- send a signal or report process status
+
+.SH SYNOPSIS
+.nf
+skill [signal to send] [options] process selection criteria
+snice [new priority] [options] process selection criteria
+.fi
+
+.SH DESCRIPTION
+These tools are probably obsolete and unportable. The command
+syntax is poorly defined. Consider using the killall, pkill,
+and pgrep commands instead.
+
+The default signal for skill is TERM. Use -l or -L to list available signals.
+Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
+
+The default priority for snice is +4. (snice +4 ...)
+Priority numbers range from +20 (slowest) to -20 (fastest).
+Negative priority numbers are restricted to administrative users.
+
+.SH "GENERAL OPTIONS"
+.TS
+l l l.
+-f fast mode This is not currently useful.
+-i interactive use T{
+You will be asked to approve each action.
+T}
+-v verbose output T{
+Display information about selected processes.
+T}
+-w warnings enabled This is not currently useful.
+-n no action This only displays the process ID.
+-V show version Displays version of program.
+.TE
+
+.SH "PROCESS SELECTION OPTIONS"
+Selection criteria can be: terminal, user, pid, command.
+The options below may be used to ensure correct interpretation.
+Do not blame Albert for this interesting interface.
+.TS
+l l.
+-t The next argument is a terminal (tty or pty).
+-u The next argument is a username.
+-p The next argument is a process ID number.
+-c The next argument is a command name.
+.TE
+
+.SH SIGNALS
+The signals listed below may be available for use with skill.
+When known, numbers and default behavior are shown.
+.TS
+lB rB lB lB
+lfCW r l l.
+Name Num Action Description
+.TH
+0 0 n/a exit code indicates if a signal may be sent
+ALRM 14 exit
+HUP 1 exit
+INT 2 exit
+KILL 9 exit this signal may not be blocked
+PIPE 13 exit
+POLL exit
+PROF exit
+TERM 15 exit
+USR1 exit
+USR2 exit
+VTALRM exit
+STKFLT exit may not be implemented
+PWR ignore may exit on some systems
+WINCH ignore
+CHLD ignore
+URG ignore
+TSTP stop may interact with the shell
+TTIN stop may interact with the shell
+TTOU stop may interact with the shell
+STOP stop this signal may not be blocked
+CONT restart continue if stopped, otherwise ignore
+ABRT 6 core
+FPE 8 core
+ILL 4 core
+QUIT 3 core
+SEGV 11 core
+TRAP 5 core
+SYS core may not be implemented
+EMT core may not be implemented
+BUS core core dump may fail
+XCPU core core dump may fail
+XFSZ core core dump may fail
+.TE
+
+.SH EXAMPLES
+.TS
+lB lB
+lfCW l.
+Command Description
+.TC
+snice seti crack +7 Slow down seti and crack
+skill -KILL -v /dev/pts/* Kill users on new-style PTY devices
+skill -STOP viro lm davem Stop 3 users
+snice -17 root bash Give priority to root's shell
+.TE
+
+.SH "SEE ALSO"
+killall(1) pkill(1) kill(1) renice(1) nice(1) signal(7) kill(2)
+
+.SH STANDARDS
+No standards apply.
+
+.SH AUTHOR
+Albert Cahalan <albert@users.sf.net> wrote skill and snice in 1999 as a
+replacement for a non-free version, and is the current maintainer of the
+procps collection. Please send bug reports to <procps-feedback@lists.sf.net>.
--- /dev/null
+/*
+ * Copyright 1998-2002 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <fcntl.h>
+#include <pwd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "proc/pwcache.h"
+#include "proc/sig.h"
+#include "proc/devname.h"
+#include "proc/procps.h" /* char *user_from_uid(uid_t uid) */
+#include "proc/version.h" /* procps_version */
+
+static int f_flag, i_flag, v_flag, w_flag, n_flag;
+
+static int tty_count, uid_count, cmd_count, pid_count;
+static int *ttys;
+static uid_t *uids;
+static const char **cmds;
+static int *pids;
+
+#define ENLIST(thing,addme) do{ \
+if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \
+if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \
+thing##s[thing##_count++] = addme; \
+}while(0)
+
+static int my_pid;
+static int saved_argc;
+
+static int sig_or_pri;
+
+static int program;
+#define PROG_GARBAGE 0 /* keep this 0 */
+#define PROG_KILL 1
+#define PROG_SKILL 2
+/* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */
+#define PROG_SNICE 4
+
+
+/********************************************************************/
+
+static void display_kill_version(void){
+ switch(program) {
+ case PROG_KILL:
+ fprintf(stdout, "kill (%s)\n",procps_version);
+ return;
+ case PROG_SKILL:
+ fprintf(stdout, "skill (%s)\n",procps_version);
+ return;
+ case PROG_SNICE:
+ fprintf(stdout, "snice (%s)\n",procps_version);
+ return;
+ default:
+ fprintf(stdout, "unknown (%s)\n",procps_version);
+ return;
+ }
+}
+
+/***** kill or nice a process */
+static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd){
+ int failed;
+ int saved_errno;
+ char dn_buf[1000];
+ dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV);
+ if(i_flag){
+ char buf[8];
+ fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ",
+ (char*)dn_buf,user_from_uid(uid),pid,cmd
+ );
+ if(!fgets(buf,7,stdin)){
+ printf("\n");
+ exit(0);
+ }
+ if(*buf!='y' && *buf!='Y') return;
+ }
+ /* do the actual work */
+ if(program==PROG_SKILL) failed=kill(pid,sig_or_pri);
+ else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri);
+ saved_errno = errno;
+ if(w_flag && failed){
+ fprintf(stderr, "%-8s %-8s %5d %-16.16s ",
+ (char*)dn_buf,user_from_uid(uid),pid,cmd
+ );
+ errno = saved_errno;
+ perror("");
+ return;
+ }
+ if(i_flag) return;
+ if(v_flag){
+ printf("%-8s %-8s %5d %-16.16s\n",
+ (char*)dn_buf,user_from_uid(uid),pid,cmd
+ );
+ return;
+ }
+ if(n_flag){
+ printf("%d\n",pid);
+ return;
+ }
+}
+
+
+/***** check one process */
+static void check_proc(int pid){
+ char buf[128];
+ struct stat statbuf;
+ char *tmp;
+ int tty;
+ int fd;
+ int i;
+ if(pid==my_pid) return;
+ sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */
+ fd = open(buf,O_RDONLY);
+ if(fd==-1){ /* process exited maybe */
+ if(pids && w_flag) printf("WARNING: process %d could not be found.",pid);
+ return;
+ }
+ fstat(fd, &statbuf);
+ if(uids){ /* check the EUID */
+ i=uid_count;
+ while(i--) if(uids[i]==statbuf.st_uid) break;
+ if(i==-1) goto closure;
+ }
+ read(fd,buf,128);
+ buf[127] = '\0';
+ tmp = strrchr(buf, ')');
+ *tmp++ = '\0';
+ i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */
+ tty = atoi(tmp);
+ if(ttys){
+ i=tty_count;
+ while(i--) if(ttys[i]==tty) break;
+ if(i==-1) goto closure;
+ }
+ tmp = strchr(buf, '(') + 1;
+ if(cmds){
+ i=cmd_count;
+ /* fast comparison trick -- useful? */
+ while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break;
+ if(i==-1) goto closure;
+ }
+ /* This is where we kill/nice something. */
+/* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
+ pid, statbuf.st_uid, tty>>8, tty&0xf, tmp
+ );
+*/
+ hurt_proc(tty, statbuf.st_uid, pid, tmp);
+closure:
+ close(fd); /* kill/nice _first_ to avoid PID reuse */
+}
+
+
+/***** debug function */
+#if 0
+static void show_lists(void){
+ int i;
+
+ fprintf(stderr, "%d TTY: ", tty_count);
+ if(ttys){
+ i=tty_count;
+ while(i--){
+ fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n');
+ }
+ }else fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d UID: ", uid_count);
+ if(uids){
+ i=uid_count;
+ while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n');
+ }else fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d PID: ", pid_count);
+ if(pids){
+ i=pid_count;
+ while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n');
+ }else fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d CMD: ", cmd_count);
+ if(cmds){
+ i=cmd_count;
+ while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n');
+ }else fprintf(stderr, "\n");
+}
+#endif
+
+
+/***** iterate over all PIDs */
+static void iterate(void){
+ int pid;
+ DIR *d;
+ struct dirent *de;
+ if(pids){
+ pid = pid_count;
+ while(pid--) check_proc(pids[pid]);
+ return;
+ }
+#if 0
+ /* could setuid() and kill -1 to have the kernel wipe out a user */
+ if(!ttys && !cmds && !pids && !i_flag){
+ }
+#endif
+ d = opendir("/proc");
+ if(!d){
+ perror("/proc");
+ exit(1);
+ }
+ while(( de = readdir(d) )){
+ if(de->d_name[0] > '9') continue;
+ if(de->d_name[0] < '1') continue;
+ pid = atoi(de->d_name);
+ if(pid) check_proc(pid);
+ }
+ closedir (d);
+}
+
+/***** kill help */
+static void kill_usage(void) NORETURN;
+static void kill_usage(void){
+ fprintf(stderr,
+ "Usage:\n"
+ " kill pid ... Send SIGTERM to every process listed.\n"
+ " kill signal pid ... Send a signal to every process listed.\n"
+ " kill -s signal pid ... Send a signal to every process listed.\n"
+ " kill -l List all signal names.\n"
+ " kill -L List all signal names in a nice table.\n"
+ " kill -l signal Convert between signal numbers and names.\n"
+ );
+ exit(1);
+}
+
+/***** kill */
+static void kill_main(int argc, const char *restrict const *restrict argv) NORETURN;
+static void kill_main(int argc, const char *restrict const *restrict argv){
+ const char *sigptr;
+ int signo = SIGTERM;
+ int exitvalue = 0;
+ if(argc<2) kill_usage();
+ if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
+ display_kill_version();
+ exit(0);
+ }
+ if(argv[1][0]!='-'){
+ argv++;
+ argc--;
+ goto no_more_args;
+ }
+
+ /* The -l option prints out signal names. */
+ if(argv[1][1]=='l' && argv[1][2]=='\0'){
+ if(argc==2){
+ unix_print_signals();
+ exit(0);
+ }
+ /* at this point, argc must be 3 or more */
+ if(argc>128 || argv[2][0] == '-') kill_usage();
+ exit(print_given_signals(argc-2, argv+2, 80));
+ }
+
+ /* The -L option prints out signal names in a nice table. */
+ if(argv[1][1]=='L' && argv[1][2]=='\0'){
+ if(argc==2){
+ pretty_print_signals();
+ exit(0);
+ }
+ kill_usage();
+ }
+ if(argv[1][1]=='-' && argv[1][2]=='\0'){
+ argv+=2;
+ argc-=2;
+ goto no_more_args;
+ }
+ if(argv[1][1]=='-') kill_usage(); /* likely --help */
+ // FIXME: "kill -sWINCH $$" not handled
+ if(argv[1][2]=='\0' && (argv[1][1]=='s' || argv[1][1]=='n')){
+ sigptr = argv[2];
+ argv+=3;
+ argc-=3;
+ }else{
+ sigptr = argv[1]+1;
+ argv+=2;
+ argc-=2;
+ }
+ signo = signal_name_to_number(sigptr);
+ if(signo<0){
+ fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr);
+ kill_usage();
+ }
+no_more_args:
+ if(!argc) kill_usage(); /* nothing to kill? */
+ while(argc--){
+ long pid;
+ char *endp;
+ pid = strtol(argv[argc],&endp,10);
+ if(!*endp){
+ if(!kill((pid_t)pid,signo)) continue;
+ // The UNIX standard contradicts itself. If at least one process
+ // is matched for each PID (as if processes could share PID!) and
+ // "the specified signal was successfully processed" (the systcall
+ // returned zero?) for at least one of those processes, then we must
+ // exit with zero. Note that an error might have also occured.
+ // The standard says we return non-zero if an error occurs. Thus if
+ // killing two processes gives 0 for one and EPERM for the other,
+ // we are required to return both zero and non-zero. Quantum kill???
+ exitvalue = 1;
+ continue;
+ }
+ fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]);
+ kill_usage();
+ }
+ exit(exitvalue);
+}
+
+/***** skill/snice help */
+static void skillsnice_usage(void) NORETURN;
+static void skillsnice_usage(void){
+ if(program==PROG_SKILL){
+ fprintf(stderr,
+ "Usage: skill [signal to send] [options] process selection criteria\n"
+ "Example: skill -KILL -v pts/*\n"
+ "\n"
+ "The default signal is TERM. Use -l or -L to list available signals.\n"
+ "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
+ "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"
+ );
+ }else{
+ fprintf(stderr,
+ "Usage: snice [new priority] [options] process selection criteria\n"
+ "Example: snice netscape crack +7\n"
+ "\n"
+ "The default priority is +4. (snice +4 ...)\n"
+ "Priority numbers range from +20 (slowest) to -20 (fastest).\n"
+ "Negative priority numbers are restricted to administrative users.\n"
+ );
+ }
+ fprintf(stderr,
+ "\n"
+ "General options:\n"
+ "-f fast mode This is not currently useful.\n"
+ "-i interactive use You will be asked to approve each action.\n"
+ "-v verbose output Display information about selected processes.\n"
+ "-w warnings enabled This is not currently useful.\n"
+ "-n no action This only displays the process ID.\n"
+ "\n"
+ "Selection criteria can be: terminal, user, pid, command.\n"
+ "The options below may be used to ensure correct interpretation.\n"
+ "-t The next argument is a terminal (tty or pty).\n"
+ "-u The next argument is a username.\n"
+ "-p The next argument is a process ID number.\n"
+ "-c The next argument is a command name.\n"
+ );
+ exit(1);
+}
+
+#if 0
+static void _skillsnice_usage(int line){
+ fprintf(stderr,"Something at line %d.\n", line);
+ skillsnice_usage();
+}
+#define skillsnice_usage() _skillsnice_usage(__LINE__)
+#endif
+
+#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL)
+
+/***** common skill/snice argument parsing code */
+#define NO_PRI_VAL ((int)0xdeafbeef)
+static void skillsnice_parse(int argc, const char *restrict const *restrict argv){
+ int signo = -1;
+ int prino = NO_PRI_VAL;
+ int force = 0;
+ int num_found = 0;
+ const char *restrict argptr;
+ if(argc<2) skillsnice_usage();
+ if(argc==2 && argv[1][0]=='-'){
+ if(!strcmp(argv[1],"-L")){
+ pretty_print_signals();
+ exit(0);
+ }
+ if(!strcmp(argv[1],"-l")){
+ unix_print_signals();
+ exit(0);
+ }
+ if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
+ display_kill_version();
+ exit(0);
+ }
+ skillsnice_usage();
+ }
+ NEXTARG;
+ /* Time for serious parsing. What does "skill -int 123 456" mean? */
+ while(argc){
+ if(force && !num_found){ /* if forced, _must_ find something */
+ fprintf(stderr,"ERROR: -%c used with bad data.\n", force);
+ skillsnice_usage();
+ }
+ force = 0;
+ if(program==PROG_SKILL && signo<0 && *argptr=='-'){
+ signo = signal_name_to_number(argptr+1);
+ if(signo>=0){ /* found a signal */
+ if(!NEXTARG) break;
+ continue;
+ }
+ }
+ if(program==PROG_SNICE && prino==NO_PRI_VAL
+ && (*argptr=='+' || *argptr=='-') && argptr[1]){
+ long val;
+ char *endp;
+ val = strtol(argptr,&endp,10);
+ if(!*endp && val<=999 && val>=-999){
+ prino=val;
+ if(!NEXTARG) break;
+ continue;
+ }
+ }
+ /* If '-' found, collect any flags. (but lone "-" is a tty) */
+ if(*argptr=='-' && argptr[1]){
+ argptr++;
+ do{
+ switch(( force = *argptr++ )){
+ default: skillsnice_usage();
+ case 't':
+ case 'u':
+ case 'p':
+ case 'c':
+ if(!*argptr){ /* nothing left here, *argptr is '\0' */
+ if(!NEXTARG){
+ fprintf(stderr,"ERROR: -%c with nothing after it.\n", force);
+ skillsnice_usage();
+ }
+ }
+ goto selection_collection;
+ case 'f': f_flag++; break;
+ case 'i': i_flag++; break;
+ case 'v': v_flag++; break;
+ case 'w': w_flag++; break;
+ case 'n': n_flag++; break;
+ case 0:
+ NEXTARG;
+ /*
+ * If no more arguments, all the "if(argc)..." tests will fail
+ * and the big loop will exit.
+ */
+ } /* END OF SWITCH */
+ }while(force);
+ } /* END OF IF */
+selection_collection:
+ num_found = 0; /* we should find at least one thing */
+ switch(force){ /* fall through each data type */
+ default: skillsnice_usage();
+ case 0: /* not forced */
+ case 't':
+ if(argc){
+ struct stat sbuf;
+ char path[32];
+ if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */
+ snprintf(path,32,"/dev/%s",argptr);
+ if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){
+ num_found++;
+ ENLIST(tty,sbuf.st_rdev);
+ if(!NEXTARG) break;
+ }else if(!(argptr[1])){ /* if only 1 character */
+ switch(*argptr){
+ default:
+ if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */
+ case '-':
+ case '?':
+ num_found++;
+ ENLIST(tty,0);
+ if(!NEXTARG) break;
+ }
+ }
+ }
+ if(force) continue;
+ case 'u':
+ if(argc){
+ struct passwd *passwd_data;
+ passwd_data = getpwnam(argptr);
+ if(passwd_data){
+ num_found++;
+ ENLIST(uid,passwd_data->pw_uid);
+ if(!NEXTARG) break;
+ }
+ }
+ if(force) continue;
+ case 'p':
+ if(argc && *argptr>='0' && *argptr<='9'){
+ char *endp;
+ int num;
+ num = strtol(argptr, &endp, 0);
+ if(*endp == '\0'){
+ num_found++;
+ ENLIST(pid,num);
+ if(!NEXTARG) break;
+ }
+ }
+ if(force) continue;
+ if(num_found) continue; /* could still be an option */
+ case 'c':
+ if(argc){
+ num_found++;
+ ENLIST(cmd,argptr);
+ if(!NEXTARG) break;
+ }
+ } /* END OF SWITCH */
+ } /* END OF WHILE */
+ /* No more arguments to process. Must sanity check. */
+ if(!tty_count && !uid_count && !cmd_count && !pid_count){
+ fprintf(stderr,"ERROR: no process selection criteria.\n");
+ skillsnice_usage();
+ }
+ if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){
+ fprintf(stderr,"ERROR: general flags may not be repeated.\n");
+ skillsnice_usage();
+ }
+ if(i_flag && (v_flag|f_flag|n_flag)){
+ fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n");
+ skillsnice_usage();
+ }
+ if(v_flag && (i_flag|f_flag)){
+ fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n");
+ skillsnice_usage();
+ }
+ /* OK, set up defaults */
+ if(prino==NO_PRI_VAL) prino=4;
+ if(signo<0) signo=SIGTERM;
+ if(n_flag){
+ program=PROG_SKILL;
+ signo=0; /* harmless */
+ }
+ if(program==PROG_SKILL) sig_or_pri = signo;
+ else sig_or_pri = prino;
+}
+
+/***** main body */
+int main(int argc, const char *argv[]){
+ const char *tmpstr;
+ my_pid = getpid();
+ saved_argc = argc;
+ if(!argc){
+ fprintf(stderr,"ERROR: could not determine own name.\n");
+ exit(1);
+ }
+ tmpstr=strrchr(*argv,'/');
+ if(tmpstr) tmpstr++;
+ if(!tmpstr) tmpstr=*argv;
+ program = PROG_GARBAGE;
+ if(*tmpstr=='s'){
+ setpriority(PRIO_PROCESS,my_pid,-20);
+ if(!strcmp(tmpstr,"snice")) program = PROG_SNICE;
+ if(!strcmp(tmpstr,"skill")) program = PROG_SKILL;
+ }else{
+ if(!strcmp(tmpstr,"kill")) program = PROG_KILL;
+ }
+ switch(program){
+ case PROG_SNICE:
+ case PROG_SKILL:
+ skillsnice_parse(argc, argv);
+/* show_lists(); */
+ iterate(); /* this is it, go get them */
+ break;
+ case PROG_KILL:
+ kill_main(argc, argv);
+ break;
+ default:
+ fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr);
+ }
+ return 0;
+}
+
+
--- /dev/null
+.\" slabtop.1 - manpage for the slabtop(1) utility, part of procps
+.\"
+.\" Copyright (C) 2003 Chris Rivera
+.\" Licensed under the terms of the GNU Library General Public License, v2
+.TH SLABTOP 1 "13 Sep 2003" "Linux" "Linux User's Manual"
+.SH NAME
+slabtop \- display kernel slab cache information in real time
+
+.SH SYNOPSIS
+.BI "slabtop [ " options " ] "
+
+.SH DESCRIPTION
+.BR slabtop (1)
+displays detailed kernel slab cache information in real time. It displays a
+listing of the top caches sorted by one of the listed sort criteria. It also
+displays a statistics header filled with slab layer information.
+
+.SH OPTIONS
+Normal invocation of
+.BR slabtop (1)
+does not require any options. The behavior, however, can be fine-tuned by
+specifying one or more of the following flags:
+.TP
+.B \-\^\-delay=n, \-d n
+Refresh the display every n seconds. By default,
+.BR slabtop (1)
+refreshes the display every three seconds. To exit the program, hit
+.BR q.
+.TP
+.B \-\^\-sort=S, \-s S
+Sort by S, where S is one of the sort criteria.
+.TP
+.B \-\^\-once, \-o
+Display the output once and then exit.
+.TP
+.B \-\^\-version, \-V
+Display version information and exit.
+.TP
+.B \-\^\-help
+Display usage information and exit.
+
+.SH SORT CRITERIA
+The following are valid sort criteria used to sort the individual slab caches
+and thereby determine what are the "top" slab caches to display. The default
+sort criteria is to sort by the number of objects ("o").
+
+The sort criteria can also be changed while slabtop is running by pressing
+the associated character.
+.TP
+.BR a:
+sort by number of active objects
+.TP
+.BR b:
+sort by objects per slab
+.TP
+.BR c:
+sort by cache size
+.TP
+.BR l:
+sort by number of slabs
+.TP
+.BR v
+sort by number of active slabs
+.TP
+.BR n:
+sort by name
+.TP
+.BR o:
+sort by number of objects
+.TP
+.BR p:
+sort by pages per slab
+.TP
+.BR s:
+sort by object size
+.TP
+.BR u:
+sort by cache utilization
+
+.SH COMMANDS
+.BR slabtop (1)
+accepts keyboard commands from the user during use. The following are
+supported. In the case of letters, both cases are accepted.
+
+Each of the valid sort characters are also accepted, to change the sort
+routine. See the section
+.IR "SORT CRITERIA" .
+
+.TP
+.BR <SPACEBAR>
+Refresh the screen.
+.TP
+.BR Q
+Quit the program.
+
+.SH FILES
+.IR /proc/slabinfo " \-\- slab information"
+
+.SH "SEE ALSO"
+.BR free (1),
+.BR ps (1),
+.BR top (1),
+.BR vmstat (8)
+
+.SH NOTES
+Currently,
+.BR slabtop (1)
+requires a 2.4 or later kernel (specifically, a version 1.1 or later
+.IR /proc/slabinfo ).
+Kernel 2.2 should be supported in the future.
+
+The slabtop statistic header is tracking how many bytes of slabs are being used
+and it not a measure of physical memory. The 'Slab' field in the /proc/meminfo
+file is tracking information about used slab physical memory.
+
+.SH AUTHORS
+Written by Chris Rivera and Robert Love.
+
+.BR slabtop (1)
+was inspired by Martin Bligh's perl script,
+.BR vmtop .
+The procps package is maintained by Albert Cahalan <albert@users.sf.net>.
+
+Please send bug reports to <procps-feedback@lists.sf.net>.
--- /dev/null
+/*
+ * slabtop.c - utility to display kernel slab information.
+ *
+ * Chris Rivera <cmrivera@ufl.edu>
+ * Robert Love <rml@tech9.net>
+ *
+ * This program is licensed under the GNU Library General Public License, v2
+ *
+ * Copyright (C) 2003 Chris Rivera
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <ncurses.h>
+#include <termios.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "proc/slab.h"
+#include "proc/version.h"
+
+#define DEF_SORT_FUNC sort_nr_objs
+#define SLAB_STAT_ZERO { nr_objs: 0 }
+
+static unsigned short cols, rows;
+static struct termios saved_tty;
+static long delay = 3;
+static int (*sort_func)(const struct slab_info *, const struct slab_info *);
+
+static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b)
+{
+ struct slab_info sorted_list;
+ struct slab_info *curr = &sorted_list;
+
+ while ((a != NULL) && (b != NULL)) {
+ if (sort_func(a, b)) {
+ curr->next = a;
+ curr = a;
+ a = a->next;
+ } else {
+ curr->next = b;
+ curr = b;
+ b = b->next;
+ }
+ }
+
+ curr->next = (a == NULL) ? b : a;
+ return sorted_list.next;
+}
+
+/*
+ * slabsort - merge sort the slab_info linked list based on sort_func
+ */
+static struct slab_info *slabsort(struct slab_info *list)
+{
+ struct slab_info *a, *b;
+
+ if ((list == NULL) || (list->next == NULL))
+ return list;
+
+ a = list;
+ b = list->next;
+
+ while ((b != NULL) && (b->next != NULL)) {
+ list = list->next;
+ b = b->next->next;
+ }
+
+ b = list->next;
+ list->next = NULL;
+
+ return merge_objs(slabsort(a), slabsort(b));
+}
+
+/*
+ * Sort Routines. Each of these should be associated with a command-line
+ * search option. The functions should fit the prototype:
+ *
+ * int sort_foo(const struct slab_info *a, const struct slab_info *b)
+ *
+ * They return one if the first parameter is larger than the second
+ * Otherwise, they return zero.
+ */
+
+static int sort_name(const struct slab_info *a, const struct slab_info *b)
+{
+ return (strcmp(a->name, b->name) < 0) ? 1 : 0;
+}
+
+static int sort_nr_objs(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->nr_objs > b->nr_objs);
+}
+
+static int sort_nr_active_objs(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->nr_active_objs > b->nr_active_objs);
+}
+
+static int sort_obj_size(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->obj_size > b->obj_size);
+}
+
+static int sort_objs_per_slab(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->objs_per_slab > b->objs_per_slab);
+}
+
+static int sort_pages_per_slab(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->pages_per_slab > b->pages_per_slab);
+}
+
+static int sort_nr_slabs(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->nr_slabs > b->nr_slabs);
+}
+
+static int sort_nr_active_slabs(const struct slab_info *a,
+ const struct slab_info *b)
+{
+ return (a->nr_active_slabs > b->nr_active_slabs);
+}
+
+
+static int sort_use(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->use > b->use);
+}
+
+static int sort_cache_size(const struct slab_info *a, const struct slab_info *b)
+{
+ return (a->cache_size > b->cache_size);
+}
+
+/*
+ * term_size - set the globals 'cols' and 'rows' to the current terminal size
+ */
+static void term_size(int unused)
+{
+ struct winsize ws;
+ (void) unused;
+
+ if ((ioctl(1, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) {
+ cols = ws.ws_col;
+ rows = ws.ws_row;
+ } else {
+ cols = 80;
+ rows = 24;
+ }
+}
+
+static void sigint_handler(int unused)
+{
+ (void) unused;
+
+ delay = 0;
+}
+
+static void usage(const char *cmd)
+{
+ fprintf(stderr, "usage: %s [options]\n\n", cmd);
+ fprintf(stderr, "options:\n");
+ fprintf(stderr, " --delay=n, -d n "
+ "delay n seconds between updates\n");
+ fprintf(stderr, " --once, -o "
+ "only display once, then exit\n");
+ fprintf(stderr, " --sort=S, -s S "
+ "specify sort criteria S (see below)\n");
+ fprintf(stderr, " --version, -V "
+ "display version information and exit\n");
+ fprintf(stderr, " --help display this help and exit\n\n");
+ fprintf(stderr, "The following are valid sort criteria:\n");
+ fprintf(stderr, " a: sort by number of active objects\n");
+ fprintf(stderr, " b: sort by objects per slab\n");
+ fprintf(stderr, " c: sort by cache size\n");
+ fprintf(stderr, " l: sort by number of slabs\n");
+ fprintf(stderr, " v: sort by number of active slabs\n");
+ fprintf(stderr, " n: sort by name\n");
+ fprintf(stderr, " o: sort by number of objects\n");
+ fprintf(stderr, " p: sort by pages per slab\n");
+ fprintf(stderr, " s: sort by object size\n");
+ fprintf(stderr, " u: sort by cache utilization\n");
+}
+
+/*
+ * set_sort_func - return the slab_sort_func that matches the given key.
+ * On unrecognizable key, DEF_SORT_FUNC is returned.
+ */
+static void * set_sort_func(char key)
+{
+ switch (key) {
+ case 'n':
+ return sort_name;
+ case 'o':
+ return sort_nr_objs;
+ case 'a':
+ return sort_nr_active_objs;
+ case 's':
+ return sort_obj_size;
+ case 'b':
+ return sort_objs_per_slab;
+ case 'p':
+ return sort_pages_per_slab;
+ case 'l':
+ return sort_nr_slabs;
+ case 'v':
+ return sort_nr_active_slabs;
+ case 'c':
+ return sort_cache_size;
+ case 'u':
+ return sort_use;
+ default:
+ return DEF_SORT_FUNC;
+ }
+}
+
+static void parse_input(char c)
+{
+ c = toupper(c);
+ switch(c) {
+ case 'A':
+ sort_func = sort_nr_active_objs;
+ break;
+ case 'B':
+ sort_func = sort_objs_per_slab;
+ break;
+ case 'C':
+ sort_func = sort_cache_size;
+ break;
+ case 'L':
+ sort_func = sort_nr_slabs;
+ break;
+ case 'V':
+ sort_func = sort_nr_active_slabs;
+ break;
+ case 'N':
+ sort_func = sort_name;
+ break;
+ case 'O':
+ sort_func = sort_nr_objs;
+ break;
+ case 'P':
+ sort_func = sort_pages_per_slab;
+ break;
+ case 'S':
+ sort_func = sort_obj_size;
+ break;
+ case 'U':
+ sort_func = sort_use;
+ break;
+ case 'Q':
+ delay = 0;
+ break;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int o;
+ unsigned short old_rows;
+ struct slab_info *slab_list = NULL;
+
+ struct option longopts[] = {
+ { "delay", 1, NULL, 'd' },
+ { "sort", 1, NULL, 's' },
+ { "once", 0, NULL, 'o' },
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ sort_func = DEF_SORT_FUNC;
+
+ while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) {
+ int ret = 1;
+
+ switch (o) {
+ case 'd':
+ errno = 0;
+ delay = strtol(optarg, NULL, 10);
+ if (errno) {
+ perror("strtoul");
+ return 1;
+ }
+ if (delay < 0) {
+ fprintf(stderr, "error: can't have a "\
+ "negative delay\n");
+ exit(1);
+ }
+ break;
+ case 's':
+ sort_func = set_sort_func(optarg[0]);
+ break;
+ case 'o':
+ delay = 0;
+ break;
+ case 'V':
+ display_version();
+ return 0;
+ case 'h':
+ ret = 0;
+ default:
+ usage(argv[0]);
+ return ret;
+ }
+ }
+
+ if (tcgetattr(0, &saved_tty) == -1)
+ perror("tcgetattr");
+
+ initscr();
+ term_size(0);
+ old_rows = rows;
+ resizeterm(rows, cols);
+ signal(SIGWINCH, term_size);
+ signal(SIGINT, sigint_handler);
+
+ do {
+ struct slab_info *curr;
+ struct slab_stat stats = SLAB_STAT_ZERO;
+ struct timeval tv;
+ fd_set readfds;
+ char c;
+ int i;
+
+ if (get_slabinfo(&slab_list, &stats))
+ break;
+
+ if (old_rows != rows) {
+ resizeterm(rows, cols);
+ old_rows = rows;
+ }
+
+ move(0,0);
+ printw( " Active / Total Objects (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Slabs (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Caches (%% used) : %d / %d (%.1f%%)\n"
+ " Active / Total Size (%% used) : %.2fK / %.2fK (%.1f%%)\n"
+ " Minimum / Average / Maximum Object : %.2fK / %.2fK / %.2fK\n\n",
+ stats.nr_active_objs, stats.nr_objs, 100.0 * stats.nr_active_objs / stats.nr_objs,
+ stats.nr_active_slabs, stats.nr_slabs, 100.0 * stats.nr_active_slabs / stats.nr_slabs,
+ stats.nr_active_caches, stats.nr_caches, 100.0 * stats.nr_active_caches / stats.nr_caches,
+ stats.active_size / 1024.0, stats.total_size / 1024.0, 100.0 * stats.active_size / stats.total_size,
+ stats.min_obj_size / 1024.0, stats.avg_obj_size / 1024.0, stats.max_obj_size / 1024.0
+ );
+
+ slab_list = slabsort(slab_list);
+
+ attron(A_REVERSE);
+ printw( "%6s %6s %4s %8s %6s %8s %10s %-23s\n",
+ "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS",
+ "OBJ/SLAB", "CACHE SIZE", "NAME");
+ attroff(A_REVERSE);
+
+ curr = slab_list;
+ for (i = 0; i < rows - 8 && curr->next; i++) {
+ printw("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
+ curr->nr_objs, curr->nr_active_objs, curr->use,
+ curr->obj_size / 1024.0, curr->nr_slabs,
+ curr->objs_per_slab, (unsigned)(curr->cache_size / 1024),
+ curr->name);
+ curr = curr->next;
+ }
+
+ refresh();
+ put_slabinfo(slab_list);
+
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ tv.tv_sec = delay;
+ tv.tv_usec = 0;
+ if (select(1, &readfds, NULL, NULL, &tv) > 0) {
+ if (read(0, &c, 1) != 1)
+ break;
+ parse_input(c);
+ }
+ } while (delay);
+
+ tcsetattr(0, TCSAFLUSH, &saved_tty);
+ free_slabinfo(slab_list);
+ endwin();
+ return 0;
+}
--- /dev/null
+.so man1/skill.1
--- /dev/null
+.\" Copyright 1999, George Staikos (staikos@0wned.org)
+.\" This file may be used subject to the terms and conditions of the
+.\" GNU General Public License Version 2, or any later version
+.\" at your option, as published by the Free Software Foundation.
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details."
+.TH SYSCTL 8 "21 Sep 1999" "" ""
+.SH NAME
+sysctl \- configure kernel parameters at runtime
+.SH SYNOPSIS
+.B "sysctl [-n] [-e] variable ..."
+.br
+.B "sysctl [-n] [-e] [-q] -w variable=value ..."
+.br
+.B "sysctl [-n] [-e] [-q] -p <filename>"
+.br
+.B "sysctl [-n] [-e] -a"
+.br
+.B "sysctl [-n] [-e] -A"
+.SH DESCRIPTION
+.B sysctl
+is used to modify kernel parameters at runtime. The parameters available
+are those listed under /proc/sys/. Procfs is required for
+.B sysctl(8)
+support in Linux. You can use
+.B sysctl(8)
+to both read and write sysctl data.
+.SH PARAMETERS
+.TP
+.B "variable"
+The name of a key to read from. An example is kernel.ostype. The '/'
+separator is also accepted in place of a '.'.
+.TP
+.B "variable=value"
+To set a key, use the form variable=value, where variable is the key and
+value is the value to set it to. If the value contains quotes or characters
+which are parsed by the shell, you may need to enclose the value in double
+quotes. This requires the -w parameter to use.
+.TP
+.B "-n"
+Use this option to disable printing of the key name when printing values.
+.TP
+.B "-e"
+Use this option to ignore errors about unknown keys.
+.TP
+.B "-N"
+Use this option to only print the names. It may be useful with shells that
+have programmable completion.
+.TP
+.B "-q"
+Use this option to not display the values set to stdout.
+.TP
+.B "-w"
+Use this option when you want to change a sysctl setting.
+.TP
+.B "-p"
+Load in sysctl settings from the file specified or /etc/sysctl.conf if none given.
+Specifying \- as filename means reading data from standard input.
+.TP
+.B "-a"
+Display all values currently available.
+.TP
+.B "-A"
+Display all values currently available in table form.
+.SH EXAMPLES
+.TP
+/sbin/sysctl -a
+.TP
+/sbin/sysctl -n kernel.hostname
+.TP
+/sbin/sysctl -w kernel.domainname="example.com"
+.TP
+/sbin/sysctl -p /etc/sysctl.conf
+.SH FILES
+.I /proc/sys
+.I /etc/sysctl.conf
+.SH SEE ALSO
+.BR sysctl.conf (5)
+.SH BUGS
+The -A parameter behaves just as -a does.
+.SH AUTHOR
+George Staikos, <staikos@0wned.org>
+
--- /dev/null
+
+/*
+ * 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 <preload> to preload values from a file
+ * Horms:
+ * - added -q to be quiet when modifying values
+ *
+ * Changes by Albert Cahalan, 2002.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include "proc/procps.h"
+#include "proc/version.h"
+
+
+// Proof that C++ causes brain damage:
+typedef int bool;
+static bool true = 1;
+static bool false = 0;
+
+/*
+ * Globals...
+ */
+
+static const char PROC_PATH[] = "/proc/sys/";
+static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
+static bool NameOnly;
+static bool PrintName;
+static bool PrintNewline;
+static bool IgnoreError;
+static bool Quiet;
+
+/* error messages */
+static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter \"%s\"\n";
+static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting \"%s\"\n";
+static const char ERR_NO_EQUALS[] = "error: \"%s\" must be of the form name=value\n";
+static const char ERR_INVALID_KEY[] = "error: \"%s\" is an unknown key\n";
+static const char ERR_UNKNOWN_WRITING[] = "error: \"%s\" setting key \"%s\"\n";
+static const char ERR_UNKNOWN_READING[] = "error: \"%s\" reading key \"%s\"\n";
+static const char ERR_PERMISSION_DENIED[] = "error: permission denied on key '%s'\n";
+static const char ERR_OPENING_DIR[] = "error: unable to open directory \"%s\"\n";
+static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n";
+static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n";
+
+
+static void slashdot(char *restrict p, char old, char new){
+ p = strpbrk(p,"/.");
+ if(!p) return; /* nothing -- can't be, but oh well */
+ if(*p==new) return; /* already in desired format */
+ while(p){
+ char c = *p;
+ if(c==old) *p=new;
+ if(c==new) *p=old;
+ p = strpbrk(p+1,"/.");
+ }
+}
+
+
+
+/*
+ * Display the usage format
+ *
+ */
+static int Usage(const char *restrict const name) {
+ printf("usage: %s [-n] [-e] variable ... \n"
+ " %s [-n] [-e] [-q] -w variable=value ... \n"
+ " %s [-n] [-e] -a \n"
+ " %s [-n] [-e] [-q] -p <file> (default /etc/sysctl.conf) \n"
+ " %s [-n] [-e] -A\n", name, name, name, name, name);
+ return -1;
+}
+
+
+/*
+ * Strip the leading and trailing spaces from a string
+ *
+ */
+static char *StripLeadingAndTrailingSpaces(char *oneline) {
+ char *t;
+
+ if (!oneline || !*oneline)
+ return oneline;
+
+ t = oneline;
+ t += strlen(oneline)-1;
+
+ while ((*t==' ' || *t=='\t' || *t=='\n' || *t=='\r') && t!=oneline)
+ *t-- = 0;
+
+ t = oneline;
+
+ while ((*t==' ' || *t=='\t') && *t!=0)
+ t++;
+
+ return t;
+}
+
+static int DisplayAll(const char *restrict const path);
+
+/*
+ * Read a sysctl setting
+ *
+ */
+static int ReadSetting(const char *restrict const name) {
+ int rc = 0;
+ char *restrict tmpname;
+ char *restrict outname;
+ char inbuf[1025];
+ FILE *restrict fp;
+
+ if (!name || !*name) {
+ fprintf(stderr, ERR_INVALID_KEY, name);
+ return -1;
+ }
+
+ /* used to open the file */
+ tmpname = malloc(strlen(name)+strlen(PROC_PATH)+2);
+ strcpy(tmpname, PROC_PATH);
+ strcat(tmpname, name);
+ slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
+
+ /* used to display the output */
+ outname = strdup(name);
+ slashdot(outname,'/','.'); /* change / to . */
+
+ fp = fopen(tmpname, "r");
+
+ if (!fp) {
+ switch(errno) {
+ case ENOENT:
+ if (!IgnoreError) {
+ fprintf(stderr, ERR_INVALID_KEY, outname);
+ rc = -1;
+ }
+ break;
+ case EACCES:
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+ rc = -1;
+ break;
+ default:
+ fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
+ rc = -1;
+ break;
+ }
+ } else {
+ if(fgets(inbuf, sizeof inbuf - 1, fp)) {
+ // this loop is required, see
+ // /sbin/sysctl -a | egrep -6 dev.cdrom.info
+ do {
+ if (NameOnly) {
+ fprintf(stdout, "%s\n", outname);
+ } else {
+ /* already has the \n in it */
+ if (PrintName) {
+ fprintf(stdout, "%s = %s", outname, inbuf);
+ } else {
+ if (!PrintNewline) {
+ char *nlptr = strchr(inbuf,'\n');
+ if(nlptr) *nlptr='\0';
+ }
+ fprintf(stdout, "%s", inbuf);
+ }
+ }
+ } while(fgets(inbuf, sizeof inbuf - 1, fp));
+ } else {
+ switch(errno) {
+ case EACCES:
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+ rc = -1;
+ break;
+ case EISDIR:{
+ size_t len;
+ len = strlen(tmpname);
+ tmpname[len] = '/';
+ tmpname[len+1] = '\0';
+ rc = DisplayAll(tmpname);
+ break;
+ }
+ default:
+ fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
+ rc = -1;
+ break;
+ }
+ }
+ fclose(fp);
+ }
+
+ free(tmpname);
+ free(outname);
+ return rc;
+}
+
+
+
+/*
+ * Display all the sysctl settings
+ *
+ */
+static int DisplayAll(const char *restrict const path) {
+ int rc = 0;
+ int rc2;
+ DIR *restrict dp;
+ struct dirent *restrict de;
+ struct stat ts;
+
+ dp = opendir(path);
+
+ if (!dp) {
+ fprintf(stderr, ERR_OPENING_DIR, path);
+ rc = -1;
+ } else {
+ readdir(dp); // skip .
+ readdir(dp); // skip ..
+ while (( de = readdir(dp) )) {
+ char *restrict tmpdir;
+ tmpdir = (char *restrict)malloc(strlen(path)+strlen(de->d_name)+2);
+ sprintf(tmpdir, "%s%s", path, de->d_name);
+ rc2 = stat(tmpdir, &ts);
+ if (rc2 != 0) {
+ perror(tmpdir);
+ } else {
+ if (S_ISDIR(ts.st_mode)) {
+ strcat(tmpdir, "/");
+ DisplayAll(tmpdir);
+ } else {
+ rc |= ReadSetting(tmpdir+strlen(PROC_PATH));
+ }
+ }
+ free(tmpdir);
+ }
+ closedir(dp);
+ }
+ return rc;
+}
+
+
+/*
+ * Write a sysctl setting
+ *
+ */
+static int WriteSetting(const char *setting) {
+ int rc = 0;
+ const char *name = setting;
+ const char *value;
+ const char *equals;
+ char *tmpname;
+ FILE *fp;
+ char *outname;
+
+ if (!name) { /* probably don't want to display this err */
+ return 0;
+ } /* end if */
+
+ equals = index(setting, '=');
+
+ if (!equals) {
+ fprintf(stderr, ERR_NO_EQUALS, setting);
+ return -1;
+ }
+
+ value = equals + 1; /* point to the value in name=value */
+
+ if (!*name || !*value || name == equals) {
+ fprintf(stderr, ERR_MALFORMED_SETTING, setting);
+ return -2;
+ }
+
+ /* used to open the file */
+ tmpname = malloc(equals-name+1+strlen(PROC_PATH));
+ strcpy(tmpname, PROC_PATH);
+ strncat(tmpname, name, (int)(equals-name));
+ tmpname[equals-name+strlen(PROC_PATH)] = 0;
+ slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
+
+ /* used to display the output */
+ outname = malloc(equals-name+1);
+ strncpy(outname, name, (int)(equals-name));
+ outname[equals-name] = 0;
+ slashdot(outname,'/','.'); /* change / to . */
+
+ fp = fopen(tmpname, "w");
+
+ if (!fp) {
+ switch(errno) {
+ case ENOENT:
+ if (!IgnoreError) {
+ fprintf(stderr, ERR_INVALID_KEY, outname);
+ rc = -1;
+ }
+ break;
+ case EACCES:
+ fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+ rc = -1;
+ break;
+ default:
+ fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
+ rc = -1;
+ break;
+ }
+ } else {
+ rc = fprintf(fp, "%s\n", value);
+ if (rc < 0) {
+ fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
+ fclose(fp);
+ } else {
+ rc=fclose(fp);
+ if (rc != 0)
+ fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
+ }
+ if (rc==0 && !Quiet) {
+ if (NameOnly) {
+ fprintf(stdout, "%s\n", outname);
+ } else {
+ if (PrintName) {
+ fprintf(stdout, "%s = %s\n", outname, value);
+ } else {
+ if (PrintNewline)
+ fprintf(stdout, "%s\n", value);
+ else
+ fprintf(stdout, "%s", value);
+ }
+ }
+ }
+ }
+
+ free(tmpname);
+ free(outname);
+ return rc;
+}
+
+
+
+/*
+ * Preload the sysctl's from the conf file
+ * - we parse the file and then reform it (strip out whitespace)
+ *
+ */
+static int Preload(const char *restrict const filename) {
+ char oneline[256];
+ char buffer[256];
+ FILE *fp;
+ char *t;
+ int n = 0;
+ int rc = 0;
+ char *name, *value;
+
+ fp = (filename[0]=='-' && !filename[1])
+ ? stdin
+ : fopen(filename, "r")
+ ;
+
+ if (!fp) {
+ fprintf(stderr, ERR_PRELOAD_FILE, filename);
+ return -1;
+ }
+
+ while (fgets(oneline, sizeof oneline, fp)) {
+ n++;
+ t = StripLeadingAndTrailingSpaces(oneline);
+
+ if (strlen(t) < 2)
+ continue;
+
+ if (*t == '#' || *t == ';')
+ continue;
+
+ name = strtok(t, "=");
+ if (!name || !*name) {
+ fprintf(stderr, WARN_BAD_LINE, filename, n);
+ continue;
+ }
+
+ StripLeadingAndTrailingSpaces(name);
+
+ value = strtok(NULL, "\n\r");
+ if (!value || !*value) {
+ fprintf(stderr, WARN_BAD_LINE, filename, n);
+ continue;
+ }
+
+ while ((*value == ' ' || *value == '\t') && *value != 0)
+ value++;
+
+ // should NameOnly affect this?
+ sprintf(buffer, "%s=%s", name, value);
+ rc |= WriteSetting(buffer);
+ }
+
+ fclose(fp);
+ return rc;
+}
+
+
+
+/*
+ * Main...
+ *
+ */
+int main(int argc, char *argv[]) {
+ const char *me = (const char *)basename(argv[0]);
+ bool SwitchesAllowed = true;
+ bool WriteMode = false;
+ int ReturnCode = 0;
+ const char *preloadfile = DEFAULT_PRELOAD;
+
+ PrintName = true;
+ PrintNewline = true;
+ IgnoreError = false;
+ Quiet = false;
+
+ if (argc < 2) {
+ return Usage(me);
+ }
+
+ argv++;
+
+ for (; argv && *argv && **argv; argv++) {
+ if (SwitchesAllowed && **argv == '-') { /* we have a switch */
+ if ((*argv)[1] && (*argv)[2]){ // don't yet handle "sysctl -ew"
+ if (!strcmp("--help",*argv)) {
+ Usage(me);
+ exit(0);
+ }
+ if (!strcmp("--version",*argv)) {
+ fprintf(stdout, "sysctl (%s)\n",procps_version);
+ exit(0);
+ }
+ fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
+ return Usage(me);
+ }
+ switch((*argv)[1]) {
+ case 'b':
+ /* This is "binary" format, which means more for BSD. */
+ PrintNewline = false;
+ /* FALL THROUGH */
+ case 'n':
+ PrintName = false;
+ break;
+ case 'e':
+ // For FreeBSD, -e means a "%s=%s\n" format. ("%s: %s\n" default)
+ // We (and NetBSD) use "%s = %s\n" always, and -e to ignore errors.
+ IgnoreError = true;
+ break;
+ case 'N':
+ NameOnly = true;
+ break;
+ case 'w':
+ SwitchesAllowed = false;
+ WriteMode = true;
+ break;
+ case 'f': // the NetBSD way
+ case 'p':
+ argv++;
+ if (argv && *argv && **argv) {
+ preloadfile = *argv;
+ }
+ return Preload(preloadfile);
+ case 'q':
+ Quiet = true;
+ break;
+ case 'o': // BSD: binary values too, 1st 16 bytes in hex
+ case 'x': // BSD: binary values too, whole thing in hex
+ /* does nothing */ ;
+ break;
+ case 'a': // string and integer values (for Linux, all of them)
+ case 'A': // same as -a -o
+ case 'X': // same as -a -x
+ SwitchesAllowed = false;
+ return DisplayAll(PROC_PATH);
+ case 'V':
+ fprintf(stdout, "sysctl (%s)\n",procps_version);
+ exit(0);
+ case 'd': // BSD: print description ("vm.kvm_size: Size of KVM")
+ case 'h': // BSD: human-readable (did FreeBSD 5 make -e default?)
+ case '?':
+ return Usage(me);
+ default:
+ fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
+ return Usage(me);
+ }
+ } else {
+ if (NameOnly && Quiet) // nonsense
+ return Usage(me);
+ SwitchesAllowed = false;
+ if (WriteMode || index(*argv, '='))
+ ReturnCode = WriteSetting(*argv);
+ else
+ ReturnCode = ReadSetting(*argv);
+ }
+ }
+
+ return ReturnCode;
+}
+
+
--- /dev/null
+# /etc/sysctl.conf - Configuration file for setting system variables
+# See sysctl.conf (5) for information.
+
+# you can have the CD-ROM close when you use it, and open
+# when you are done.
+#dev.cdrom.autoeject = 1
+#dev.cdrom.autoclose = 1
+
+# protection from the SYN flood attack
+net/ipv4/tcp_syncookies=1
+
+# see the evil packets in your log files
+net/ipv4/conf/all/log_martians=1
+
+# makes you vulnerable or not :-)
+net/ipv4/conf/all/accept_redirects=0
+net/ipv4/conf/all/accept_source_route=0
+net/ipv4/icmp_echo_ignore_broadcasts =1
+
+# needed for routing, including masquerading or NAT
+#net/ipv4/ip_forward=1
+
+# sets the port range used for outgoing connections
+#net.ipv4.ip_local_port_range = 32768 61000
+
+# Broken routers and obsolete firewalls will corrupt the window scaling
+# and ECN. Set these values to 0 to disable window scaling and ECN.
+# This may, rarely, cause some performance loss when running high-speed
+# TCP/IP over huge distances or running TCP/IP over connections with high
+# packet loss and modern routers. This sure beats dropped connections.
+#net.ipv4.tcp_default_win_scale = 0
+#net.ipv4.tcp_ecn = 0
+
+# Swapping too much or not enough? Disks spinning up when you'd
+# rather they didn't? Tweak these.
+#vm.vfs_cache_pressure = 100
+#vm.laptop_mode = 0
+#vm.swappiness = 60
+
+#kernel.printk_ratelimit_burst = 10
+#kernel.printk_ratelimit = 5
+#kernel.panic_on_oops = 0
+
+# Reboot 600 seconds after a panic
+#kernel.panic = 600
+
+# enable SysRq key (note: console security issues)
+#kernel.sysrq = 1
+
+# Change name of core file to start with the command name
+# so you get things like: emacs.core mozilla-bin.core X.core
+#kernel.core_pattern = %e.core
+
+# NIS/YP domain (not always equal to DNS domain)
+#kernel.domainname = example.com
+#kernel.hostname = darkstar
+
+# This limits PID values to 4 digits, which allows tools like ps
+# to save screen space.
+kernel/pid_max=10000
--- /dev/null
+.\" 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, <staikos@0wned.org>
+
--- /dev/null
+#!/bin/sh
+#
+# Wow, using $* causes great pain with embedded spaces in arguments.
+# The "$@" won't break that into 2 arguments.
+#
+LD_LIBRARY_PATH=proc exec ./top "$@"
--- /dev/null
+.\" -*-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 <david@ods.com>, and
+Michael K. Johnson <johnsonm@redhat.com>.
+
+Please send bug reports to <albert@users.sf.net>
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+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();
+ }
+}
--- /dev/null
+.ig
+. manual page for NEW top
+. Copyright (c) 2002, by: JC Warner & Associates, Ltd.
+.
+. Permission is granted to copy, distribute and/or modify this document
+. under the terms of the GNU Free Documentation License, Version 1.1 or
+. any later version published by the Free Software Foundation;
+. with no Front-Cover Texts, no Back-Cover Texts, and with the following
+. Invariant Sections (and any sub-sections therein):
+. all .ig sections, including this one
+. STUPID TRICKS Sampler
+. AUTHOR
+.
+. A copy of the Free Documentation License is included in the section
+. entitled "GNU Free Documentation License".
+.
+. [ that section is found near the end of this document & ]
+. [ can be made printable by disabling the .ig directive! ]
+.
+..
+.\" Setup ////////////////////////////////////////////////////////////////
+\# ** Comment out '.nr' or set to 0 to eliminate WIDTH fiddlin' !
+.nr half_xtra 4
+.
+.ll +(\n[half_xtra] + \n[half_xtra])
+.
+\# Our darn Bullet style ----------------------------
+.de Jbu
+.IP "-" 3
+..
+\# - bullet continuation paragraph
+.de Jp
+.IP "" 3
+..
+\# New features/differences style -------------------
+.de New
+.IP "-*-" 5
+..
+.
+\# Commonly used strings (for consistency) ----------
+\# - a real em-dash, darn-it
+.ds EM \ \fB\-\-\ \fR
+\# - these two are for chuckles, makes great grammar
+.ds Me top
+.ds ME \fBtop\fR
+\# - other misc strings for consistent usage/emphasis
+.ds F \fIOff\fR
+.ds O \fIOn\fR
+.
+.ds AM alternate\-display mode
+.ds AS asterisk ('*')
+.ds CF configuration file
+.ds CI interactive command
+.ds CO command\-line option
+.ds CW \'current' window
+.ds FM full\-screen mode
+.ds MP \fBphysical\fR memory
+.ds MS \fBshared\fR memory
+.ds MV \fBvirtual\fR memory
+.ds NT \fBNote\fR:
+.ds PU CPU
+.ds Pu cpu
+.ds SA summary area
+.ds TA task area
+.ds TD task display
+.ds TW task window
+\# - xref's that depend on commands or topic names
+.ds XC See the
+.ds Xc see the
+.ds XT See topic
+.ds Xt see topic
+.
+.\" //////////////////////////////////////////////////////////////////////
+.\" ----------------------------------------------------------------------
+.TH TOP 1 "September 2002" "Linux" "Linux User's Manual"
+.\" ----------------------------------------------------------------------
+
+
+.\" ----------------------------------------------------------------------
+.SH NAME
+.\" ----------------------------------------------------------------------
+top \- display Linux tasks
+
+
+.\" ----------------------------------------------------------------------
+.SH SYNOPSIS
+.\" ----------------------------------------------------------------------
+\*(ME \-\fBhv\fR | \-\fBbcHisS\fR \-\fBd\fI delay\fR \-\fBn\fI
+iterations\fR \-\fBp\fI pid\fR [,\fI pid\fR ...]
+
+The traditional switches '-' and whitespace are optional.
+
+
+.\" ----------------------------------------------------------------------
+.SH DESCRIPTION
+.\" ----------------------------------------------------------------------
+The \*(ME program provides a dynamic real-time view of a running system.
+It can display\fB system\fR summary information as well as a list of\fB
+tasks\fR currently being managed by the Linux kernel.
+The types of system summary information shown and the types, order and
+size of information displayed for tasks are all user configurable and
+that configuration can be made persistent across restarts.
+
+The program provides a limited interactive interface for process
+manipulation as well as a much more extensive interface for personal
+configuration \*(EM encompassing every aspect of its operation.
+And while \*(ME is referred to throughout this document, you are free
+to name the program anything you wish.
+That new name, possibly an alias, will then be reflected on \*(Me's display
+and used when reading and writing a \*(CF.
+
+
+.\" ----------------------------------------------------------------------
+.SH OVERVIEW
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS Documentation
+.\" ----------------------------------------------------------------------
+The remaining Table of Contents
+ 1. COMMAND\-LINE Options
+ 2. FIELDS / Columns
+ a. DESCRIPTIONS of Fields
+ b. SELECTING and ORDERING Columns
+ 3. INTERACTIVE Commands
+ a. GLOBAL Commands
+ b. SUMMARY Area Commands
+ c. TASK Area Commands
+ d. COLOR Mapping
+ 4. ALTERNATE\-DISPLAY Mode
+ a. WINDOWS Overview
+ b. COMMANDS for Windows
+ 5. FILES
+ a. SYSTEM Configuration File
+ b. PERSONAL Configuration File
+ 6. STUPID TRICKS Sampler
+ a. Kernel Magic
+ b. Bouncing Windows
+ c. The Big Bird Window
+ 7. BUGS, 8. HISTORY Former top, 9. AUTHOR, 10. SEE ALSO
+
+.\" ......................................................................
+.SS Operation
+.\" ----------------------------------------------------------------------
+When operating \*(Me, the two most important keys are help ('h' or '?') and
+quit ('q') key.
+Alternatively, you could simply use the traditional interrupt key ('^C')
+when you're done.
+
+When you start \*(Me for the first time, you'll be presented with the
+traditional screen elements: 1) Summary Area; 2) Message/Prompt Line;
+3) Columns Header; 4) Task Area.
+There will, however, be some differences when compared to the former top.
+
+.TP 3
+.B Highlighting
+.I Summary_Area\fR:
+There is no highlighting for load/uptime and only values are highlighted for
+other elements.
+
+.I Task_Area\fR:
+Tasks running (or ready to run) will be highlighted, and bold is only one way
+of emphasizing such processes.
+
+.TP 3
+.B Content/Labels
+.I Summary_Area\fR:
+The program name is shown, perhaps a symlink or alias.
+The Cpu(s) state label hints at other possibilities.
+The memory stats use a lower case 'k'.
+
+.I Columns_Header\fR:
+Will show a new field and some changed labels.
+More new fields will be found as you customize your \*(Me.
+
+.PP
+\*(NT the width of \*(Me's display will be limited to 512 positions.
+Displaying all fields requires a minimum of 160 characters.
+The remaining width could be used for the 'Command' column.
+
+.\" ......................................................................
+.SS Startup Defaults
+.\" ----------------------------------------------------------------------
+The following startup defaults assume no \*(CF, thus no user customizations.
+Even so, items shown with an \*(AS could be overridden through the
+command-line.
+
+ \fIGlobal_defaults\fR
+ 'A' - Alt display Off (full-screen)
+ * 'd' - Delay time 3.0 seconds
+ 'I' - Irix mode On\ \ (no, 'solaris' smp)
+ * 'p' - PID monitoring Off
+ * 's' - Secure mode Off (unsecured)
+ 'B' - Bold disable Off
+ \fISummary_Area_defaults\fR
+ 'l' - Load Avg/Uptime On\ \ (thus program name)
+ 't' - Task/Cpu states On\ \ (1+1 lines, see '1')
+ 'm' - Mem/Swap usage On\ \ (2 lines worth)
+ '1' - Single Cpu On\ \ (thus 1 line if smp)
+ \fITask_Area_defaults\fR
+ 'b' - Bold hilite On\ \ (not 'reverse')
+ * 'c' - Command line Off (name, not cmdline)
+ * 'H' - Threads Off\ (show all threads)
+ * 'i' - Idle tasks On\ \ (show all tasks)
+ 'R' - Reverse sort On\ \ (pids high-to-low)
+ * 'S' - Cumulative time Off (no, dead children)
+ 'x' - Column hilite Off\ (no, sort field)
+ 'y' - Row hilite On\ \ (yes, running tasks)
+ 'z' - color/mono Off\ (no, colors)
+
+
+.\" ----------------------------------------------------------------------
+.SH 1. COMMAND-LINE Options
+.\" ----------------------------------------------------------------------
+The command-line syntax for \*(Me consists of:
+
+ \-\fBhv\fR\ |\ -\fBbcHisS\fR\ \-\fBd\fI\ delay\fR\ \-\fBn\fI\ iterations\
+\fR\ \-\fBp\fI\ pid\fR\ [,\fIpid\fR...]
+
+The typically mandatory switches ('-') and even whitespace are completely
+optional.
+
+.TP 5
+\-\fBb\fR :\fB Batch mode\fR operation
+Starts \*(Me in 'Batch mode', which could be useful for sending output
+from \*(Me to other programs or to a file.
+In this mode, \*(Me will not accept input and runs until the iterations
+limit you've set with the '-n' \*(CO or until killed.
+
+.TP 5
+\-\fBc\fR :\fB Command line/Program name\fR toggle
+Starts \*(Me with the last remembered 'c' state reversed.
+Thus, if \*(Me was displaying command lines, now that field will show program
+names, and visa versa.
+\*(XC 'c' \*(CI for additional information.
+
+.TP 5
+\-\fBd\fR :\fB Delay time\fR interval as:\ \ \fB-d ss.tt\fR (\fIseconds\fR.\fItenths\fR)
+Specifies the delay between screen updates, and overrides the corresponding
+value in one's personal \*(CF or the startup default.
+Later this can be changed with the 'd' or 's' \*(CIs.
+
+Fractional seconds are honored, but a negative number is not allowed.
+In all cases, however, such changes are prohibited if \*(Me is running
+in 'Secure mode', except for root (unless the 's' \*(CO was used).
+For additional information on 'Secure mode' \*(Xt 5a. SYSTEM Configuration File.
+
+
+.TP 5
+\-\fBh\fR :\fB Help\fR
+Show library version and the usage prompt, then quit.
+
+.TP 5
+\-\fBH\fR :\fB Threads\fR toggle
+Starts \*(Me with the last remembered 'H' state reversed.
+When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process.
+
+.TP 5
+\-\fBi\fR :\fB Idle Processes\fR toggle
+Starts \*(Me with the last remembered 'i' state reversed.
+When this toggle is \*F, tasks that are idled or zombied will not be displayed.
+
+.TP 5
+\-\fBn\fR :\fB Number of iterations\fR limit as:\fB\ \ -n number\fR
+Specifies the maximum number of iterations, or frames, \*(Me should
+produce before ending.
+
+.TP 5
+\-\fBu\fR :\fB Monitor by user\fR as:\fB\ \ -u somebody
+Monitor only processes with an effective UID or user name
+matching that given.
+
+.TP 5
+\-\fBU\fR :\fB Monitor by user\fR as:\fB\ \ -U somebody
+Monitor only processes with a UID or user name matching that given.
+This matches real, effective, saved, and filesystem UIDs.
+
+.TP 5
+\-\fBp\fR :\fB Monitor PIDs\fR as:\fB\ \ -pN1 -pN2 ...\fR\ \ or\fB\ \ -pN1, N2 [,...]
+Monitor only processes with specified process IDs.
+This option can be given up to 20 times, or you can provide a comma delimited
+list with up to 20 pids.
+Co-mingling both approaches is permitted.
+
+This is a \*(CO only.
+And should you wish to return to normal operation, it is not necessary
+to quit and and restart \*(Me \*(EM just issue the '=' \*(CI.
+
+.TP 5
+\-\fBs\fR :\fB Secure mode\fR operation
+Starts \*(Me with secure mode forced, even for root.
+This mode is far better controlled through the system \*(CF
+(\*(Xt 5. FILES).
+
+.TP 5
+\-\fBS\fR :\fB Cumulative time mode\fR toggle
+Starts \*(Me with the last remembered 'S' state reversed.
+When 'Cumulative mode' is \*O, each process is listed with the \*(Pu
+time that it and its dead children have used.
+\*(XC 'S' \*(CI for additional information regarding this mode.
+
+.TP 5
+\-\fBv\fR :\fB Version\fR
+Show library version and the usage prompt, then quit.
+
+
+.\" ----------------------------------------------------------------------
+.SH 2. FIELDS / Columns
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS 2a. DESCRIPTIONS of Fields
+.\" ----------------------------------------------------------------------
+Listed below are \*(Me's available fields.
+They are always associated with the letter shown, regardless of the position
+you may have established for them with the 'o' (Order fields) \*(CI.
+
+Any field is selectable as the sort field, and you control whether they
+are sorted high-to-low or low-to-high.
+For additional information on sort provisions \*(Xt 3c. TASK Area Commands.
+
+.TP 3
+a:\fB PID\fR \*(EM Process Id\fR
+The task's unique process ID, which periodically wraps,
+though never restarting at zero.
+
+.TP 3
+b:\fB PPID\fR \*(EM Parent Process Pid\fR
+The process ID of a task's parent.
+
+.TP 3
+c:\fB RUSER\fR \*(EM Real User Name\fR
+The real user name of the task's owner.
+
+.TP 3
+d:\fB UID\fR \*(EM User Id\fR
+The effective user ID of the task's owner.
+
+.TP 3
+e:\fB USER\fR \*(EM User Name\fR
+The effective user name of the task's owner.
+
+.TP 3
+f:\fB GROUP\fR \*(EM Group Name\fR
+The effective group name of the task's owner.
+
+.TP 3
+g:\fB TTY\fR \*(EM Controlling Tty
+The name of the controlling terminal.
+This is usually the device (serial port, pty, etc.) from which the
+process was started, and which it uses for input or output.
+However, a task need not be associated with a terminal, in which case
+you'll see '?' displayed.
+
+.TP 3
+h:\fB PR\fR \*(EM Priority
+The priority of the task.
+
+.TP 3
+i:\fB NI\fR \*(EM Nice value
+The nice value of the task.
+A negative nice value means higher priority, whereas a positive nice value
+means lower priority.
+Zero in this field simply means priority will not be adjusted in determining a
+task's dispatchability.
+
+.TP 3
+j:\fB P\fR \*(EM Last used \*(PU (SMP)
+A number representing the last used processor.
+In a true SMP environment this will likely change frequently since the kernel
+intentionally uses weak affinity.
+Also, the very act of running \*(Me may break this weak affinity and cause more
+processes to change \*(PUs more often (because of the extra demand for
+\*(Pu time).
+
+.TP 3
+k:\fB %CPU\fR \*(EM \*(PU usage
+The task's share of the elapsed \*(PU time since the last screen update,
+expressed as a percentage of total \*(PU time.
+In a true SMP environment, if 'Irix mode' is \*F, \*(Me will operate
+in 'Solaris mode' where a task's \*(Pu usage will be divided by the total
+number of \*(PUs.
+You toggle 'Irix/Solaris' modes with the 'I' \*(CI.
+
+.TP 3
+l:\fB TIME\fR \*(EM \*(PU Time
+Total \*(PU time the task has used since it started.
+When 'Cumulative mode' is \*O, each process is listed with the \*(Pu
+time that it and its dead children has used.
+You toggle 'Cumulative mode' with 'S', which is a \*(CO and an \*(CI.
+\*(XC 'S' \*(CI for additional information regarding this mode.
+
+.TP 3
+m:\fB TIME+\fR \*(EM \*(PU Time, hundredths
+The same as 'TIME', but reflecting more granularity through hundredths of
+a second.
+
+.TP 3
+n:\fB %MEM\fR \*(EM Memory usage (RES)
+A task's currently used share of available \*(MP.
+
+.TP 3
+o:\fB VIRT\fR \*(EM Virtual Image (kb)
+The total amount of \*(MV used by the task.
+It includes all code, data and shared libraries plus pages that have been
+swapped out.
+
+VIRT = SWAP + RES.
+
+.TP 3
+p:\fB SWAP\fR \*(EM Swapped size (kb)
+The swapped out portion of a task's total \*(MV image.
+
+.TP 3
+q:\fB RES\fR \*(EM Resident size (kb)
+The non-swapped \*(MP a task has used.
+
+RES = CODE + DATA.
+
+.TP 3
+r:\fB CODE\fR \*(EM Code size (kb)
+The amount of \*(MP devoted to executable code, also known as
+the 'text resident set' size or TRS.
+
+.TP 3
+s:\fB DATA\fR \*(EM Data+Stack size (kb)
+The amount of \*(MP devoted to other than executable code, also known as
+the 'data resident set' size or DRS.
+
+.TP 3
+t:\fB SHR\fR \*(EM Shared Mem size (kb)
+The amount of \*(MS used by a task.
+It simply reflects memory that could be potentially shared with
+other processes.
+
+.TP 3
+u:\fB nFLT\fR \*(EM Page Fault count
+The number of\fB major\fR page faults that have occurred for a task.
+A page fault occurs when a process attempts to read from or write to a virtual
+page that is not currently present in its address space.
+A major page fault is when disk access is involved in making that
+page available.
+
+.TP 3
+v:\fB nDRT\fR \*(EM Dirty Pages count
+The number of pages that have been modified since they were last
+written to disk.
+Dirty pages must be written to disk before the corresponding physical memory
+location can be used for some other virtual page.
+
+.TP 3
+w:\fB S\fR \*(EM Process Status
+The status of the task which can be one of:
+ '\fBD\fR' = uninterruptible sleep
+ '\fBR\fR' = running
+ '\fBS\fR' = sleeping
+ '\fBT\fR' = traced or stopped
+ '\fBZ\fR' = zombie
+
+Tasks shown as running should be more properly thought of as 'ready to run'
+\*(EM their task_struct is simply represented on the Linux run-queue.
+Even without a true SMP machine, you may see numerous tasks in this state
+depending on \*(Me's delay interval and nice value.
+
+.TP 3
+x:\fB Command\fR \*(EM Command\fB line\fR or Program\fB name\fR
+Display the command line used to start a task or the name of the associated
+program.
+You toggle between command\fI line\fR and\fI name\fR with 'c', which is both
+a \*(CO and an \*(CI.
+
+When you've chosen to display command lines, processes without a command
+line (like kernel threads) will be shown with only the program name in
+parentheses, as in this example:
+ \fR( mdrecoveryd )
+
+Either form of display is subject to potential truncation if it's too long to
+fit in this field's current width.
+That width depends upon other fields selected, their order and the current
+screen width.
+
+\*(NT The 'Command' field/column is unique, in that it is not fixed-width.
+When displayed, this column will be allocated all remaining screen width (up
+to the maximum 512 characters) to provide for the potential growth of program
+names into command lines.
+
+.TP 3
+y:\fB WCHAN\fR \*(EM Sleeping in Function
+Depending on the availability of the kernel link map ('System.map'), this field
+will show the name or the address of the kernel function in which the task is
+currently sleeping.
+Running tasks will display a dash ('-') in this column.
+
+\*(NT By displaying this field, \*(Me's own working set will be increased by
+over 700Kb.
+Your only means of reducing that overhead will be to stop and restart \*(Me.
+
+.TP 3
+z:\fB Flags\fR \*(EM Task Flags
+This column represents the task's current scheduling flags which are
+expressed in hexadecimal notation and with zeros suppressed.
+These flags are officially documented in <linux/sched.h>.
+Less formal documentation can also be found on the 'Fields select'
+and 'Order fields' screens.
+
+.\" ......................................................................
+.SS 2b. SELECTING and ORDERING Columns
+.\" ----------------------------------------------------------------------
+After pressing the \*(CIs 'f' (Fields select) or \'o' (Order fields) you will
+be shown a screen containing the current \fBfields string\fR followed by names
+and descriptions for all fields.
+
+Here is a sample\fB fields string\fR from one of \*(Me's four windows/field
+groups and an explanation of the conventions used:
+.Jbu
+Sample fields string:
+ \fIANOPQRSTUVXbcdefgjlmyzWHIK\fR
+.Jbu
+The order of displayed fields corresponds to the order of the letters
+in that string.
+.Jbu
+If the letter is\fI upper case\fR the corresponding field itself will
+then be shown as part of the \*(TD (screen width permitting).
+This will also be indicated by a leading \*(AS, as in this excerpt:
+ \fR...
+ \fB* K: %CPU = CPU usage
+ \fR l: TIME = CPU Time
+ \fR m: TIME+ = CPU Time, hundredths
+ \fB* N: %MEM = Memory usage (RES)
+ \fB* O: VIRT = Virtual Image (kb)
+ \fR...
+
+.TP
+.B Fields select\fR screen \*(EM the 'f' \*(CI
+You\fI toggle\fR the\fB display\fR of a field by simply pressing the
+corresponding letter.
+
+.TP
+.B Order fields\fR screen \*(EM the 'o' \*(CI
+You\fI move\fR a field to the\fB left\fR by pressing the corresponding\fB
+upper case\fR letter and to the\fB right\fR with the\fB lower case\fR
+letter.
+
+
+.\" ----------------------------------------------------------------------
+.SH 3. INTERACTIVE Commands
+.\" ----------------------------------------------------------------------
+Listed below is a brief index of commands within categories.
+Some commands appear more than once \*(EM their meaning or scope may vary
+depending on the context in which they are issued.
+
+ 3a.\fI GLOBAL_Commands\fR
+ <Ret/Sp> ?, =, A, B, d, G, h, I, k, q, r, s, W, Z
+ 3b.\fI SUMMARY_Area_Commands\fR
+ l, m, t, 1
+ 3c.\fI TASK_Area_Commands\fR
+ Appearance: b, x, y, z
+ Content: c, f, H, o, S, u
+ Size: #, i, n
+ Sorting: <, >, F, O, R
+ 3d.\fI COLOR_Mapping\fR
+ <Ret>, a, B, b, H, M, q, S, T, w, z, 0 - 7
+ 4b.\fI COMMANDS_for_Windows\fR
+ -, _, =, +, A, a, G, g, w
+
+.\" ......................................................................
+.SS 3a. GLOBAL Commands
+The global \*(CIs are\fB always\fR available\fR in both \*(FM and \*(AM.
+However, some of these \*(CIs are\fB not available\fR when running
+in 'Secure mode'.
+
+If you wish to know in advance whether or not your \*(Me has been secured,
+simply ask for help and view the system summary on the second line.
+
+.TP 7
+\ \ \<\fBEnter\fR> or <\fBSpace\fR> :\fIRefresh_Display\fR
+These commands do nothing, they are simply ignored.
+However, they will awaken \*(Me and following receipt of any input
+the entire display will be repainted.
+
+Use either of these keys if you have a large delay interval and wish to
+see current status,
+
+.TP 7
+\ \ \'\fB?\fR\' or \'\fBh\fR\' :\fIHelp\fR
+There are two help levels available.
+The first will provide a reminder of all the basic \*(CIs.
+If \*(Me is\fI secured\fR, that screen will be abbreviated.
+
+Typing 'h' or '?' on that help screen will take you to help for those \*(CIs
+applicable to \*(AM.
+
+.TP 7
+\ \ \'\fB=\fR\' :\fIExit_Task_Limits\fR
+Removes restrictions on which tasks are shown.
+This command will reverse any 'i' (idle tasks) and 'n' (max tasks) commands
+that might be active.
+It also provides for an 'exit' from PID monitoring.
+See the '-p' \*(CO for a discussion of PID monitoring.
+
+When operating in \*(AM this command has a slightly broader meaning.
+
+.TP 7
+\ \ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR
+This command will switch between \*(FM and \*(AM.
+\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into
+\*(CWs and field groups.
+
+.TP 7
+\ \ \'\fBB\fR\' :\fIBold_Disable/Enable_toggle\fR
+This command will influence use of the 'bold' terminfo capability and
+alters\fB both\fR the \*(SA and \*(TA for the \*(CW.
+While it is intended primarily for use with dumb terminals, it can be
+applied anytime.
+
+\*(NT When this toggle is \*O and \*(Me is operating in monochrome mode,
+the\fB entire display\fR will appear as normal text.
+Thus, unless the 'x' and/or 'y' toggles are using reverse for emphasis,
+there will be no visual confirmation that they are even on.
+
+.TP 7
+*\ \'\fBd\fR\' or \'\fBs\fR\' :\fIChange_Delay_Time_interval\fR
+You will be prompted to enter the delay time, in seconds, between
+display updates.
+
+Fractional seconds are honored, but a negative number is not allowed.
+Entering 0 causes (nearly) continuous updates, with an unsatisfactory
+display as the system and tty driver try to keep up with \*(Me's demands.
+The delay value is inversely proportional to system loading,
+so set it with care.
+
+If at any time you wish to know the current delay time, simply ask for help
+and view the system summary on the second line.
+
+.TP 7
+\ \ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR
+You will be prompted to enter a number between 1 and 4 designating the
+window/field group which should be made the \*(CW.
+You will soon grow comfortable with these 4 windows, especially after
+experimenting with \*(AM.
+
+.TP 7
+\ \ \'\fBI\fR\' :\fIIrix/Solaris_Mode_toggle\fR
+When operating in 'Solaris mode' ('I' toggled \*F), a task's \*(Pu usage
+will be divided by the total number of \*(PUs.
+After issuing this command, you'll be informed of the new state of this toggle.
+
+.TP 7
+\ \ \'\fBu\fR\' :\fIselect a user\fR
+You will be prompted for a UID or username. Only processes
+belonging to the selected user will be displayed. This option
+matches on the effective UID.
+
+.TP 7
+\ \ \'\fBU\fR\' :\fIselect a user\fR
+You will be prompted for a UID or username. Only processes
+belonging to the selected user will be displayed. This option
+matches on the real, effective, saved, and filesystem UID.
+
+.TP 7
+*\ \'\fBk\fR\' :\fIKill_a_task\fR
+You will be prompted for a PID and then the signal to send.
+The default signal, as reflected in the prompt, is SIGTERM.
+However, you can send any signal, via number or name.
+
+If you wish to abort the kill process, do one of the following
+depending on your progress:
+ 1) at the pid prompt, just press <Enter>
+ 2) at the signal prompt, type 0
+
+.TP 7
+\ \ \'\fBq\fR\' :\fIQuit\fR
+
+.TP 7
+*\ \'\fBr\fR\' :\fIRenice_a_Task\fR
+You will be prompted for a PID and then the value to nice it to.
+Entering a positive value will cause a process to lose priority.
+Conversely, a negative value will cause a process to be viewed more
+favorably by the kernel.
+
+.TP 7
+\ \ \'\fBW\fR\' :\fIWrite_the_Configuration_File\fR
+This will save all of your options and toggles plus the current
+display mode and delay time.
+By issuing this command just before quitting \*(Me, you will be able restart
+later in exactly that same state.
+
+.TP 7
+\ \ \'\fBZ\fR\' :\fIChange_Color_Mapping
+This key will take you to a separate screen where you can change the
+colors for the \*(CW, or for all windows.
+For details regarding this \*(CI \*(Xt 3d. COLOR Mapping.
+
+.IP "*" 3
+The commands shown with an \*(AS are not available in 'Secure mode',
+nor will they be shown on the level-1 help screen.
+
+.\" ......................................................................
+.SS 3b. SUMMARY Area Commands
+The \*(SA \*(CIs are\fB always available\fR in both \*(FM and \*(AM.
+They affect the beginning lines of your display and will determine the position
+of messages and prompts.
+
+These commands always impact just the \*(CW/field group.
+\*(XT 4. ALTERNATE\-DISPLAY Mode and the 'G' \*(CI for insight into
+\*(CWs and field groups.
+
+.TP 7
+\ \ \'\fBl\fR\' :\fIToggle_Load_Average/Uptime\fR \*(EM On/Off
+This is also the line containing the program name (possibly an alias) when
+operating in \*(FM or the \*(CW name when operating in \*(AM.
+
+.TP 7
+\ \ \'\fBm\fR\' :\fIToggle_Memory/Swap_Usage\fR \*(EM On/Off
+This command affects two \*(SA lines.
+
+.TP 7
+\ \ \'\fBt\fR\' :\fIToggle_Task/Cpu_States\fR \*(EM On/Off
+This command affects from 2 to many \*(SA lines, depending on the state
+of the '1' toggle and whether or not \*(Me is running under true SMP.
+
+.TP 7
+\ \ \'\fB1\fR\' :\fIToggle_Single/Separate_Cpu_States\fR \*(EM On/Off
+This command affects how the 't' command's Cpu States portion is shown.
+Although this toggle exists primarily to serve massively-parallel SMP machines,
+it is not restricted to solely SMP environments.
+
+When you see 'Cpu(s):' in the \*(SA, the '1' toggle is \*O and all
+\*(Pu information is gathered in a single line.
+Otherwise, each \*(Pu is displayed separately as: 'Cpu0, Cpu1, ...'
+
+.PP
+\*(NT If the entire \*(SA has been toggled \*F for any window, you would be left
+with just the\fB message line\fR.
+In that way, you will have maximized available task rows but (temporarily)
+sacrificed the program name in \*(FM or the \*(CW name when in \*(AM.
+
+.\" ......................................................................
+.SS 3c. TASK Area Commands
+The \*(TA \*(CIs are\fB always\fR available in \*(FM.
+
+The \*(TA \*(CIs are\fB never available\fR in \*(AM\fI if\fR the \*(CW's
+\*(TD has been toggled \*F (\*(Xt 4. ALTERNATE\-DISPLAY Mode).
+
+.PP
+.\" .........................
+.B APPEARANCE\fR of \*(TW
+.br
+.in +2
+The following commands will also be influenced by the state of the
+global 'B' (bold disable) toggle.
+.in
+
+.TP 7
+\ \ \'\fBb\fR\' :\fIBold/Reverse_toggle\fR
+This command will impact how the 'x' and 'y' toggles are displayed.
+Further, it will only be available when at least one of those toggles is \*O.
+
+.TP 7
+\ \ \'\fBx\fR\' :\fIColumn_Highlight_toggle\fR
+Changes highlighting for the current sort field.
+You probably don't need a constant visual reminder of the sort field and
+\*(Me hopes that you always run with 'column highlight' \*F, due to the cost
+in path-length.
+
+If you forget which field is being sorted this command can serve as a quick
+visual reminder.
+
+.TP 7
+\ \ \'\fBy\fR\' :\fIRow_Highlight_toggle\fR
+Changes highlighting for "running" tasks.
+For additional insight into this task state, \*(Xt 2a. DESCRIPTIONS of Fields,
+Process Status.
+
+Use of this provision provides important insight into your system's health.
+The only costs will be a few additional tty escape sequences.
+
+.TP 7
+\ \ \'\fBz\fR\' :\fIColor/Monochrome_toggle\fR
+Switches the \*(CW between your last used color scheme and the older form
+of black-on-white or white-on-black.
+This command will alter\fB both\fR the \*(SA and \*(TA but does not affect the
+state of the 'x', 'y' or 'b' toggles.
+
+.PP
+.\" .........................
+.B CONTENT\fR of \*(TW
+.PD 0
+.TP 7
+\ \ \'\fBc\fR\' :\fICommand_Line/Program_Name_toggle\fR
+This command will be honored whether or not the 'Command' column
+is currently visible.
+Later, should that field come into view, the change you applied will be seen.
+
+.TP 7
+\ \ \'\fBf\fR\' and \'\fBo\fR\' :\fIFields_select\fR or \fIOrder_fields\fR
+These keys display separate screens where you can change which
+fields are displayed and their order.
+For additional information on these \*(CIs
+\*(Xt 2b. SELECTING and ORDERING Columns.
+
+.TP 7
+\ \ \'\fBH\fR\' :\fIThreads_toggle\fR
+When this toggle is \*O, all individual threads will be displayed. Otherwise, \*(Me displays a summation of all threads in a process.
+
+.TP 7
+\ \ \'\fBS\fR\' :\fICumulative_Time_Mode_toggle\fR
+When 'Cumulative mode' is \*O, each process is listed with the \*(Pu
+time that it and its dead children have used.
+
+When \*F, programs that fork into many separate tasks will appear
+less demanding.
+For programs like 'init' or a shell this is appropriate but for others,
+like compilers, perhaps not.
+Experiment with two \*(TWs sharing the same sort field but with different 'S'
+states and see which representation you prefer.
+
+After issuing this command, you'll be informed of the new state of this toggle.
+If you wish to know in advance whether or not 'Cumulative mode' is in
+effect, simply ask for help and view the window summary on the second line.
+
+.TP 7
+\ \ \'\fBu\fR\' :\fIShow_Specific_User_Only\fR
+You will be prompted to enter the name of the user to display.
+Thereafter, in that \*(TW only matching User ID's will be shown, or possibly
+no tasks will be shown.
+
+Later, if you wish to monitor all tasks again, re-issue this command but
+just press <Enter> at the prompt, without providing a name.
+
+.PP
+.\" .........................
+.B SIZE\fR of \*(TW
+.PD 0
+.TP 7
+\ \ \'\fBi\fR\' :\fIIdle_Processes_toggle\fR
+Displays all tasks or just active tasks.
+When this toggle is \*F, idled or zombied processes will not be displayed.
+
+If this command is applied to the last \*(TD when in \*(AM, then it will not
+affect the window's size, as all prior \*(TDs will have already been painted.
+
+.TP 7
+\ \ \'\fBn\fR\' or \'#\' :\fISet_Maximum_Tasks\fR
+You will be prompted to enter the number of tasks to display.
+The lessor of your number and available screen rows will be used.
+
+When used in \*(AM, this is the command that gives you precise control over
+the size of each currently visible \*(TD, except for the very last.
+It will not affect the last window's size, as all prior \*(TDs will have
+already been painted.
+
+\*(NT If you wish to increase the size of the last visible \*(TD when in \*(AM,
+simply decrease the size of the \*(TD(s) above it.
+
+.PP
+.\" .........................
+.B SORTING\fR of \*(TW
+.br
+.in +2
+For compatibility, this \*(Me supports most of the former \*(Me sort keys.
+Since this is primarily a service to former \*(Me users, these commands do
+not appear on any help screen.
+ command sorted field supported
+ A start time (non-display) No
+ M %MEM Yes
+ N PID Yes
+ P %CPU Yes
+ T TIME+ Yes
+
+Before using any of the following sort provisions, \*(Me suggests that you
+temporarily turn on column highlighting using the 'x' \*(CI.
+That will help ensure that the actual sort environment matches your intent.
+
+The following \*(CIs will\fB only\fR be honored when the
+current sort field is\fB visible\fR.
+The sort field might\fI not\fR be visible because:
+ 1) there is insufficient\fI Screen Width\fR
+ 2) the 'f' \*(CI turned it \*F
+.in
+
+.TP 7
+\ \ \'\fB<\fR\' :\fIMove_Sort_Field_Left\fR
+Moves the sort column to the left unless the current sort field is
+the first field being displayed.
+
+.TP 7
+\ \ \'\fB>\fR\' :\fIMove_Sort_Field_Right\fR
+Moves the sort column to the right unless the current sort field is
+the last field being displayed.
+
+.PP
+.in +2
+The following \*(CIs will\fB always\fR be honored whether or not
+the current sort field is visible.
+.in
+
+.TP 7
+\ \ \'\fBF\fR\' or \'\fBO\fR\' :\fISelect_Sort_Field\fR
+These keys display a separate screen where you can change which field
+is used as the sort column.
+
+If a field is selected which was not previously being displayed, it will
+be forced \*O when you return to the \*(Me display.
+However, depending upon your screen width and the order of your fields,
+this sort field may not be displayable.
+
+This \*(CI can be a convenient way to simply verify the current sort field,
+when running \*(Me with column highlighting turned \*F.
+
+.TP 7
+\ \ \'\fBR\fR\' :\fIReverse/Normal_Sort_Field_toggle\fR
+Using this \*(CI you can alternate between high-to-low and low-to-high sorts.
+
+.PP
+.in +2
+\*(NT Field sorting uses internal values, not those in column display.
+Thus, the TTY and WCHAN fields will violate strict ASCII collating sequence.
+.in
+
+.\" ......................................................................
+.SS 3d. COLOR Mapping
+When you issue the 'Z' \*(CI, you will be presented with a separate screen.
+That screen can be used to change the colors in just the \*(CW or
+in all four windows before returning to the \*(Me display.
+
+.P
+.B Available \*(CIs
+ \fB4\fR upper case letters to select a\fB target\fR
+ \fB8\fR numbers to select a\fB color\fR
+ normal toggles available\fR
+ 'B' :bold disable/enable
+ 'b' :running tasks "bold"/reverse
+ 'z' :color/mono
+ other commands available\fR
+ 'a'/'w' :apply, then go to next/prior
+ <Enter> :apply and exit
+ 'q' :abandon current changes and exit
+
+If your use 'a' or 'w' to cycle the targeted window, you will
+have applied the color scheme that was displayed when you left that window.
+You can, of course, easily return to any window and reapply different
+colors or turn colors \*F completely with the 'z' toggle.
+
+The Color Mapping screen can also be used to change the \*(CW/field group
+in either \*(FM or \*(AM.
+Whatever was targeted when 'q' or <Enter> was pressed will be made current
+as you return to the \*(Me display.
+
+
+.\" ----------------------------------------------------------------------
+.SH 4. ALTERNATE\-DISPLAY Mode
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS 4a. WINDOWS Overview
+.TP
+.B Field Groups/Windows\fR:
+.br
+In \*(FM there is a single window represented by the entire screen.
+That single window can still be changed to display 1 of 4 different\fB field
+groups\fR (\*(Xc 'G' \*(CI, repeated below).
+Each of the 4 field groups has a unique separately configurable\fB \*(SA\fR
+and its own configurable\fB \*(TA\fR.
+
+In \*(AM, those 4 underlying field groups can now be made visible
+simultaneously, or can be turned \*F individually at your command.
+
+The \*(SA will always exist, even if it's only the message line.
+At any given time only\fI one\fR \*(SA can be displayed.
+However, depending on your commands, there could be from\fI zero\fR
+to\fI four\fR separate \*(TDs currently showing on the screen.
+
+.TP
+.B Current Window\fR:
+.br
+The \*(CW is the window associated with the \*(SA and the window to which
+task related commands are always directed.
+Since in \*(AM you can toggle the \*(TD \*F, some commands might be
+restricted for the \*(CW.
+
+A further complication arises when you have toggled the first \*(SA
+line \*F.
+With the loss of the window name (the 'l' toggled line), you'll not easily
+know what window is the \*(CW.
+
+.\" ......................................................................
+.SS 4b. COMMANDS for Windows
+.TP 7
+\ \ \'\fB-\fR\' and \'\fB_\fR\' :\fIShow/Hide_Window(s)_toggles\fR
+The '-' key turns the \*(CW's \*(TD \*O and \*F.
+When \*O, that \*(TA will show a minimum of the columns header you've
+established with the 'f' and 'o' commands.
+It will also reflect any other \*(TA options/toggles you've applied yielding
+zero or more tasks.
+
+The '_' key does the same for all \*(TDs.
+In other words, it switches between the currently visible \*(TD(s) and any
+\*(TD(s) you had toggled \*F.
+If all 4 \*(TDs are currently visible, this \*(CI will leave the \*(SA
+as the only display element.
+
+.TP 7
+*\ \'\fB=\fR\' and \'\fB+\fR\' :\fIEqualize_(re-balance)_Window(s)\fR
+The '=' key forces the \*(CW's \*(TD to be visible.
+It also reverses any 'i' (idle tasks) and 'n' (max tasks) commands that might
+be active.
+
+The '+' key does the same for all windows.
+The four \*(TDs will reappear, evenly balanced.
+They will also have retained any customizations you had previously applied,
+except for the 'i' (idle tasks) and 'n' (max tasks) commands.
+
+.TP 7
+*\ \'\fBA\fR\' :\fIAlternate_Display_Mode_toggle\fR
+This command will switch between \*(FM and \*(AM.
+
+The first time you issue this command, all four \*(TDs will be shown.
+Thereafter when you switch modes, you will see only the \*(TD(s) you've
+chosen to make visible.
+
+.TP 7
+*\ \'\fBa\fR\' and \'\fBw\fR\' :\fINext_Window_Forward/Backward\fR
+This will change the \*(CW, which in turn changes the window to which
+commands are directed.
+These keys act in a circular fashion so you can reach any desired \*(CW
+using either key.
+
+Assuming the window name is visible (you have not toggled 'l' \*F),
+whenever the \*(CW name loses its emphasis/color, that's a reminder
+the \*(TD is \*F and many commands will be restricted.
+
+.TP 7
+*\ \'\fBG\fR\' :\fIChoose_Another_Window/Field_Group\fR
+You will be prompted to enter a number between 1 and 4 designating the
+window/field group which should be made the \*(CW.
+
+In \*(FM, this command is necessary to alter the \*(CW.
+In \*(AM, it is simply a less convenient alternative to the 'a' and 'w'
+commands.
+
+.TP 7
+\ \ \'\fBg\fR\' :\fIChange_Window/Field_Group_Name\fR
+You will be prompted for a new name to be applied to the \*(CW.
+It does not require that the window name be visible
+(the 'l' toggle to be \*O).
+
+.IP "*" 3
+The \*(CIs shown with an \*(AS have use beyond \*(AM.
+ \'=', 'A', 'G' are always available
+ \'a', 'w' act the same when color mapping
+
+
+.\" ----------------------------------------------------------------------
+.SH 5. FILES
+.\" ----------------------------------------------------------------------
+.\" ......................................................................
+.SS 5a. SYSTEM Configuration File
+The presence of this file will influence which version of the 'help' screen
+is shown to an ordinary user.
+More importantly, it will limit what ordinary users are allowed
+to do when \*(Me is running.
+They will not be able to issue the following commands.
+ k Kill a task
+ r Renice a task
+ d or s Change delay/sleep interval
+
+The system \*(CF is\fB not\fR created by \*(Me.
+Rather, you create this file manually and place it in the \fI/etc\fR
+directory.
+Its name must be 'toprc' and must have no leading '.' (period).
+It must have only two lines.
+
+Here is an example of the contents of\fI /etc/toprc\fR:
+ s # line 1: 'secure' mode switch
+ 5.0 # line 2: 'delay'\ \ interval in seconds
+
+.\" ......................................................................
+.SS 5b. PERSONAL Configuration File
+This file is written as '$HOME/.your-name-4-top' + 'rc'.
+Use the 'W' \*(CI to create it or update it.
+
+Here is the general layout:
+ global # line 1: the program name/alias notation
+ " # line 2: id,altscr,irixps,delay,curwin
+ per ea # line a: winname,fieldscur
+ window # line b: winflags,sortindx,maxtasks
+ " # line c: summclr,msgsclr,headclr,taskclr
+
+If the $HOME variable is not present, \*(Me will try to write the
+personal \*(CF to the current directory, subject to permissions.
+
+
+.\" ----------------------------------------------------------------------
+.SH 6. STUPID TRICKS Sampler
+.\" ----------------------------------------------------------------------
+Many of these 'tricks' work best when you give \*(Me a scheduling boost.
+So plan on starting him with a nice value of -10, assuming you've got
+the authority.
+
+.\" ......................................................................
+.SS 6a. Kernel Magic
+.\" sorry, just can't help it -- don't ya love the sound of this?
+For these stupid tricks, \*(Me needs \*(FM.
+.\" ( apparently AM static was a potential concern )
+
+.New
+The user interface, through prompts and help, intentionally implies
+that the delay interval is limited to tenths of a second.
+However, you're free to set any desired delay.
+If you want to see Linux at his scheduling best, try a delay of .09
+seconds or less.
+
+For this experiment, under x-windows open an xterm and maximize it.
+Then do the following:
+ . provide a scheduling boost and tiny delay via:
+ nice -n -10 top -d.09
+ . keep sorted column highlighting \*F to minimize
+ path length
+ . turn \*O reverse row highlighting for emphasis
+ . try various sort columns (TIME/MEM work well),
+ and normal or reverse sorts to bring the most
+ active processes into view
+
+What you'll see is a very busy Linux doing what he's always done for you,
+but there was no program available to illustrate this.
+
+.New
+Under an xterm using 'white-on-black' colors, try setting \*(Me's task color
+to black and be sure that task highlighting is set to bold, not reverse.
+Then set the delay interval to around .3 seconds.
+
+After bringing the most active processes into view, what you'll see are
+the ghostly images of just the currently running tasks.
+
+.New
+Delete the existing rcfile, or create a new symlink.
+Start this new version then type 'T' (a secret key, \*(Xt 3c. TASK Area
+Commands, Sorting) followed by 'W' and 'q'.
+Finally, restart the program with -d0 (zero delay).
+
+Your display will be refreshed at three times the rate of the former \*(Me,
+a 300% speed advantage.
+As \*(Me climbs the TIME ladder, be as patient as you can while speculating
+on whether or not \*(Me will ever reach the \*(Me.
+
+.\" ......................................................................
+.SS 6b. Bouncing Windows
+For these stupid tricks, \*(Me needs \*(AM.
+
+.New
+With 3 or 4 \*(TDs visible, pick any window other than the last
+and turn idle processes \*F.
+Depending on where you applied 'i', sometimes several \*(TDs are bouncing and
+sometimes it's like an accordion, as \*(Me tries his best to allocate space.
+
+.New
+Set each window's summary lines differently: one with no memory; another with
+no states; maybe one with nothing at all, just the message line.
+Then hold down 'a' or 'w' and watch a variation on bouncing windows \*(EM
+hopping windows.
+
+.New
+Display all 4 windows and for each, in turn, set idle processes to \*F.
+You've just entered the "extreme bounce" zone.
+
+.\" ......................................................................
+.SS 6c. The Big Bird Window
+This stupid trick also requires \*(AM.
+
+.New
+Display all 4 windows and make sure that 1:Def is the \*(CW.
+Then, keep increasing window size until the all the other \*(TDs
+are "pushed out of the nest".
+
+When they've all been displaced, toggle between all visible/invisible windows.
+Then ponder this:
+.br
+ is \*(Me fibbing or telling honestly your imposed truth?
+
+
+.\" ----------------------------------------------------------------------
+.SH 7. BUGS
+.\" ----------------------------------------------------------------------
+Send bug reports to:
+ Albert D\. Cahalan, <albert@users.sf.net>
+
+
+.\" ----------------------------------------------------------------------
+.SH 8. HISTORY Former top
+.\" ----------------------------------------------------------------------
+The original top was written by Roger Binns,
+based on Branko Lankester's <lankeste@fwi.uva.nl> ps program.
+
+Robert Nation <nation@rocket.sanders.lockheed.com>
+adapted it for the proc file system.
+
+Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+added support for configurable fields.
+
+Plus many other individuals contributed over the years.
+
+
+.\" ----------------------------------------------------------------------
+.SH 9. AUTHOR
+.\" ----------------------------------------------------------------------
+This entirely new and enhanced replacement was written by:
+ Jim / James C. Warner, <warnerjc@worldnet.att.net>
+.ig
+ ( as a means to learn Linux, can you believe it? )
+ ( & he accidentally learned a little groff, too! )
+..
+
+With invaluable help from:
+ Albert D\. Cahalan, <albert@users.sf.net>
+ Craig Small, <csmall@small.dropbear.id.au>
+
+.ig
+.rj 2
+.B -*-\fR few though they are, some yet believe\fB -*-\fR
+.B -*-\~\~\~\~\~\~\~\fRin-the-\fBart\fR-of-programming\~\~\~\~\~\~\~\fB-*-\fR
+..
+
+.\" ----------------------------------------------------------------------
+.SH 10. SEE ALSO
+.\" ----------------------------------------------------------------------
+.BR free (1),
+.BR ps (1),
+.BR uptime (1),
+.BR atop (1),
+.BR slabtop (1),
+.BR vmstat (8),
+.BR w (1).
+
+
+.\" ----------------------------------------------------------------------
+.ig
+.rj 1
+\-*-
+.PD
+.in -3
+Copyright (c) 2002 \*(EM JC Warner & Associates, Ltd.
+
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.1 or
+any later version published by the Free Software Foundation;
+with no Front-Cover Texts, no Back-Cover Texts, and with the following
+Invariant Sections and any sub-sections therein:
+.na
+.hy 0
+.in +3
+STUPID\ TRICKS\ Sampler;
+.br
+AUTHOR
+.in
+A copy of the license is included in the section entitled
+\(dqGNU Free Documentation License\(dq.
+..
+.
+.\" end: active doc ||||||||||||||||||||||||||||||||||||||||||||||||||
+.\" ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+.ig
+.\" ----------------------------------------------------------------------
+.SH GNU Free Documentation License
+Version 1.1, March 2000
+
+Copyright (C) 2000 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+.SS 0. PREAMBLE
+The purpose of this License is to make a manual, textbook, or other
+written document "free" in the sense of freedom: to assure everyone
+the effective freedom to copy and redistribute it, with or without
+modifying it, either commercially or noncommercially. Secondarily,
+this License preserves for the author and publisher a way to get
+credit for their work, while not being considered responsible for
+modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+.SS 1. APPLICABILITY AND DEFINITIONS
+This License applies to any manual or other work that contains a
+notice placed by the copyright holder saying it can be distributed
+under the terms of this License. The "Document", below, refers to any
+such manual or work. Any member of the public is a licensee, and is
+addressed as "you".
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (For example, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, whose contents can be viewed and edited directly and
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup has been designed to thwart or discourage
+subsequent modification by readers is not Transparent. A copy that is
+not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML designed for human modification. Opaque formats include
+PostScript, PDF, proprietary formats that can be read and edited only
+by proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML produced by some word processors for output
+purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+.SS 2. VERBATIM COPYING
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+.SS 3. COPYING IN QUANTITY
+If you publish printed copies of the Document numbering more than 100,
+and the Document's license notice requires Cover Texts, you must enclose
+the copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a publicly-accessible computer-network location containing a complete
+Transparent copy of the Document, free of added material, which the
+general network-using public has access to download anonymously at no
+charge using public-standard network protocols. If you use the latter
+option, you must take reasonably prudent steps, when you begin
+distribution of Opaque copies in quantity, to ensure that this
+Transparent copy will remain thus accessible at the stated location
+until at least one year after the last time you distribute an Opaque
+copy (directly or through your agents or retailers) of that edition to
+the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+.SS 4. MODIFICATIONS
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+.HP 3
+.B A\fR.\ Use in the Title Page (and on the covers, if any) a title distinct
+from that of the Document, and from those of previous versions (which should,
+if there were any, be listed in the History section of the Document).
+You may use the same title as a previous version if the original publisher of
+that version gives permission.
+.HP 3
+.B B\fR.\ List on the Title Page, as authors, one or more persons or entities
+responsible for authorship of the modifications in the Modified Version,
+together with at least five of the principal authors of the Document
+(all of its principal authors, if it has less than five).
+.HP 3
+.B C\fR.\ State on the Title page the name of the publisher of the Modified
+Version, as the publisher.
+.HP 3
+.B D\fR.\ Preserve all the copyright notices of the Document.
+.HP 3
+.B E\fR.\ Add an appropriate copyright notice for your modifications adjacent
+to the other copyright notices.
+.HP 3
+.B F\fR.\ Include, immediately after the copyright notices, a license notice
+giving the public permission to use the Modified Version under the terms of
+this License, in the form shown in the Addendum below.
+.HP 3
+.B G\fR.\ Preserve in that license notice the full lists of Invariant Sections
+and required Cover Texts given in the Document's license notice.
+.HP 3
+.B H\fR.\ Include an unaltered copy of this License.
+.HP 3
+.B I\fR.\ Preserve the section entitled "History", and its title, and add to it
+an item stating at least the title, year, new authors, and publisher of the
+Modified Version as given on the Title Page.
+If there is no section entitled "History" in the Document, create one stating
+the title, year, authors, and publisher of the Document as given on its Title
+Page, then add an item describing the Modified Version as stated in the
+previous sentence.
+.HP 3
+.B J\fR.\ Preserve the network location, if any, given in the Document for
+public access to a Transparent copy of the Document, and likewise the network
+locations given in the Document for previous versions it was based on.
+These may be placed in the "History" section.
+You may omit a network location for a work that was published at least four
+years before the Document itself, or if the original publisher of the version
+it refers to gives permission.
+.HP 3
+.B K\fR.\ In any section entitled "Acknowledgements" or "Dedications", preserve
+the section's title, and preserve in the section all the substance and tone of
+each of the contributor acknowledgements and/or dedications given therein.
+.HP 3
+.B L\fR.\ Preserve all the Invariant Sections of the Document, unaltered in their
+text and in their titles.
+Section numbers or the equivalent are not considered part of the section titles.
+.HP 3
+.B M\fR.\ Delete any section entitled "Endorsements".
+Such a section may not be included in the Modified Version.
+.HP 3
+.B N\fR.\ Do not retitle any existing section as "Endorsements" or to conflict
+in title with any Invariant Section.
+
+.PP
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+.SS 5. COMBINING DOCUMENTS
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections entitled "History"
+in the various original documents, forming one section entitled
+"History"; likewise combine any sections entitled "Acknowledgements",
+and any sections entitled "Dedications". You must delete all sections
+entitled "Endorsements."
+
+.SS 6. COLLECTIONS OF DOCUMENTS
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+.SS 7. AGGREGATION WITH INDEPENDENT WORKS
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, does not as a whole count as a Modified Version
+of the Document, provided no compilation copyright is claimed for the
+compilation. Such a compilation is called an "aggregate", and this
+License does not apply to the other self-contained works thus compiled
+with the Document, on account of their being thus compiled, if they
+are not themselves derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one quarter
+of the entire aggregate, the Document's Cover Texts may be placed on
+covers that surround only the Document within the aggregate.
+Otherwise they must appear on covers around the whole aggregate.
+
+.SS 8. TRANSLATION
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License provided that you also include the
+original English version of this License. In case of a disagreement
+between the translation and the original English version of this
+License, the original English version will prevail.
+
+.SS 9. TERMINATION
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+.SS 10. FUTURE REVISIONS OF THIS LICENSE
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+.SS ADDENDUM: How to use this License for your documents
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+.IP "" 3
+Copyright (c) YEAR YOUR NAME.
+
+Permission is granted to copy, distribute and/or modify this document under the
+terms of the GNU Free Documentation License, Version 1.1 or any later version
+published by the Free Software Foundation;\ \ with the Invariant Sections being
+LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the
+Back-Cover Texts being LIST.
+A copy of the license is included in the section entitled "GNU
+Free Documentation License".
+
+If you have no Invariant Sections, write "with no Invariant Sections"
+instead of saying which ones are invariant. If you have no
+Front-Cover Texts, write "no Front-Cover Texts" instead of
+"Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
+.\" ----------------------------------------------------------------------
+.SH \fRend of\fB GNU Free Documentation License
+.IP ""
+.PP
+..
+.\" end: gfdl license ||||||||||||||||||||||||||||||||||||||||||||||||
+.\" ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
--- /dev/null
+/* top.c - Source file: show Linux processes */
+/*
+ * Copyright (c) 2002, by: James C. Warner
+ * All rights reserved. 8921 Hilloway Road
+ * Eden Prairie, Minnesota 55347 USA
+ * <warnerjc@worldnet.att.net>
+ *
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * For their contributions to this program, the author wishes to thank:
+ * Albert D. Cahalan, <albert@users.sf.net>
+ * Craig Small, <csmall@small.dropbear.id.au>
+ *
+ * Changes by Albert Cahalan, 2002-2004.
+ */
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <curses.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Foul POS defines all sorts of stuff...
+#include <term.h>
+#undef tab
+
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <values.h>
+
+#include "proc/devname.h"
+#include "proc/wchan.h"
+#include "proc/procps.h"
+#include "proc/readproc.h"
+#include "proc/escape.h"
+#include "proc/sig.h"
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+#include "proc/whattime.h"
+
+#include "top.h"
+
+/*###### Miscellaneous global stuff ####################################*/
+
+ /* The original and new terminal attributes */
+static struct termios Savedtty,
+ Rawtty;
+static int Ttychanged = 0;
+
+ /* Program name used in error messages and local 'rc' file name */
+static char *Myname;
+
+ /* Name of user config file (dynamically constructed) and our
+ 'Current' rcfile contents, initialized with defaults but may be
+ overridden with the local rcfile (old or new-style) values */
+static char Rc_name [OURPATHSZ];
+static RCF_t Rc = DEF_RCFILE;
+
+ /* The run-time acquired page size */
+static unsigned Page_size;
+static unsigned page_to_kb_shift;
+
+ /* SMP Irix/Solaris mode */
+static int Cpu_tot;
+static double pcpu_max_value; // usually 99.9, for %CPU display
+ /* assume no IO-wait stats, overridden if linux 2.5.41 */
+static const char *States_fmts = STATES_line2x4;
+
+ /* Specific process id monitoring support */
+static pid_t Monpids [MONPIDMAX] = { 0 };
+static int Monpidsidx = 0;
+
+ /* A postponed error message */
+static char Msg_delayed [SMLBUFSIZ];
+static int Msg_awaiting = 0;
+
+// This is the select() timeout. Clear it in sig handlers to avoid a race.
+// (signal happens just as we are about to select() and thus does not
+// break us out of the select(), causing us to delay until timeout)
+static volatile struct timeval tv;
+#define ZAP_TIMEOUT do{tv.tv_usec=0; tv.tv_sec=0;}while(0);
+
+ /* Configurable Display support ##################################*/
+
+ /* Current screen dimensions.
+ note: the number of processes displayed is tracked on a per window
+ basis (see the WIN_t). Max_lines is the total number of
+ screen rows after deducting summary information overhead. */
+ /* Current terminal screen size. */
+static int Screen_cols, Screen_rows, Max_lines;
+
+// set to 1 if writing to the last column would be troublesome
+// (we don't distinguish the lowermost row from the other rows)
+static int avoid_last_column;
+
+ /* This is really the number of lines needed to display the summary
+ information (0 - nn), but is used as the relative row where we
+ stick the cursor between frames. */
+static int Msg_row;
+
+ /* Global/Non-windows mode stuff that is NOT persistent */
+static int No_ksyms = -1, // set to '0' if ksym avail, '1' otherwise
+ PSDBopen = 0, // set to '1' if psdb opened (now postponed)
+ Batch = 0, // batch mode, collect no input, dumb output
+ Loops = -1, // number of iterations, -1 loops forever
+ Secure_mode = 0; // set if some functionality restricted
+
+ /* Some cap's stuff to reduce runtime calls --
+ to accomodate 'Batch' mode, they begin life as empty strings */
+static char Cap_clr_eol [CAPBUFSIZ],
+ Cap_clr_eos [CAPBUFSIZ],
+ Cap_clr_scr [CAPBUFSIZ],
+ Cap_rmam [CAPBUFSIZ],
+ Cap_smam [CAPBUFSIZ],
+ Cap_curs_norm [CAPBUFSIZ],
+ Cap_curs_huge [CAPBUFSIZ],
+ Cap_home [CAPBUFSIZ],
+ Cap_norm [CAPBUFSIZ],
+ Cap_reverse [CAPBUFSIZ],
+ Caps_off [CAPBUFSIZ];
+static int Cap_can_goto = 0;
+
+ /* Some optimization stuff, to reduce output demands...
+ The Pseudo_ guys are managed by wins_resize and frame_make. They
+ are exploited in a macro and represent 90% of our optimization.
+ The Stdout_buf is transparent to our code and regardless of whose
+ buffer is used, stdout is flushed at frame end or if interactive. */
+static char *Pseudo_scrn;
+static int Pseudo_row, Pseudo_cols, Pseudo_size;
+#ifndef STDOUT_IOLBF
+ // less than stdout's normal buffer but with luck mostly '\n' anyway
+static char Stdout_buf[2048];
+#endif
+
+
+ /* ////////////////////////////////////////////////////////////// */
+ /* Special Section: multiple windows/field groups ---------------*/
+
+ /* The pointers to our four WIN_t's, and which of those is considered
+ the 'current' window (ie. which window is associated with any summ
+ info displayed and to which window commands are directed) */
+static WIN_t Winstk [GROUPSMAX],
+ *Curwin;
+
+ /* Frame oriented stuff that can't remain local to any 1 function
+ and/or that would be too cumbersome managed as parms,
+ and/or that are simply more efficiently handled as globals
+ (first 2 persist beyond a single frame, changed infrequently) */
+static int Frames_libflags; // PROC_FILLxxx flags (0 = need new)
+//atic int Frames_maxcmdln; // the largest from the 4 windows
+static unsigned Frame_maxtask; // last known number of active tasks
+ // ie. current 'size' of proc table
+static unsigned Frame_running, // state categories for this frame
+ Frame_sleepin,
+ Frame_stopped,
+ Frame_zombied;
+static float Frame_tscale; // so we can '*' vs. '/' WHEN 'pcpu'
+static int Frame_srtflg, // the subject window's sort direction
+ Frame_ctimes, // the subject window's ctimes flag
+ Frame_cmdlin; // the subject window's cmdlin flag
+ /* ////////////////////////////////////////////////////////////// */
+
+\f
+/*###### Sort callbacks ################################################*/
+
+ /*
+ * These happen to be coded in the same order as the enum 'pflag'
+ * values. Note that 2 of these routines serve double duty --
+ * 2 columns each.
+ */
+
+SCB_NUMx(P_PID, XXXID)
+SCB_NUMx(P_PPD, ppid)
+SCB_STRx(P_URR, ruser)
+SCB_NUMx(P_UID, euid)
+SCB_STRx(P_URE, euser)
+SCB_STRx(P_GRP, egroup)
+SCB_NUMx(P_TTY, tty)
+SCB_NUMx(P_PRI, priority)
+SCB_NUMx(P_NCE, nice)
+SCB_NUMx(P_CPN, processor)
+SCB_NUM1(P_CPU, pcpu)
+ // also serves P_TM2 !
+static int sort_P_TME (const proc_t **P, const proc_t **Q)
+{
+ if (Frame_ctimes) {
+ if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
+ < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+ return SORT_lt;
+ if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
+ > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+ return SORT_gt;
+ } else {
+ if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
+ return SORT_lt;
+ if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
+ return SORT_gt;
+ }
+ return SORT_eq;
+}
+
+SCB_NUM1(P_VRT, size)
+SCB_NUM2(P_SWP, size, resident)
+SCB_NUM1(P_RES, resident) // also serves P_MEM !
+SCB_NUM1(P_COD, trs)
+SCB_NUM1(P_DAT, drs)
+SCB_NUM1(P_SHR, share)
+SCB_NUM1(P_FLT, maj_flt)
+SCB_NUM1(P_DRT, dt)
+SCB_NUMx(P_STA, state)
+
+static int sort_P_CMD (const proc_t **P, const proc_t **Q)
+{
+ /* if a process doesn't have a cmdline, we'll consider it a kernel thread
+ -- since displayed tasks are given special treatment, we must too */
+ if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) {
+ if (!(*Q)->cmdline) return Frame_srtflg * -1;
+ if (!(*P)->cmdline) return Frame_srtflg;
+ return Frame_srtflg *
+ strncmp((*Q)->cmdline[0], (*P)->cmdline[0], (unsigned)Curwin->maxcmdln);
+ }
+ // this part also handles the compare if both are kernel threads
+ return Frame_srtflg * strcmp((*Q)->cmd, (*P)->cmd);
+}
+
+SCB_NUM1(P_WCH, wchan)
+SCB_NUM1(P_FLG, flags)
+
+ /* ///////////////////////////////// special sort for prochlp() ! */
+static int sort_HST_t (const HST_t *P, const HST_t *Q)
+{
+ return P->pid - Q->pid;
+}
+
+
+/*###### Tiny useful routine(s) ########################################*/
+
+ /*
+ * This routine isolates ALL user INPUT and ensures that we
+ * wont be mixing I/O from stdio and low-level read() requests */
+static int chin (int ech, char *buf, unsigned cnt)
+{
+ int rc;
+
+ fflush(stdout);
+ if (!ech)
+ rc = read(STDIN_FILENO, buf, cnt);
+ else {
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ rc = read(STDIN_FILENO, buf, cnt);
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
+ }
+ // may be the beginning of a lengthy escape sequence
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ return rc; // note: we do NOT produce a vaid 'string'
+}
+
+
+// This routine simply formats whatever the caller wants and
+// returns a pointer to the resulting 'const char' string...
+static const char *fmtmk (const char *fmts, ...) __attribute__((format(printf,1,2)));
+static const char *fmtmk (const char *fmts, ...)
+{
+ static char buf[BIGBUFSIZ]; // with help stuff, our buffer
+ va_list va; // requirements exceed 1k
+
+ va_start(va, fmts);
+ vsnprintf(buf, sizeof(buf), fmts, va);
+ va_end(va);
+ return (const char *)buf;
+}
+
+
+// This guy is just our way of avoiding the overhead of the standard
+// strcat function (should the caller choose to participate)
+static inline char *scat (char *restrict dst, const char *restrict src)
+{
+ while (*dst) dst++;
+ while ((*(dst++) = *(src++)));
+ return --dst;
+}
+
+
+// Trim the rc file lines and any 'open_psdb_message' result which arrives
+// with an inappropriate newline (thanks to 'sysmap_mmap')
+static char *strim_0 (char *str)
+{
+ static const char ws[] = "\b\e\f\n\r\t\v\x9b"; // 0x9b is an escape
+ char *p;
+
+ if ((p = strpbrk(str, ws))) *p = 0;
+ return str;
+}
+
+
+// This guy just facilitates Batch and protects against dumb ttys
+// -- we'd 'inline' him but he's only called twice per frame,
+// yet used in many other locations.
+static const char *tg2 (int x, int y)
+{
+ return Cap_can_goto ? tgoto(cursor_address, x, y) : "";
+}
+
+
+/*###### Exit/Interrput routines #######################################*/
+
+// The usual program end -- called only by functions in this section.
+static void bye_bye (FILE *fp, int eno, const char *str) NORETURN;
+static void bye_bye (FILE *fp, int eno, const char *str)
+{
+ if (!Batch)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ putp(tg2(0, Screen_rows));
+ putp(Cap_curs_norm);
+ putp(Cap_smam);
+ putp("\n");
+ fflush(stdout);
+
+//#define ATEOJ_REPORT
+#ifdef ATEOJ_REPORT
+
+ fprintf(fp,
+ "\n\tTerminal: %s"
+ "\n\t device = %s, ncurses = v%s"
+ "\n\t max_colors = %d, max_pairs = %d"
+ "\n\t Cap_can_goto = %s"
+ "\n\t Screen_cols = %d, Screen_rows = %d"
+ "\n\t Max_lines = %d, most recent Pseudo_size = %d"
+ "\n"
+#ifdef PRETENDNOCAP
+ , "dumb"
+#else
+ , termname()
+#endif
+ , ttyname(STDOUT_FILENO), NCURSES_VERSION
+ , max_colors, max_pairs
+ , Cap_can_goto ? "yes" : "No!"
+ , Screen_cols, Screen_rows
+ , Max_lines, Pseudo_size
+ );
+
+ fprintf(fp,
+#ifndef STDOUT_IOLBF
+ "\n\t Stdout_buf = %d, BUFSIZ = %u"
+#endif
+ "\n\tWindows and Curwin->"
+ "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d"
+ "\n\t rc.winname = %s, grpname = %s"
+ "\n\t rc.winflags = %08x, maxpflgs = %d"
+ "\n\t rc.fieldscur = %s"
+ "\n\t winlines = %d, rc.maxtasks = %d, maxcmdln = %d"
+ "\n\t rc.sortindx = %d"
+ "\n"
+#ifndef STDOUT_IOLBF
+ , sizeof(Stdout_buf), (unsigned)BUFSIZ
+#endif
+ , sizeof(WIN_t), GROUPSMAX
+ , Curwin->rc.winname, Curwin->grpname
+ , Curwin->rc.winflags, Curwin->maxpflgs
+ , Curwin->rc.fieldscur
+ , Curwin->winlines, Curwin->rc.maxtasks, Curwin->maxcmdln
+ , Curwin->rc.sortindx
+ );
+
+ fprintf(fp,
+ "\n\tProgram"
+ "\n\t Linux version = %u.%u.%u, %s"
+ "\n\t Hertz = %u (%u bytes, %u-bit time)"
+ "\n\t Page_size = %d, Cpu_tot = %d, sizeof(proc_t) = %u"
+ "\n\t sizeof(CPU_t) = %u, sizeof(HST_t) = %u (%u HST_t's/Page)"
+ "\n"
+ , LINUX_VERSION_MAJOR(linux_version_code)
+ , LINUX_VERSION_MINOR(linux_version_code)
+ , LINUX_VERSION_PATCH(linux_version_code)
+ , procps_version
+ , (unsigned)Hertz, sizeof(Hertz), sizeof(Hertz) * 8
+ , Page_size, Cpu_tot, sizeof(proc_t)
+ , sizeof(CPU_t), sizeof(HST_t), Page_size / sizeof(HST_t)
+ );
+
+
+#endif
+
+ if (str) fputs(str, fp);
+ exit(eno);
+}
+
+
+ /*
+ * Normal end of execution.
+ * catches:
+ * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
+// FIXME: can't do this shit in a signal handler
+static void end_pgm (int sig) NORETURN;
+static void end_pgm (int sig)
+{
+ if(sig)
+ sig |= 0x80; // for a proper process exit code
+ bye_bye(stdout, sig, NULL);
+}
+
+
+ /*
+ * Standard error handler to normalize the look of all err o/p */
+static void std_err (const char *str) NORETURN;
+static void std_err (const char *str)
+{
+ static char buf[SMLBUFSIZ];
+
+ fflush(stdout);
+ /* we'll use our own buffer so callers can still use fmtmk() and, yes the
+ leading tab is not the standard convention, but the standard is wrong
+ -- OUR msg won't get lost in screen clutter, like so many others! */
+ snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
+ if (!Ttychanged) {
+ fprintf(stderr, "%s\n", buf);
+ exit(1);
+ }
+ /* not to worry, he'll change our exit code to 1 due to 'buf' */
+ bye_bye(stderr, 1, buf);
+}
+
+
+ /*
+ * Standard out handler */
+static void std_out (const char *str) NORETURN;
+static void std_out (const char *str)
+{
+ static char buf[SMLBUFSIZ];
+
+ fflush(stdout);
+ /* we'll use our own buffer so callers can still use fmtmk() and, yes the
+ leading tab is not the standard convention, but the standard is wrong
+ -- OUR msg won't get lost in screen clutter, like so many others! */
+ snprintf(buf, sizeof(buf), "\t%s: %s\n", Myname, str);
+ if (!Ttychanged) {
+ fprintf(stdout, "%s\n", buf);
+ exit(0);
+ }
+ bye_bye(stdout, 0, buf);
+}
+
+
+ /*
+ * Suspend ourself.
+ * catches:
+ * SIGTSTP, SIGTTIN and SIGTTOU */
+// FIXME: can't do this shit in a signal handler!
+static void suspend (int dont_care_sig)
+{
+ (void)dont_care_sig;
+ /* reset terminal */
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+ putp(tg2(0, Screen_rows));
+ putp(Cap_curs_norm);
+ putp(Cap_smam);
+ putp("\n");
+ fflush(stdout);
+ raise(SIGSTOP);
+ /* later, after SIGCONT... */
+ ZAP_TIMEOUT
+ if (!Batch)
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
+ putp(Cap_clr_scr);
+ putp(Cap_rmam);
+}
+
+
+/*###### Misc Color/Display support ####################################*/
+
+ /* macro to test if a basic (non-color) capability is valid
+ thanks: Floyd Davidson <floyd@ptialaska.net> */
+#define tIF(s) s ? s : ""
+#define CAPCOPY(dst,src) src && strcpy(dst,src)
+
+ /*
+ * Make the appropriate caps/color strings and set some
+ * lengths which are used to distinguish twix the displayed
+ * columns and an actual printed row!
+ * note: we avoid the use of background color so as to maximize
+ * compatibility with the user's xterm settings */
+static void capsmk (WIN_t *q)
+{
+ static int capsdone = 0;
+
+ // we must NOT disturb our 'empty' terminfo strings!
+ if (Batch) return;
+
+ // these are the unchangeable puppies, so we only do 'em once
+ if (!capsdone) {
+ CAPCOPY(Cap_clr_eol, clr_eol);
+ CAPCOPY(Cap_clr_eos, clr_eos);
+ CAPCOPY(Cap_clr_scr, clear_screen);
+
+ if (!eat_newline_glitch) { // we like the eat_newline_glitch
+ CAPCOPY(Cap_rmam, exit_am_mode);
+ CAPCOPY(Cap_smam, enter_am_mode);
+ if (!*Cap_rmam || !*Cap_smam) { // need both
+ *Cap_rmam = '\0';
+ *Cap_smam = '\0';
+ if (auto_right_margin) {
+ avoid_last_column = 1;
+ }
+ }
+ }
+
+ CAPCOPY(Cap_curs_huge, cursor_visible);
+ CAPCOPY(Cap_curs_norm, cursor_normal);
+ CAPCOPY(Cap_home, cursor_home);
+ CAPCOPY(Cap_norm, exit_attribute_mode);
+ CAPCOPY(Cap_reverse, enter_reverse_mode);
+
+ snprintf(Caps_off, sizeof(Caps_off), "%s%s", Cap_norm, tIF(orig_pair));
+ if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1;
+ capsdone = 1;
+ }
+ /* the key to NO run-time costs for configurable colors -- we spend a
+ little time with the user now setting up our terminfo strings, and
+ the job's done until he/she/it has a change-of-heart */
+ strcpy(q->cap_bold, CHKw(q, View_NOBOLD) ? Cap_norm : tIF(enter_bold_mode));
+ if (CHKw(q, Show_COLORS) && max_colors > 0) {
+ strcpy(q->capclr_sum, tparm(set_a_foreground, q->rc.summclr));
+ snprintf(q->capclr_msg, sizeof(q->capclr_msg), "%s%s"
+ , tparm(set_a_foreground, q->rc.msgsclr), Cap_reverse);
+ snprintf(q->capclr_pmt, sizeof(q->capclr_pmt), "%s%s"
+ , tparm(set_a_foreground, q->rc.msgsclr), q->cap_bold);
+ snprintf(q->capclr_hdr, sizeof(q->capclr_hdr), "%s%s"
+ , tparm(set_a_foreground, q->rc.headclr), Cap_reverse);
+ snprintf(q->capclr_rownorm, sizeof(q->capclr_rownorm), "%s%s"
+ , Caps_off, tparm(set_a_foreground, q->rc.taskclr));
+ } else {
+ q->capclr_sum[0] = '\0';
+ strcpy(q->capclr_msg, Cap_reverse);
+ strcpy(q->capclr_pmt, q->cap_bold);
+ strcpy(q->capclr_hdr, Cap_reverse);
+ strcpy(q->capclr_rownorm, Cap_norm);
+ }
+ // composite(s), so we do 'em outside and after the if
+ snprintf(q->capclr_rowhigh, sizeof(q->capclr_rowhigh), "%s%s"
+ , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? q->cap_bold : Cap_reverse);
+ q->len_rownorm = strlen(q->capclr_rownorm);
+ q->len_rowhigh = strlen(q->capclr_rowhigh);
+
+#undef tIF
+}
+
+
+// Show an error, but not right now.
+// Due to the postponed opening of ksym, using open_psdb_message,
+// if P_WCH had been selected and the program is restarted, the
+// message would otherwise be displayed prematurely.
+static void msg_save (const char *fmts, ...) __attribute__((format(printf,1,2)));
+static void msg_save (const char *fmts, ...)
+{
+ char tmp[SMLBUFSIZ];
+ va_list va;
+
+ va_start(va, fmts);
+ vsnprintf(tmp, sizeof(tmp), fmts, va);
+ va_end(va);
+ /* we'll add some extra attention grabbers to whatever this is */
+ snprintf(Msg_delayed, sizeof(Msg_delayed), "\a*** %s ***", strim_0(tmp));
+ Msg_awaiting = 1;
+}
+
+
+ /*
+ * Show an error message (caller may include a '\a' for sound) */
+static void show_msg (const char *str)
+{
+ PUTT("%s%s %s %s%s",
+ tg2(0, Msg_row),
+ Curwin->capclr_msg,
+ str,
+ Caps_off,
+ Cap_clr_eol
+ );
+ fflush(stdout);
+ sleep(MSG_SLEEP);
+ Msg_awaiting = 0;
+}
+
+
+ /*
+ * Show an input prompt + larger cursor */
+static void show_pmt (const char *str)
+{
+ PUTT("%s%s%s: %s%s",
+ tg2(0, Msg_row),
+ Curwin->capclr_pmt,
+ str,
+ Cap_curs_huge,
+ Caps_off
+ );
+ fflush(stdout);
+}
+
+
+ /*
+ * Show lines with specially formatted elements, but only output
+ * what will fit within the current screen width.
+ * Our special formatting consists of:
+ * "some text <_delimiter_> some more text <_delimiter_>...\n"
+ * Where <_delimiter_> is a single byte in the range of:
+ * \01 through \10 (in decimalizee, 1 - 8)
+ * and is used to select an 'attribute' from a capabilities table
+ * which is then applied to the *preceding* substring.
+ * Once recognized, the delimiter is replaced with a null character
+ * and viola, we've got a substring ready to output! Strings or
+ * substrings without delimiters will receive the Cap_norm attribute.
+ *
+ * Caution:
+ * This routine treats all non-delimiter bytes as displayable
+ * data subject to our screen width marching orders. If callers
+ * embed non-display data like tabs or terminfo strings in our
+ * glob, a line will truncate incorrectly at best. Worse case
+ * would be truncation of an embedded tty escape sequence.
+ *
+ * Tabs must always be avoided or our efforts are wasted and
+ * lines will wrap. To lessen but not eliminate the risk of
+ * terminfo string truncation, such non-display stuff should
+ * be placed at the beginning of a "short" line.
+ * (and as for tabs, gimme 1 more color then no worries, mate) */
+static void show_special (int interact, const char *glob)
+{ /* note: the following is for documentation only,
+ the real captab is now found in a group's WIN_t !
+ +------------------------------------------------------+
+ | char *captab[] = { : Cap's/Delim's |
+ | Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 |
+ | Sum_color, = \03 |
+ | Msg_color, Pmt_color, = \04, \05 |
+ | Hdr_color, = \06 |
+ | Row_color_high, = \07 |
+ | Row_color_norm }; = \10 [octal!] |
+ +------------------------------------------------------+ */
+ char lin[BIGBUFSIZ], row[ROWBUFSIZ], tmp[ROWBUFSIZ];
+ char *rp, *cap, *lin_end, *sub_beg, *sub_end;
+ int room;
+
+ /* handle multiple lines passed in a bunch */
+ while ((lin_end = strchr(glob, '\n'))) {
+
+ /* create a local copy we can extend and otherwise abuse */
+ size_t amt = lin_end - glob;
+ if(amt > sizeof lin - 1)
+ amt = sizeof lin - 1; // shit happens
+ memcpy(lin, glob, amt);
+ /* zero terminate this part and prepare to parse substrings */
+ lin[amt] = '\0';
+ room = Screen_cols;
+ sub_beg = sub_end = lin;
+ *(rp = row) = '\0';
+
+ while (*sub_beg) {
+ switch (*sub_end) {
+ case 0: /* no end delim, captab makes normal */
+ *(sub_end + 1) = '\0'; /* extend str end, then fall through */
+ case 1 ... 8:
+ cap = Curwin->captab[(int)*sub_end];
+ *sub_end = '\0';
+ snprintf(tmp, sizeof(tmp), "%s%.*s%s", cap, room, sub_beg, Caps_off);
+ amt = strlen(tmp);
+ if(rp - row + amt + 1 > sizeof row)
+ goto overflow; // shit happens
+ rp = scat(rp, tmp);
+ room -= (sub_end - sub_beg);
+ sub_beg = ++sub_end;
+ break;
+ default: /* nothin' special, just text */
+ ++sub_end;
+ }
+ if (unlikely(0 >= room)) break; /* skip substrings that won't fit */
+ }
+overflow:
+ if (interact) PUTT("%s%s\n", row, Cap_clr_eol);
+ else PUFF("%s%s\n", row, Cap_clr_eol);
+ glob = ++lin_end; /* point to next line (maybe) */
+ } /* end: while 'lines' */
+
+ /* If there's anything left in the glob (by virtue of no trailing '\n'),
+ it probably means caller wants to retain cursor position on this final
+ line. That, in turn, means we're interactive and so we'll just do our
+ 'fit-to-screen' thingy... */
+ if (*glob) PUTT("%.*s", Screen_cols, glob);
+}
+
+
+/*###### Small Utility routines ########################################*/
+
+// Get a string from the user
+static char *ask4str (const char *prompt)
+{
+ static char buf[GETBUFSIZ];
+
+ show_pmt(prompt);
+ memset(buf, '\0', sizeof(buf));
+ chin(1, buf, sizeof(buf) - 1);
+ putp(Cap_curs_norm);
+ return strim_0(buf);
+}
+
+
+// Get a float from the user
+static float get_float (const char *prompt)
+{
+ char *line;
+ float f;
+
+ if (!(*(line = ask4str(prompt)))) return -1;
+ // note: we're not allowing negative floats
+ if (strcspn(line, ",.1234567890")) {
+ show_msg("\aNot valid");
+ return -1;
+ }
+ sscanf(line, "%f", &f);
+ return f;
+}
+
+
+// Get an integer from the user
+static int get_int (const char *prompt)
+{
+ char *line;
+ int n;
+
+ if (!(*(line = ask4str(prompt)))) return -1;
+ // note: we've got to allow negative ints (renice)
+ if (strcspn(line, "-1234567890")) {
+ show_msg("\aNot valid");
+ return -1;
+ }
+ sscanf(line, "%d", &n);
+ return n;
+}
+
+
+ /*
+ * Do some scaling stuff.
+ * We'll interpret 'num' as one of the following types and
+ * try to format it to fit 'width'.
+ * SK_no (0) it's a byte count
+ * SK_Kb (1) it's kilobytes
+ * SK_Mb (2) it's megabytes
+ * SK_Gb (3) it's gigabytes
+ * SK_Tb (4) it's terabytes */
+static const char *scale_num (unsigned long num, const int width, const unsigned type)
+{
+ /* kilobytes, megabytes, gigabytes, terabytes, duh! */
+ static double scale[] = { 1024.0, 1024.0*1024, 1024.0*1024*1024, 1024.0*1024*1024*1024, 0 };
+ /* kilo, mega, giga, tera, none */
+#ifdef CASEUP_SCALE
+ static char nextup[] = { 'K', 'M', 'G', 'T', 0 };
+#else
+ static char nextup[] = { 'k', 'm', 'g', 't', 0 };
+#endif
+ static char buf[TNYBUFSIZ];
+ double *dp;
+ char *up;
+
+ /* try an unscaled version first... */
+ if (width >= snprintf(buf, sizeof(buf), "%lu", num)) return buf;
+
+ /* now try successively higher types until it fits */
+ for (up = nextup + type, dp = scale; *dp; ++dp, ++up) {
+ /* the most accurate version */
+ if (width >= snprintf(buf, sizeof(buf), "%.1f%c", num / *dp, *up))
+ return buf;
+ /* the integer version */
+ if (width >= snprintf(buf, sizeof(buf), "%ld%c", (unsigned long)(num / *dp), *up))
+ return buf;
+ }
+ /* well shoot, this outta' fit... */
+ return "?";
+}
+
+
+ /*
+ * Do some scaling stuff.
+ * format 'tics' to fit 'width'. */
+static const char *scale_tics (TIC_t tics, const int width)
+{
+#ifdef CASEUP_SCALE
+#define HH "%uH"
+#define DD "%uD"
+#define WW "%uW"
+#else
+#define HH "%uh"
+#define DD "%ud"
+#define WW "%uw"
+#endif
+ static char buf[TNYBUFSIZ];
+ unsigned long nt; // narrow time, for speed on 32-bit
+ unsigned cc; // centiseconds
+ unsigned nn; // multi-purpose whatever
+
+ nt = (tics * 100ull) / Hertz;
+ cc = nt % 100; // centiseconds past second
+ nt /= 100; // total seconds
+ nn = nt % 60; // seconds past the minute
+ nt /= 60; // total minutes
+ if (width >= snprintf(buf, sizeof(buf), "%lu:%02u.%02u", nt, nn, cc))
+ return buf;
+ if (width >= snprintf(buf, sizeof buf, "%lu:%02u", nt, nn))
+ return buf;
+ nn = nt % 60; // minutes past the hour
+ nt /= 60; // total hours
+ if (width >= snprintf(buf, sizeof buf, "%lu,%02u", nt, nn))
+ return buf;
+ nn = nt; // now also hours
+ if (width >= snprintf(buf, sizeof buf, HH, nn))
+ return buf;
+ nn /= 24; // now days
+ if (width >= snprintf(buf, sizeof buf, DD, nn))
+ return buf;
+ nn /= 7; // now weeks
+ if (width >= snprintf(buf, sizeof buf, WW, nn))
+ return buf;
+ // well shoot, this outta' fit...
+ return "?";
+
+#undef HH
+#undef DD
+#undef WW
+}
+
+#include <pwd.h>
+
+static int selection_type;
+static uid_t selection_uid;
+
+// FIXME: this is "temporary" code we hope
+static int good_uid(const proc_t *restrict const pp){
+ switch(selection_type){
+ case 'p':
+ return 1;
+ case 0:
+ return 1;
+ case 'U':
+ if (pp->ruid == selection_uid) return 1;
+ if (pp->suid == selection_uid) return 1;
+ if (pp->fuid == selection_uid) return 1;
+ // FALLTHROUGH
+ case 'u':
+ if (pp->euid == selection_uid) return 1;
+ // FALLTHROUGH
+ default:
+ ; // don't know what it is; find bugs fast
+ }
+ return 0;
+}
+
+// swiped from ps, and ought to be in libproc
+static const char *parse_uid(const char *restrict const str, uid_t *restrict const ret){
+ struct passwd *passwd_data;
+ char *endp;
+ unsigned long num;
+ static const char uidrange[] = "User ID out of range.";
+ static const char uidexist[] = "User name does not exist.";
+ num = strtoul(str, &endp, 0);
+ if(*endp != '\0'){ /* hmmm, try as login name */
+ passwd_data = getpwnam(str);
+ if(!passwd_data) return uidexist;
+ num = passwd_data->pw_uid;
+ }
+ if(num > 0xfffffffeUL) return uidrange;
+ *ret = num;
+ return 0;
+}
+
+
+/*###### Library Alternatives ##########################################*/
+
+ /*
+ * Handle our own memory stuff without the risk of leaving the
+ * user's terminal in an ugly state should things go sour. */
+
+static void *alloc_c (unsigned numb) MALLOC;
+static void *alloc_c (unsigned numb)
+{
+ void * p;
+
+ if (!numb) ++numb;
+ if (!(p = calloc(1, numb)))
+ std_err("failed memory allocate");
+ return p;
+}
+
+static void *alloc_r (void *q, unsigned numb) MALLOC;
+static void *alloc_r (void *q, unsigned numb)
+{
+ void *p;
+
+ if (!numb) ++numb;
+ if (!(p = realloc(q, numb)))
+ std_err("failed memory allocate");
+ return p;
+}
+
+
+ /*
+ * This guy's modeled on libproc's 'five_cpu_numbers' function except
+ * we preserve all cpu data in our CPU_t array which is organized
+ * as follows:
+ * cpus[0] thru cpus[n] == tics for each separate cpu
+ * cpus[Cpu_tot] == tics from the 1st /proc/stat line */
+static CPU_t *cpus_refresh (CPU_t *cpus)
+{
+ static FILE *fp = NULL;
+ int i;
+ int num;
+ // enough for a /proc/stat CPU line (not the intr line)
+ char buf[SMLBUFSIZ];
+
+ /* by opening this file once, we'll avoid the hit on minor page faults
+ (sorry Linux, but you'll have to close it for us) */
+ if (!fp) {
+ if (!(fp = fopen("/proc/stat", "r")))
+ std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno)));
+ /* note: we allocate one more CPU_t than Cpu_tot so that the last slot
+ can hold tics representing the /proc/stat cpu summary (the first
+ line read) -- that slot supports our View_CPUSUM toggle */
+ cpus = alloc_c((1 + Cpu_tot) * sizeof(CPU_t));
+ }
+ rewind(fp);
+ fflush(fp);
+
+ // first value the last slot with the cpu summary line
+ if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
+ cpus[Cpu_tot].x = 0; // FIXME: can't tell by kernel version number
+ cpus[Cpu_tot].y = 0; // FIXME: can't tell by kernel version number
+ cpus[Cpu_tot].z = 0; // FIXME: can't tell by kernel version number
+ num = sscanf(buf, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
+ &cpus[Cpu_tot].u,
+ &cpus[Cpu_tot].n,
+ &cpus[Cpu_tot].s,
+ &cpus[Cpu_tot].i,
+ &cpus[Cpu_tot].w,
+ &cpus[Cpu_tot].x,
+ &cpus[Cpu_tot].y,
+ &cpus[Cpu_tot].z
+ );
+ if (num < 4)
+ std_err("failed /proc/stat read");
+
+ // and just in case we're 2.2.xx compiled without SMP support...
+ if (Cpu_tot == 1) {
+ cpus[1].id = 0;
+ memcpy(cpus, &cpus[1], sizeof(CPU_t));
+ }
+
+ // now value each separate cpu's tics
+ for (i = 0; 1 < Cpu_tot && i < Cpu_tot; i++) {
+ if (!fgets(buf, sizeof(buf), fp)) std_err("failed /proc/stat read");
+ cpus[i].x = 0; // FIXME: can't tell by kernel version number
+ cpus[i].y = 0; // FIXME: can't tell by kernel version number
+ cpus[i].z = 0; // FIXME: can't tell by kernel version number
+ num = sscanf(buf, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
+ &cpus[i].id,
+ &cpus[i].u, &cpus[i].n, &cpus[i].s, &cpus[i].i, &cpus[i].w, &cpus[i].x, &cpus[i].y, &cpus[i].z
+ );
+ if (num < 4)
+ std_err("failed /proc/stat read");
+ }
+ return cpus;
+}
+
+
+ /*
+ * Refresh procs *Helper* function to eliminate yet one more need
+ * to loop through our darn proc_t table. He's responsible for:
+ * 1) calculating the elapsed time since the previous frame
+ * 2) counting the number of tasks in each state (run, sleep, etc)
+ * 3) maintaining the HST_t's and priming the proc_t pcpu field
+ * 4) establishing the total number tasks for this frame */
+static void prochlp (proc_t *this)
+{
+ static HST_t *hist_sav = NULL;
+ static HST_t *hist_new = NULL;
+ static unsigned hist_siz = 0; // number of structs
+ static unsigned maxt_sav; // prior frame's max tasks
+ TIC_t tics;
+
+ if (unlikely(!this)) {
+ static struct timeval oldtimev;
+ struct timeval timev;
+ struct timezone timez;
+ HST_t *hist_tmp;
+ float et;
+
+ gettimeofday(&timev, &timez);
+ et = (timev.tv_sec - oldtimev.tv_sec)
+ + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
+ oldtimev.tv_sec = timev.tv_sec;
+ oldtimev.tv_usec = timev.tv_usec;
+
+ // if in Solaris mode, adjust our scaling for all cpus
+ Frame_tscale = 100.0f / ((float)Hertz * (float)et * (Rc.mode_irixps ? 1 : Cpu_tot));
+ maxt_sav = Frame_maxtask;
+ Frame_maxtask = Frame_running = Frame_sleepin = Frame_stopped = Frame_zombied = 0;
+
+ // reuse memory each time around
+ hist_tmp = hist_sav;
+ hist_sav = hist_new;
+ hist_new = hist_tmp;
+ // prep for our binary search by sorting the last frame's HST_t's
+ qsort(hist_sav, maxt_sav, sizeof(HST_t), (QFP_t)sort_HST_t);
+ return;
+ }
+
+ switch (this->state) {
+ case 'R':
+ Frame_running++;
+ break;
+ case 'S':
+ case 'D':
+ Frame_sleepin++;
+ break;
+ case 'T':
+ Frame_stopped++;
+ break;
+ case 'Z':
+ Frame_zombied++;
+ break;
+ }
+
+ if (unlikely(Frame_maxtask+1 >= hist_siz)) {
+ hist_siz = hist_siz * 5 / 4 + 100; // grow by at least 25%
+ hist_sav = alloc_r(hist_sav, sizeof(HST_t) * hist_siz);
+ hist_new = alloc_r(hist_new, sizeof(HST_t) * hist_siz);
+ }
+ /* calculate time in this process; the sum of user time (utime) and
+ system time (stime) -- but PLEASE dont waste time and effort on
+ calcs and saves that go unused, like the old top! */
+ hist_new[Frame_maxtask].pid = this->tid;
+ hist_new[Frame_maxtask].tics = tics = (this->utime + this->stime);
+
+#if 0
+{ int i;
+ int lo = 0;
+ int hi = maxt_sav - 1;
+
+ // find matching entry from previous frame and make ticks elapsed
+ while (lo <= hi) {
+ i = (lo + hi) / 2;
+ if (this->tid < hist_sav[i].pid)
+ hi = i - 1;
+ else if (likely(this->tid > hist_sav[i].pid))
+ lo = i + 1;
+ else {
+ tics -= hist_sav[i].tics;
+ break;
+ }
+ }
+}
+#else
+{
+ HST_t tmp;
+ const HST_t *ptr;
+ tmp.pid = this->tid;
+ ptr = bsearch(&tmp, hist_sav, maxt_sav, sizeof tmp, sort_HST_t);
+ if(ptr) tics -= ptr->tics;
+}
+#endif
+
+ // we're just saving elapsed tics, to be converted into %cpu if
+ // this task wins it's displayable screen row lottery... */
+ this->pcpu = tics;
+// if (Frames_maxcmdln) { }
+ // shout this to the world with the final call (or us the next time in)
+ Frame_maxtask++;
+}
+
+
+ /*
+ * This guy's modeled on libproc's 'readproctab' function except
+ * we reuse and extend any prior proc_t's. He's been customized
+ * for our specific needs and to avoid the use of <stdarg.h> */
+static proc_t **procs_refresh (proc_t **table, int flags)
+{
+#define PTRsz sizeof(proc_t *)
+#define ENTsz sizeof(proc_t)
+ static unsigned savmax = 0; // first time, Bypass: (i)
+ proc_t *ptsk = (proc_t *)-1; // first time, Force: (ii)
+ unsigned curmax = 0; // every time (jeeze)
+ PROCTAB* PT;
+ static int show_threads_was_enabled = 0; // optimization
+
+ prochlp(NULL); // prep for a new frame
+ if (Monpidsidx)
+ PT = openproc(flags, Monpids);
+ else
+ PT = openproc(flags);
+
+ // i) Allocated Chunks: *Existing* table; refresh + reuse
+ if (!(CHKw(Curwin, Show_THREADS))) {
+ while (curmax < savmax) {
+ if (table[curmax]->cmdline) {
+ unsigned idx;
+ // Skip if Show_THREADS was never enabled
+ if (show_threads_was_enabled) {
+ for (idx = curmax + 1; idx < savmax; idx++) {
+ if (table[idx]->cmdline == table[curmax]->cmdline)
+ table[idx]->cmdline = NULL;
+ }
+ }
+ free(*table[curmax]->cmdline);
+ table[curmax]->cmdline = NULL;
+ }
+ if (unlikely(!(ptsk = readproc(PT, table[curmax])))) break;
+ prochlp(ptsk); // tally & complete this proc_t
+ ++curmax;
+ }
+ }
+ else { // show each thread in a process separately
+ while (curmax < savmax) {
+ proc_t *ttsk;
+ if (unlikely(!(ptsk = readproc(PT, NULL)))) break;
+ show_threads_was_enabled = 1;
+ while (curmax < savmax) {
+ unsigned idx;
+ if (table[curmax]->cmdline) {
+ // threads share the same cmdline storage. 'table' is
+ // qsort()ed, so must look through the rest of the table.
+ for (idx = curmax + 1; idx < savmax; idx++) {
+ if (table[idx]->cmdline == table[curmax]->cmdline)
+ table[idx]->cmdline = NULL;
+ }
+ free(*table[curmax]->cmdline); // only free once
+ table[curmax]->cmdline = NULL;
+ }
+ if (!(ttsk = readtask(PT, ptsk, table[curmax]))) break;
+ prochlp(ttsk);
+ ++curmax;
+ }
+ free(ptsk); // readproc() proc_t not used
+ }
+ }
+
+ // ii) Unallocated Chunks: *New* or *Existing* table; extend + fill
+ if (!(CHKw(Curwin, Show_THREADS))) {
+ while (ptsk) {
+ // realloc as we go, keeping 'table' ahead of 'currmax++'
+ table = alloc_r(table, (curmax + 1) * PTRsz);
+ // here, readproc will allocate the underlying proc_t stg
+ if (likely(ptsk = readproc(PT, NULL))) {
+ prochlp(ptsk); // tally & complete this proc_t
+ table[curmax++] = ptsk;
+ }
+ }
+ }
+ else { // show each thread in a process separately
+ while (ptsk) {
+ proc_t *ttsk;
+ if (likely(ptsk = readproc(PT, NULL))) {
+ show_threads_was_enabled = 1;
+ while (1) {
+ table = alloc_r(table, (curmax + 1) * PTRsz);
+ if (!(ttsk = readtask(PT, ptsk, NULL))) break;
+ prochlp(ttsk);
+ table[curmax++] = ttsk;
+ }
+ free(ptsk); // readproc() proc_t not used
+ }
+ }
+ }
+ closeproc(PT);
+
+ // iii) Chunkless: make 'eot' entry, after ensuring proc_t exists
+ if (curmax >= savmax) {
+ table = alloc_r(table, (curmax + 1) * PTRsz);
+ // here, we must allocate the underlying proc_t stg ourselves
+ table[curmax] = alloc_c(ENTsz);
+ savmax = curmax + 1;
+ }
+ // this frame's end, but not necessarily end of allocated space
+ table[curmax]->tid = -1;
+ return table;
+
+#undef PTRsz
+#undef ENTsz
+}
+
+/*###### Field Table/RCfile compatability support ######################*/
+
+// from either 'stat' or 'status' (preferred), via bits not otherwise used
+#define L_EITHER PROC_SPARE_1
+// These are the Fieldstab.lflg values used here and in reframewins.
+// (own identifiers as documentation and protection against changes)
+#define L_stat PROC_FILLSTAT
+#define L_statm PROC_FILLMEM
+#define L_status PROC_FILLSTATUS
+#define L_CMDLINE L_EITHER | PROC_FILLARG
+#define L_EUSER PROC_FILLUSR
+#define L_RUSER L_status | PROC_FILLUSR
+#define L_GROUP L_status | PROC_FILLGRP
+#define L_NONE 0
+// for reframewins and summary_show 1st pass
+#define L_DEFAULT PROC_FILLSTAT
+
+// a temporary macro, soon to be undef'd...
+#define SF(f) (QFP_t)sort_P_ ## f
+
+ /* These are our gosh darn 'Fields' !
+ They MUST be kept in sync with pflags !!
+ note: for integer data, the length modifiers found in .fmts may
+ NOT reflect the true field type found in proc_t -- this plus
+ a cast when/if displayed provides minimal width protection. */
+static FLD_t Fieldstab[] = {
+/* .lflg anomolies:
+ P_UID, L_NONE - natural outgrowth of 'stat()' in readproc (euid)
+ P_CPU, L_stat - never filled by libproc, but requires times (pcpu)
+ P_CMD, L_stat - may yet require L_CMDLINE in reframewins (cmd/cmdline)
+ L_EITHER - must L_status, else 64-bit math, __udivdi3 on 32-bit !
+ keys head fmts width scale sort desc lflg
+ ------ ----------- ------- ------ ----- ----- ---------------------- -------- */
+ { "AaAa", " PID", " %5u", -1, -1, SF(PID), "Process Id", L_NONE },
+ { "BbBb", " PPID", " %5u", -1, -1, SF(PPD), "Parent Process Pid", L_EITHER },
+ { "CcQq", " RUSER ", " %-8.8s", -1, -1, SF(URR), "Real user name", L_RUSER },
+ { "DdCc", " UID", " %4u", -1, -1, SF(UID), "User Id", L_NONE },
+ { "EeDd", " USER ", " %-8.8s", -1, -1, SF(URE), "User Name", L_EUSER },
+ { "FfNn", " GROUP ", " %-8.8s", -1, -1, SF(GRP), "Group Name", L_GROUP },
+ { "GgGg", " TTY ", " %-8.8s", 8, -1, SF(TTY), "Controlling Tty", L_stat },
+ { "HhHh", " PR", " %3d", -1, -1, SF(PRI), "Priority", L_stat },
+ { "IiIi", " NI", " %3d", -1, -1, SF(NCE), "Nice value", L_stat },
+ { "JjYy", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
+ { "KkEe", " %CPU", " %#4.1f", -1, -1, SF(CPU), "CPU usage", L_stat },
+ { "LlWw", " TIME", " %6.6s", 6, -1, SF(TME), "CPU Time", L_stat },
+ { "MmRr", " TIME+ ", " %9.9s", 9, -1, SF(TME), "CPU Time, hundredths", L_stat },
+ { "NnFf", " %MEM", " %#4.1f", -1, -1, SF(RES), "Memory usage (RES)", L_statm },
+ { "OoMm", " VIRT", " %5.5s", 5, SK_Kb, SF(VRT), "Virtual Image (kb)", L_statm },
+ { "PpOo", " SWAP", " %4.4s", 4, SK_Kb, SF(SWP), "Swapped size (kb)", L_statm },
+ { "QqTt", " RES", " %4.4s", 4, SK_Kb, SF(RES), "Resident size (kb)", L_statm },
+ { "RrKk", " CODE", " %4.4s", 4, SK_Kb, SF(COD), "Code size (kb)", L_statm },
+ { "SsLl", " DATA", " %4.4s", 4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm },
+ { "TtPp", " SHR", " %4.4s", 4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm },
+ { "UuJj", " nFLT", " %4.4s", 4, SK_no, SF(FLT), "Page Fault count", L_stat },
+ { "VvSs", " nDRT", " %4.4s", 4, SK_no, SF(DRT), "Dirty Pages count", L_statm },
+ { "WwVv", " S", " %c", -1, -1, SF(STA), "Process Status", L_EITHER },
+ // next entry's special: '.head' will be formatted using table entry's own
+ // '.fmts' plus runtime supplied conversion args!
+ { "XxXx", " COMMAND", " %-*.*s", -1, -1, SF(CMD), "Command name/line", L_EITHER },
+ { "YyUu", " WCHAN ", " %-9.9s", -1, -1, SF(WCH), "Sleeping in Function", L_stat },
+ // next entry's special: the 0's will be replaced with '.'!
+ { "ZzZz", " Flags ", " %08lx", -1, -1, SF(FLG), "Task Flags <sched.h>", L_stat },
+#if 0
+ { "..Qq", " A", " %4.4s", 4, SK_no, SF(PID), "Accessed Page count", L_stat },
+ { "..Nn", " TRS", " %4.4s", 4, SK_Kb, SF(PID), "Code in memory (kb)", L_stat },
+ { "..Rr", " WP", " %4.4s", 4, SK_no, SF(PID), "Unwritable Pages", L_stat },
+ { "Jj[{", " #C", " %2u", -1, -1, SF(CPN), "Last used cpu (SMP)", L_stat },
+ { "..\\|"," Bad", " %2u", -1, -1, SF(CPN), "-- must ignore | --", 0 },
+ { "..]}", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
+ { "..^~", " Bad", " %2u", -1, -1, SF(CPN), "-- not used --", 0 },
+#endif
+};
+#undef SF
+
+
+ /* All right, those-that-follow -- Listen Up!
+ * For the above table keys and the following present/future rc file
+ * compatibility support, you have Mr. Albert D. Cahalan to thank.
+ * He must have been in a 'Christmas spirit'. Were it left to me,
+ * this top would never have gotten that close to the former top's
+ * crufty rcfile. Not only is it illogical, it's odoriferous !
+ */
+
+ // used as 'to' and/or 'from' args in the ft_xxx utilities...
+#define FT_NEW_fmt 0
+#define FT_OLD_fmt 2
+
+
+#if 0
+ // convert, or 0 for failure
+static int ft_cvt_char (const int fr, const int to, int c) {
+ int j = -1;
+
+ while (++j < MAXTBL(Fieldstab)) {
+ if (c == Fieldstab[j].keys[fr]) return Fieldstab[j].keys[to];
+ if (c == Fieldstab[j].keys[fr+1]) return Fieldstab[j].keys[to+1];
+ }
+ return 0;
+}
+#endif
+
+
+ // convert
+static inline int ft_get_char (const int fr, int i) {
+ int c;
+ if (i < 0) return 0;
+ if (i >= MAXTBL(Fieldstab)) return 0;
+ c = Fieldstab[i].keys[fr];
+ if (c == '.') c = 0; // '.' marks a bad entry
+ return c;
+}
+
+
+#if 0
+ // convert, or -1 for failure
+static int ft_get_idx (const int fr, int c) {
+ int j = -1;
+
+ while (++j < MAXTBL(Fieldstab)) {
+ if (c == Fieldstab[j].keys[fr]) return j;
+ if (c == Fieldstab[j].keys[fr+1]) return j;
+ }
+ return -1;
+}
+#endif
+
+
+ // convert, or NULL for failure
+static const FLD_t *ft_get_ptr (const int fr, int c) {
+ int j = -1;
+
+ while (++j < MAXTBL(Fieldstab)) {
+ if (c == Fieldstab[j].keys[fr]) return Fieldstab+j;
+ if (c == Fieldstab[j].keys[fr+1]) return Fieldstab+j;
+ }
+ return NULL;
+}
+
+
+#if 0
+ // convert, or NULL for failure
+static const FLD_t *ft_idx_to_ptr (const int i) {
+ if (i < 0) return NULL;
+ if (i >= MAXTBL(Fieldstab)) return NULL;
+ return Fieldstab + i;
+}
+
+
+ // convert, or -1 for failure
+static int ft_ptr_to_idx (const FLD_t *p) {
+ int i;
+ if (p < Fieldstab) return -1;
+ i = p - Fieldstab;
+ if (i >= MAXTBL(Fieldstab)) return -1;
+ return i;
+}
+#endif
+
+
+#if 0
+static void rc_bugless (const RCF_t *const rc) {
+ const RCW_t *w;
+ int i = 0;
+
+ fprintf(stderr,"\n%d %d %f %d\n",
+ rc->mode_altscr, rc->mode_irixps, rc->delay_time, rc->win_index
+ );
+ while(i < 4) {
+ w = &rc->win[i++];
+ fprintf(stderr, "<%s> <%s> %d %08x %d %d %d %d %d\n",
+ w->winname, w->fieldscur, w->sortindx, w->winflags, w->maxtasks,
+ w->summclr, w->msgsclr, w->headclr, w->taskclr
+ );
+ }
+}
+#endif
+
+
+// '$HOME/Rc_name' contains multiple lines - 2 global + 3 per window.
+// line 1: an eyecatcher, with a shameless advertisement
+// line 2: an id, Mode_altcsr, Mode_irixps, Delay_time and Curwin.
+// For each of the 4 windows:
+// line a: contains winname, fieldscur
+// line b: contains winflags, sortindx, maxtasks
+// line c: contains summclr, msgsclr, headclr, taskclr
+// line d: if present, would crash procps-3.1.1
+static int rc_read_new (const char *const buf, RCF_t *rc) {
+ int i;
+ int cnt;
+ const char *cp;
+
+ cp = strstr(buf, "\n\n" RCF_EYECATCHER);
+ if (!cp) return -1;
+ cp = strchr(cp + 2, '\n');
+ if (!cp++) return -2;
+
+ cnt = sscanf(cp, "Id:a, Mode_altscr=%d, Mode_irixps=%d, Delay_time=%f, Curwin=%d\n",
+ &rc->mode_altscr, &rc->mode_irixps, &rc->delay_time, &rc->win_index
+ );
+ if (cnt != 4) return -3;
+ cp = strchr(cp, '\n');
+ if (!cp++) return -4;
+
+ for (i = 0; i < GROUPSMAX; i++) {
+ RCW_t *ptr = &rc->win[i];
+ cnt = sscanf(cp, "%3s\tfieldscur=%31s\n", ptr->winname, ptr->fieldscur);
+ if (cnt != 2) return 5+100*i; // OK to have less than 4 windows
+ if (WINNAMSIZ <= strlen(ptr->winname)) return -6;
+ if (strlen(DEF_FIELDS) != strlen(ptr->fieldscur)) return -7;
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(8+100*i);
+
+ cnt = sscanf(cp, "\twinflags=%d, sortindx=%u, maxtasks=%d \n",
+ &ptr->winflags, &ptr->sortindx, &ptr->maxtasks
+ );
+ if (cnt != 3) return -(9+100*i);
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(10+100*i);
+
+ cnt = sscanf(cp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n",
+ &ptr->summclr, &ptr->msgsclr, &ptr->headclr, &ptr->taskclr
+ );
+ if (cnt != 4) return -(11+100*i);
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(12+100*i);
+ while (*cp == '\t') { // skip unknown per-window settings
+ cp = strchr(cp, '\n');
+ if (!cp++) return -(13+100*i);
+ }
+ }
+ return 13;
+}
+
+
+
+static int rc_read_old (const char *const buf, RCF_t *rc) {
+ const char std[] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzJj......";
+ const char old[] = "AaBb..CcDd..GgHhIiYyEeWw..FfMmOoTtKkLlPpJjSsVvXxUuZz[{QqNnRr";
+ unsigned u;
+ const char *cp;
+ unsigned c_show = 0;
+ int badchar = 0; // allow a limited number of duplicates and junk
+
+ char scoreboard[256];
+ memset(scoreboard, '\0', sizeof scoreboard);
+
+ cp = buf+2; // skip the "\n\n" we stuck at the beginning
+ u = 0;
+ for (;;) {
+ const char *tmp;
+ int c = *cp++;
+ if (u+1 >= sizeof rc->win[0].fieldscur) return -1;
+ if (c == '\0') return -2;
+ if (c == '\n') break;
+ if (c & ~0x7f) return -3;
+ if (~c & 0x20) c_show |= 1 << (c & 0x1f); // 0x20 means lowercase means hidden
+ if (scoreboard[c|0xe0u]) badchar++; // duplicates not allowed
+ scoreboard[c|0xe0u]++;
+ tmp = strchr(old,c);
+ if (!tmp) continue;
+ c = *((tmp-old)+std);
+ if (c == '.') continue;
+ if (scoreboard[c&0x1fu]) badchar++; // duplicates not allowed
+ scoreboard[c&0x1fu]++;
+ rc->win[0].fieldscur[u++] = c;
+ }
+ rc->win[0].fieldscur[u++] = '\0';
+ if (u < 21) return -6; // catch junk, not good files (had 23 chars in one)
+ if (u > 33) return -7; // catch junk, not good files (had 29 chars in one)
+// fprintf(stderr, "badchar: %d\n", badchar); sleep(2);
+ if (badchar > 8) return -8; // too much junk
+ if (!c_show) return -9; // nothing was shown
+
+ // rest of file is optional, but better look right if it exists
+ if (!*cp) return 12;
+ if (*cp < '2' || *cp > '9') return -13; // stupid, and why isn't '1' valid?
+ rc->delay_time = *cp - '0';
+
+ memset(scoreboard, '\0', sizeof(scoreboard));
+ for (;;) {
+ int c = *++cp & 0xffu; // protect scoreboard[] from negative char
+ if (!c) return -14; // not OK to hit EOL w/o '\n'
+ if (c == '\n') break;
+ switch (c) {
+ case ' ':
+ case '.':
+ case '0' ... '9':
+ return -15; // not supposed to have digits here
+
+// case 's': // mostly for global rcfile
+// rc->mode_secure = 1;
+// break;
+ case 'S':
+ rc->win[0].winflags |= Show_CTIMES;
+ break;
+ case 'c':
+ rc->win[0].winflags |= Show_CMDLIN;
+ break;
+ case 'i':
+ rc->win[0].winflags &= ~Show_IDLEPS;
+ break;
+ case 'H':
+ rc->win[0].winflags |= Show_THREADS;
+ break;
+ case 'm':
+ rc->win[0].winflags &= ~View_MEMORY;
+ break;
+ case 'l':
+ rc->win[0].winflags &= ~View_LOADAV;
+ break;
+ case 't':
+ rc->win[0].winflags &= ~View_STATES;
+ break;
+ case 'I':
+ rc->mode_irixps = 0;
+ break;
+
+ case 'M':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_MEM;
+ break;
+ case 'P':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_CPU;
+ break;
+ case 'A': // supposed to be start_time
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_PID;
+ break;
+ case 'T':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_TM2;
+ break;
+ case 'N':
+ c = 0; // for scoreboard
+ rc->win[0].sortindx = P_PID;
+ break;
+
+ default:
+ // just ignore it, except for the scoreboard of course
+ break;
+ }
+ if (scoreboard[c]) return -16; // duplicates not allowed
+ scoreboard[c] = 1;
+ }
+ return 17;
+}
+
+
+static void rc_write_new (FILE *fp) {
+ int i;
+
+ fprintf(fp, RCF_EYECATCHER "\"%s with windows\"\t\t# shameless braggin'\n",
+ Myname
+ );
+ fprintf(fp, RCF_DEPRECATED
+ "Mode_altscr=%d, Mode_irixps=%d, Delay_time=%.3f, Curwin=%u\n",
+ Rc.mode_altscr, Rc.mode_irixps, Rc.delay_time, (unsigned)(Curwin - Winstk)
+ );
+ for (i = 0; i < GROUPSMAX; i++) {
+ char buf[40];
+ char *cp = Winstk[i].rc.fieldscur;
+ int j = 0;
+
+ while (j < 36) {
+ int c = *cp++ & 0xff;
+ switch (c) {
+ case '.':
+ case 1 ... ' ':
+ case 0x7f ... 0xff:
+ continue; // throw away junk (some of it)
+ default:
+ buf[j++] = c; // gets the '\0' too
+ }
+ if (!c) break;
+ }
+ fprintf(fp, "%s\tfieldscur=%s\n",
+ Winstk[i].rc.winname, buf
+ );
+ fprintf(fp, "\twinflags=%d, sortindx=%d, maxtasks=%d\n",
+ Winstk[i].rc.winflags, Winstk[i].rc.sortindx, Winstk[i].rc.maxtasks
+ );
+ fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n",
+ Winstk[i].rc.summclr, Winstk[i].rc.msgsclr,
+ Winstk[i].rc.headclr, Winstk[i].rc.taskclr
+ );
+ }
+}
+
+
+static const char *rc_write_whatever (void) {
+ FILE *fp = fopen(Rc_name, "w");
+
+ if (!fp) return strerror(errno);
+ rc_write_new(fp);
+ fclose(fp);
+ return NULL;
+}
+
+
+/*###### Startup routines ##############################################*/
+
+// No mater what *they* say, we handle the really really BIG and
+// IMPORTANT stuff upon which all those lessor functions depend!
+static void before (char *me)
+{
+ int i;
+
+ /* setup our program name -- big! */
+ Myname = strrchr(me, '/');
+ if (Myname) ++Myname; else Myname = me;
+
+ /* establish cpu particulars -- even bigger! */
+ Cpu_tot = smp_num_cpus;
+ if (linux_version_code > LINUX_VERSION(2, 5, 41))
+ States_fmts = STATES_line2x5;
+ if (linux_version_code >= LINUX_VERSION(2, 6, 0)) // grrr... only some 2.6.0-testX :-(
+ States_fmts = STATES_line2x6;
+ if (linux_version_code >= LINUX_VERSION(2, 6, 11))
+ States_fmts = STATES_line2x7;
+
+ /* get virtual page size -- nearing huge! */
+ Page_size = getpagesize();
+ i = Page_size;
+ while(i>1024){
+ i >>= 1;
+ page_to_kb_shift++;
+ }
+
+ pcpu_max_value = 99.9;
+
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %1u";
+ if(smp_num_cpus>9){
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %2u";
+ }
+ if(smp_num_cpus>99){
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %3u";
+ }
+ if(smp_num_cpus>999){
+ Fieldstab[P_CPN].head = " P";
+ Fieldstab[P_CPN].fmts = " %4u";
+ }
+
+ {
+ static char pid_fmt[6];
+ unsigned pid_digits = get_pid_digits();
+ if(pid_digits<4) pid_digits=4;
+ snprintf(pid_fmt, sizeof pid_fmt, " %%%uu", pid_digits);
+ Fieldstab[P_PID].fmts = pid_fmt;
+ Fieldstab[P_PID].head = " PID" + 10 - pid_digits;
+ Fieldstab[P_PPD].fmts = pid_fmt;
+ Fieldstab[P_PPD].head = " PPID" + 10 - pid_digits;
+ }
+}
+
+
+// Config file read *helper* function.
+// Anything missing won't show as a choice in the field editor,
+// so make sure there is exactly one of each letter.
+//
+// Due to Rik blindly accepting damem's broken patches, procps-2.0.1x
+// has 3 ("three"!!!) instances of "#C", "LC", or "CPU". Fix that too.
+static void confighlp (char *fields) {
+ unsigned upper[PFLAGSSIZ];
+ unsigned lower[PFLAGSSIZ];
+ char c;
+ char *cp;
+
+ memset(upper, '\0', sizeof upper);
+ memset(lower, '\0', sizeof lower);
+
+ cp = fields;
+ for (;;) {
+ c = *cp++;
+ if (!c) break;
+ if(isupper(c)) upper[c&0x1f]++;
+ else lower[c&0x1f]++;
+ }
+
+ c = 'a';
+ while (c <= 'z') {
+ if (upper[c&0x1f] && lower[c&0x1f]) {
+ lower[c&0x1f] = 0; // got both, so wipe out unseen column
+ for (;;) {
+ cp = strchr(fields, c);
+ if (cp) memmove(cp, cp+1, strlen(cp));
+ else break;
+ }
+ }
+ while (lower[c&0x1f] > 1) { // got too many a..z
+ lower[c&0x1f]--;
+ cp = strchr(fields, c);
+ memmove(cp, cp+1, strlen(cp));
+ }
+ while (upper[c&0x1f] > 1) { // got too many A..Z
+ upper[c&0x1f]--;
+ cp = strchr(fields, toupper(c));
+ memmove(cp, cp+1, strlen(cp));
+ }
+ if (!upper[c&0x1f] && !lower[c&0x1f]) { // both missing
+ lower[c&0x1f]++;
+ memmove(fields+1, fields, strlen(fields)+1);
+ fields[0] = c;
+ }
+ c++;
+ }
+}
+
+
+// First attempt to read the /etc/rcfile which contains two lines
+// consisting of the secure mode switch and an update interval.
+// It's presence limits what ordinary users are allowed to do.
+// (it's actually an old-style config file)
+//
+// Then build the local rcfile name and try to read a crufty old-top
+// rcfile (whew, odoriferous), which may contain an embedded new-style
+// rcfile. Whether embedded or standalone, new-style rcfile values
+// will always override that crufty stuff!
+// note: If running in secure mode via the /etc/rcfile,
+// Delay_time will be ignored except for root.
+static void configs_read (void)
+{
+ const RCF_t def_rcf = DEF_RCFILE;
+ char fbuf[MEDBUFSIZ];
+ int i, fd;
+ RCF_t rcf;
+ float delay = Rc.delay_time;
+
+ // read part of an old-style config in /etc/toprc
+ fd = open(SYS_RCFILESPEC, O_RDONLY);
+ if (fd > 0) {
+ ssize_t num;
+ num = read(fd, fbuf, sizeof(fbuf) - 1);
+ if (num > 0) {
+ const char *sec = strchr(fbuf, 's');
+ const char *eol = strchr(fbuf, '\n');
+ if (eol) {
+ const char *two = eol + 1; // line two
+ if (sec < eol) Secure_mode = !!sec;
+ eol = strchr(two, '\n');
+ if (eol && eol > two && isdigit(*two)) Rc.delay_time = atof(two);
+ }
+ }
+ close(fd);
+ }
+
+ if (getenv("TOPRC")) { // should switch on Myname before documenting this?
+ // not the most optimal here...
+ snprintf(Rc_name, sizeof(Rc_name), "%s", getenv("TOPRC"));
+ } else {
+ snprintf(Rc_name, sizeof(Rc_name), ".%src", Myname); // eeew...
+ if (getenv("HOME"))
+ snprintf(Rc_name, sizeof(Rc_name), "%s/.%src", getenv("HOME"), Myname);
+ }
+
+ rcf = def_rcf;
+ fd = open(Rc_name, O_RDONLY);
+ if (fd > 0) {
+ ssize_t num;
+ num = read(fd, fbuf+2, sizeof(fbuf) -3);
+ if (num > 0) {
+ fbuf[0] = '\n';
+ fbuf[1] = '\n';
+ fbuf[num+2] = '\0';
+//fprintf(stderr,"rc_read_old returns %d\n",rc_read_old(fbuf, &rcf));
+//sleep(2);
+ if (rc_read_new(fbuf, &rcf) < 0) {
+ rcf = def_rcf; // on failure, maybe mangled
+ if (rc_read_old(fbuf, &rcf) < 0) rcf = def_rcf;
+ }
+ delay = rcf.delay_time;
+ }
+ close(fd);
+ }
+
+ // update Rc defaults, establish a Curwin and fix up the window stack
+ Rc.mode_altscr = rcf.mode_altscr;
+ Rc.mode_irixps = rcf.mode_irixps;
+ if (rcf.win_index >= GROUPSMAX) rcf.win_index = 0;
+ Curwin = &Winstk[rcf.win_index];
+ for (i = 0; i < GROUPSMAX; i++) {
+ memcpy(&Winstk[i].rc, &rcf.win[i], sizeof rcf.win[i]);
+ confighlp(Winstk[i].rc.fieldscur);
+ }
+
+ if(Rc.mode_irixps && smp_num_cpus>1){
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+ Fieldstab[P_CPU].fmts = " %4.0f";
+ }
+
+ // lastly, establish the true runtime secure mode and delay time
+ if (!getuid()) Secure_mode = 0;
+ if (!Secure_mode) Rc.delay_time = delay;
+}
+
+
+// Parse command line arguments.
+// Note: it's assumed that the rc file(s) have already been read
+// and our job is to see if any of those options are to be
+// overridden -- we'll force some on and negate others in our
+// best effort to honor the loser's (oops, user's) wishes...
+static void parse_args (char **args)
+{
+ /* differences between us and the former top:
+ -C (separate CPU states for SMP) is left to an rcfile
+ -p (pid monitoring) allows a comma delimited list
+ -q (zero delay) eliminated as redundant, incomplete and inappropriate
+ use: "nice -n-10 top -d0" to achieve what was only claimed
+ -c,i,S act as toggles (not 'on' switches) for enhanced user flexibility
+ . no deprecated/illegal use of 'breakargv:' with goto
+ . bunched args are actually handled properly and none are ignored
+ . we tolerate NO whitespace and NO switches -- maybe too tolerant? */
+ static const char usage[] =
+ " -hv | -bcisSH -d delay -n iterations [-u user | -U user] -p pid [,pid ...]";
+ float tmp_delay = MAXFLOAT;
+ char *p;
+
+ while (*args) {
+ const char *cp = *(args++);
+
+ while (*cp) {
+ switch (*cp) {
+ case '\0':
+ case '-':
+ break;
+ case 'b':
+ Batch = 1;
+ break;
+ case 'c':
+ TOGw(Curwin, Show_CMDLIN);
+ break;
+ case 'd':
+ if (cp[1]) ++cp;
+ else if (*args) cp = *args++;
+ else std_err("-d requires argument");
+ /* a negative delay will be dealt with shortly... */
+ if (sscanf(cp, "%f", &tmp_delay) != 1)
+ std_err(fmtmk("bad delay '%s'", cp));
+ break;
+ case 'H':
+ TOGw(Curwin, Show_THREADS);
+ break;
+ case 'h':
+ case 'v': case 'V':
+ std_out(fmtmk("%s\nusage:\t%s%s", procps_version, Myname, usage));
+ case 'i':
+ TOGw(Curwin, Show_IDLEPS);
+ Curwin->rc.maxtasks = 0;
+ break;
+ case 'n':
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-n requires argument");
+ if (sscanf(cp, "%d", &Loops) != 1 || Loops < 1)
+ std_err(fmtmk("bad iterations arg '%s'", cp));
+ break;
+ case 'p':
+ do {
+ if (selection_type && selection_type != 'p') std_err("conflicting process selection");
+ selection_type = 'p';
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-p argument missing");
+ if (Monpidsidx >= MONPIDMAX)
+ std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
+ if (sscanf(cp, "%d", &Monpids[Monpidsidx]) != 1 || Monpids[Monpidsidx] < 0)
+ std_err(fmtmk("bad pid '%s'", cp));
+ if (!Monpids[Monpidsidx])
+ Monpids[Monpidsidx] = getpid();
+ Monpidsidx++;
+ if (!(p = strchr(cp, ',')))
+ break;
+ cp = p;
+ } while (*cp);
+ break;
+ case 's':
+ Secure_mode = 1;
+ break;
+ case 'S':
+ TOGw(Curwin, Show_CTIMES);
+ break;
+ case 'u':
+ do {
+ const char *errmsg;
+ if (selection_type /* && selection_type != 'u' */) std_err("conflicting process selection");
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-u missing name");
+ errmsg = parse_uid(cp, &selection_uid);
+ if (errmsg) std_err(errmsg);
+ selection_type = 'u';
+ cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
+ } while(0);
+ break;
+ case 'U':
+ do {
+ const char *errmsg;
+ if (selection_type /* && selection_type != 'U' */) std_err("conflicting process selection");
+ if (cp[1]) cp++;
+ else if (*args) cp = *args++;
+ else std_err("-u missing name");
+ errmsg = parse_uid(cp, &selection_uid);
+ if (errmsg) std_err(errmsg);
+ selection_type = 'U';
+ cp += snprintf(Curwin->colusrnam, USRNAMSIZ-1, "%s", cp); // FIXME: junk
+ } while(0);
+ break;
+ default :
+ std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
+ , *cp, Myname, usage));
+
+ } /* end: switch (*cp) */
+
+ /* advance cp and jump over any numerical args used above */
+ if (*cp) cp += strspn(&cp[1], "- ,.1234567890") + 1;
+ } /* end: while (*cp) */
+ } /* end: while (*args) */
+
+ /* fixup delay time, maybe... */
+ if (MAXFLOAT != tmp_delay) {
+ if (Secure_mode || tmp_delay < 0)
+ msg_save("Delay time Not changed");
+ else
+ Rc.delay_time = tmp_delay;
+ }
+}
+
+
+ /*
+ * Set up the terminal attributes */
+static void whack_terminal (void)
+{
+ struct termios newtty;
+
+ if (Batch) {
+ setupterm("dumb", STDOUT_FILENO, NULL);
+ return;
+ }
+ setupterm(NULL, STDOUT_FILENO, NULL);
+ if (tcgetattr(STDIN_FILENO, &Savedtty) == -1)
+ std_err("failed tty get");
+ newtty = Savedtty;
+ newtty.c_lflag &= ~(ICANON | ECHO);
+ newtty.c_oflag &= ~(TAB3);
+ newtty.c_cc[VMIN] = 1;
+ newtty.c_cc[VTIME] = 0;
+
+ Ttychanged = 1;
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty) == -1) {
+ putp(Cap_clr_scr);
+ std_err(fmtmk("failed tty set: %s", strerror(errno)));
+ }
+ tcgetattr(STDIN_FILENO, &Rawtty);
+#ifndef STDOUT_IOLBF
+ // thanks anyway stdio, but we'll manage buffering at the frame level...
+ setbuffer(stdout, Stdout_buf, sizeof(Stdout_buf));
+#endif
+ putp(Cap_clr_scr);
+ fflush(stdout);
+}
+
+
+/*###### Field Selection/Ordering routines #############################*/
+
+
+// Display each field represented in the Fields Table along with its
+// description and mark (with a leading asterisk) fields associated
+// with upper case letter(s) in the passed 'fields string'.
+//
+// After all fields have been displayed, some extra explanatory
+// text may also be output
+static void display_fields (const char *fields, const char *xtra)
+{
+#define yRSVD 3
+ const char *p;
+ int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD;
+
+ /* we're relying on callers to first clear the screen and thus avoid screen
+ flicker if they're too lazy to handle their own asterisk (*) logic */
+ putp(Curwin->cap_bold);
+ for (i = 0; fields[i]; ++i) {
+ const FLD_t *f = ft_get_ptr(FT_NEW_fmt, fields[i]);
+ int b = isupper(fields[i]);
+
+ if (!f) continue; // hey, should be std_err!
+ for (p = f->head; ' ' == *p; ++p) // advance past any leading spaces
+ ;
+ PUTT("%s%s%c %c: %-10s = %s",
+ tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
+ b ? Curwin->cap_bold : Cap_norm,
+ b ? '*' : ' ',
+ fields[i],
+ p,
+ f->desc
+ );
+ }
+ if (xtra) {
+ putp(Curwin->capclr_rownorm);
+ while ((p = strchr(xtra, '\n'))) {
+ ++i;
+ PUTT("%s%.*s",
+ tg2((i / rmax) * cmax, (i % rmax) + yRSVD),
+ (int)(p - xtra),
+ xtra
+ );
+ xtra = ++p;
+ }
+ }
+ putp(Caps_off);
+
+#undef yRSVD
+}
+
+
+// Change order of displayed fields.
+static void fields_reorder (void)
+{
+ static const char prompt[] =
+ "Upper case letter moves field left, lower case right";
+ char c, *p;
+ int i;
+
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ for (;;) {
+ display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
+ show_special(1, fmtmk(FIELDS_current
+ , Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
+ chin(0, &c, 1);
+ if (!ft_get_ptr(FT_NEW_fmt, c)) break;
+ i = toupper(c) - 'A';
+ if (((p = strchr(Curwin->rc.fieldscur, i + 'A')))
+ || ((p = strchr(Curwin->rc.fieldscur, i + 'a')))) {
+ if (isupper(c)) p--;
+ if (('\0' != p[1]) && (p >= Curwin->rc.fieldscur)) {
+ c = p[0];
+ p[0] = p[1];
+ p[1] = c;
+ }
+ }
+ }
+ putp(Cap_curs_norm);
+}
+
+// Select sort field.
+static void fields_sort (void)
+{
+ static const char prompt[] =
+ "Select sort field via field letter, type any other key to return";
+ char phoney[PFLAGSSIZ];
+ char c, *p;
+ int i, x;
+
+ strcpy(phoney, NUL_FIELDS);
+ x = i = Curwin->rc.sortindx;
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ for (;;) {
+ p = phoney + i;
+ *p = toupper(*p);
+ display_fields(phoney, SORT_xtra);
+ show_special(1, fmtmk(SORT_fields, Cap_home, *p, Curwin->grpname, prompt));
+ chin(0, &c, 1);
+ if (!ft_get_ptr(FT_NEW_fmt, c)) break;
+ i = toupper(c) - 'A';
+ *p = tolower(*p);
+ x = i;
+ }
+ if ((p = strchr(Curwin->rc.fieldscur, x + 'a')))
+ *p = x + 'A';
+ Curwin->rc.sortindx = x;
+ putp(Cap_curs_norm);
+}
+
+
+// Toggle displayed fields.
+static void fields_toggle (void)
+{
+ static const char prompt[] =
+ "Toggle fields via field letter, type any other key to return";
+ char c, *p;
+ int i;
+
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ for (;;) {
+ display_fields(Curwin->rc.fieldscur, FIELDS_xtra);
+ show_special(1, fmtmk(FIELDS_current, Cap_home, Curwin->rc.fieldscur, Curwin->grpname, prompt));
+ chin(0, &c, 1);
+ if (!ft_get_ptr(FT_NEW_fmt, c)) break;
+ i = toupper(c) - 'A';
+ if ((p = strchr(Curwin->rc.fieldscur, i + 'A')))
+ *p = i + 'a';
+ else if ((p = strchr(Curwin->rc.fieldscur, i + 'a')))
+ *p = i + 'A';
+ }
+ putp(Cap_curs_norm);
+}
+
+/*###### Windows/Field Groups support #################################*/
+
+// For each of the four windows:
+// 1) Set the number of fields/columns to display
+// 2) Create the field columns heading
+// 3) Set maximum cmdline length, if command lines are in use
+// In the process, the required PROC_FILLxxx flags will be rebuilt!
+static void reframewins (void)
+{
+ WIN_t *w;
+ char *s;
+ const char *h;
+ int i, needpsdb = 0;
+
+// Frames_libflags = 0; // should be called only when it's zero
+// Frames_maxcmdln = 0; // to become largest from up to 4 windows, if visible
+ w = Curwin;
+ do {
+ if (!Rc.mode_altscr || CHKw(w, VISIBLE_tsk)) {
+ // build window's procflags array and establish a tentative maxpflgs
+ for (i = 0, w->maxpflgs = 0; w->rc.fieldscur[i]; i++) {
+ if (isupper(w->rc.fieldscur[i]))
+ w->procflags[w->maxpflgs++] = w->rc.fieldscur[i] - 'A';
+ }
+
+ /* build a preliminary columns header not to exceed screen width
+ while accounting for a possible leading window number */
+ *(s = w->columnhdr) = '\0';
+ if (Rc.mode_altscr) s = scat(s, " ");
+ for (i = 0; i < w->maxpflgs; i++) {
+ h = Fieldstab[w->procflags[i]].head;
+ // oops, won't fit -- we're outta here...
+ if (Screen_cols+1 < (int)((s - w->columnhdr) + strlen(h))) break;
+ s = scat(s, h);
+ }
+
+ // establish the final maxpflgs and prepare to grow the command column
+ // heading via maxcmdln - it may be a fib if P_CMD wasn't encountered,
+ // but that's ok because it won't be displayed anyway
+ w->maxpflgs = i;
+ w->maxcmdln = Screen_cols - (strlen(w->columnhdr) - strlen(Fieldstab[P_CMD].head));
+
+ // finally, we can build the true run-time columns header, format the
+ // command column heading, if P_CMD is really being displayed, and
+ // rebuild the all-important PROC_FILLxxx flags that will be used
+ // until/if we're we're called again
+ *(s = w->columnhdr) = '\0';
+// if (Rc.mode_altscr) s = scat(s, fmtmk("%d", w->winnum));
+ for (i = 0; i < w->maxpflgs; i++) {
+ int advance = (i==0) && !Rc.mode_altscr;
+ h = Fieldstab[w->procflags[i]].head;
+ if (P_WCH == w->procflags[i]) needpsdb = 1;
+ if (P_CMD == w->procflags[i]) {
+ s = scat(s, fmtmk(Fieldstab[P_CMD].fmts+advance, w->maxcmdln, w->maxcmdln, "COMMAND"/*h*/ ));
+ if (CHKw(w, Show_CMDLIN)) {
+ Frames_libflags |= L_CMDLINE;
+// if (w->maxcmdln > Frames_maxcmdln) Frames_maxcmdln = w->maxcmdln;
+ }
+ } else
+ s = scat(s, h+advance);
+ Frames_libflags |= Fieldstab[w->procflags[i]].lflg;
+ }
+ if (Rc.mode_altscr) w->columnhdr[0] = w->winnum + '0';
+ }
+ if (Rc.mode_altscr) w = w->next;
+ } while (w != Curwin);
+
+ // do we need the kernel symbol table (and is it already open?)
+ if (needpsdb) {
+ if (No_ksyms == -1) {
+ No_ksyms = 0;
+ if (open_psdb_message(NULL, msg_save))
+ No_ksyms = 1;
+ else
+ PSDBopen = 1;
+ }
+ }
+
+ if (selection_type=='U') Frames_libflags |= L_status;
+
+ if (Frames_libflags & L_EITHER) {
+ Frames_libflags &= ~L_EITHER;
+ if (!(Frames_libflags & L_stat)) Frames_libflags |= L_status;
+ }
+ if (!Frames_libflags) Frames_libflags = L_DEFAULT;
+ if (selection_type=='p') Frames_libflags |= PROC_PID;
+}
+
+
+// Value a window's name and make the associated group name.
+static void win_names (WIN_t *q, const char *name)
+{
+ sprintf(q->rc.winname, "%.*s", WINNAMSIZ -1, name);
+ sprintf(q->grpname, "%d:%.*s", q->winnum, WINNAMSIZ -1, name);
+}
+
+
+// Display a window/field group (ie. make it "current").
+static void win_select (char ch)
+{
+ static const char prompt[] = "Choose field group (1 - 4)";
+
+ /* if there's no ch, it means we're supporting the external interface,
+ so we must try to get our own darn ch by begging the user... */
+ if (!ch) {
+ show_pmt(prompt);
+ chin(0, (char *)&ch, 1);
+ }
+ switch (ch) {
+ case 'a': /* we don't carry 'a' / 'w' in our */
+ Curwin = Curwin->next; /* pmt - they're here for a good */
+ break; /* friend of ours -- wins_colors. */
+ case 'w': /* (however those letters work via */
+ Curwin = Curwin->prev; /* the pmt too but gee, end-loser */
+ break; /* should just press the darn key) */
+ case '1': case '2':
+ case '3': case '4':
+ Curwin = &Winstk[ch - '1'];
+ break;
+ }
+}
+
+
+// Just warn the user when a command can't be honored.
+static int win_warn (void)
+{
+ show_msg(fmtmk("\aCommand disabled, activate %s with '-' or '_'", Curwin->grpname));
+ // we gotta' return false 'cause we're somewhat well known within
+ // macro society, by way of that sassy little tertiary operator...
+ return 0;
+}
+
+
+// Change colors *Helper* function to save/restore settings;
+// ensure colors will show; and rebuild the terminfo strings.
+static void winsclrhlp (WIN_t *q, int save)
+{
+ static int flgssav, summsav, msgssav, headsav, tasksav;
+
+ if (save) {
+ flgssav = q->rc.winflags; summsav = q->rc.summclr;
+ msgssav = q->rc.msgsclr; headsav = q->rc.headclr; tasksav = q->rc.taskclr;
+ SETw(q, Show_COLORS);
+ } else {
+ q->rc.winflags = flgssav; q->rc.summclr = summsav;
+ q->rc.msgsclr = msgssav; q->rc.headclr = headsav; q->rc.taskclr = tasksav;
+ }
+ capsmk(q);
+}
+
+
+// Change colors used in display
+static void wins_colors (void)
+{
+#define kbdABORT 'q'
+#define kbdAPPLY '\n'
+ int clr = Curwin->rc.taskclr, *pclr = &Curwin->rc.taskclr;
+ char ch, tgt = 'T';
+
+ if (0 >= max_colors) {
+ show_msg("\aNo colors to map!");
+ return;
+ }
+ winsclrhlp(Curwin, 1);
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+
+ do {
+ putp(Cap_home);
+ /* this string is well above ISO C89's minimum requirements! */
+ show_special(
+ 1,
+ fmtmk(
+ COLOR_help,
+ procps_version,
+ Curwin->grpname,
+ CHKw(Curwin, View_NOBOLD) ? "On" : "Off",
+ CHKw(Curwin, Show_COLORS) ? "On" : "Off",
+ CHKw(Curwin, Show_HIBOLD) ? "On" : "Off",
+ tgt,
+ clr,
+ Curwin->grpname
+ )
+ );
+ chin(0, &ch, 1);
+ switch (ch) {
+ case 'S':
+ pclr = &Curwin->rc.summclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'M':
+ pclr = &Curwin->rc.msgsclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'H':
+ pclr = &Curwin->rc.headclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case 'T':
+ pclr = &Curwin->rc.taskclr;
+ clr = *pclr;
+ tgt = ch;
+ break;
+ case '0' ... '7':
+ clr = ch - '0';
+ *pclr = clr;
+ break;
+ case 'B':
+ TOGw(Curwin, View_NOBOLD);
+ break;
+ case 'b':
+ TOGw(Curwin, Show_HIBOLD);
+ break;
+ case 'z':
+ TOGw(Curwin, Show_COLORS);
+ break;
+ case 'a':
+ case 'w':
+ win_select(ch);
+ winsclrhlp(Curwin, 1);
+ clr = Curwin->rc.taskclr, pclr = &Curwin->rc.taskclr;
+ tgt = 'T';
+ break;
+ }
+ capsmk(Curwin);
+ } while (kbdAPPLY != ch && kbdABORT != ch);
+
+ if (kbdABORT == ch)
+ winsclrhlp(Curwin, 0);
+ putp(Cap_curs_norm);
+
+#undef kbdABORT
+#undef kbdAPPLY
+}
+
+
+// Manipulate flag(s) for all our windows.
+static void wins_reflag (int what, int flg)
+{
+ WIN_t *w;
+
+ w = Curwin;
+ do {
+ switch (what) {
+ case Flags_TOG:
+ TOGw(w, flg);
+ break;
+ case Flags_SET: /* Ummmm, i can't find anybody */
+ SETw(w, flg); /* who uses Flags_set ... */
+ break;
+ case Flags_OFF:
+ OFFw(w, flg);
+ break;
+ }
+ // a flag with special significance -- user wants to rebalance
+ // display so we gotta' 'off' one number then force on two flags...
+ if (EQUWINS_cwo == flg) {
+ w->rc.maxtasks = 0;
+ SETw(w, Show_IDLEPS | VISIBLE_tsk);
+ }
+ w = w->next;
+ } while (w != Curwin);
+}
+
+
+// using a flag to avoid other code seeing inconsistant state
+static volatile int need_resize;
+static void wins_resize_sighandler (int dont_care_sig)
+{
+ (void)dont_care_sig;
+ need_resize = 1;
+ ZAP_TIMEOUT
+}
+
+
+// Set the screen dimensions and arrange for the real workhorse.
+// (also) catches:
+// SIGWINCH and SIGCONT
+static void wins_resize (void)
+{
+ struct winsize wz;
+ char *env_columns; // Unix98 environment variable COLUMNS
+ char *env_lines; // Unix98 environment variable LINES
+
+ Screen_cols = columns; // <term.h>
+ Screen_rows = lines; // <term.h>
+
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz) != -1 && wz.ws_col>0 && wz.ws_row>0) {
+ Screen_cols = wz.ws_col;
+ Screen_rows = wz.ws_row;
+ }
+
+ if (Batch) Screen_rows = MAXINT;
+
+ env_columns = getenv("COLUMNS");
+ if(env_columns && *env_columns){
+ long t;
+ char *endptr;
+ t = strtol(env_columns, &endptr, 0);
+ if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_cols = (int)t;
+ }
+ env_lines = getenv("LINES");
+ if(env_lines && *env_lines){
+ long t;
+ char *endptr;
+ t = strtol(env_lines, &endptr, 0);
+ if(!*endptr && (t>0) && (t<=0x7fffffffL)) Screen_rows = (int)t;
+ }
+
+ // be crudely tolerant of crude tty emulators
+ if (avoid_last_column) Screen_cols--;
+
+ // we might disappoint some folks (but they'll deserve it)
+ if (SCREENMAX < Screen_cols) Screen_cols = SCREENMAX;
+
+ // keep our support for output optimization in sync with current reality
+ // note: when we're in Batch mode, we don't really need a Pseudo_scrn and
+ // when not Batch, our buffer will contain 1 extra 'line' since
+ // Msg_row is never represented -- but it's nice to have some space
+ // between us and the great-beyond...
+ Pseudo_cols = Screen_cols + CLRBUFSIZ + 1;
+ if (Batch) Pseudo_size = ROWBUFSIZ + 1;
+ else Pseudo_size = Pseudo_cols * Screen_rows;
+ Pseudo_scrn = alloc_r(Pseudo_scrn, Pseudo_size);
+
+ // force rebuild of column headers AND libproc/readproc requirements
+ Frames_libflags = 0;
+}
+
+
+// Set up the raw/incomplete field group windows --
+// they'll be finished off after startup completes.
+// [ and very likely that will override most/all of our efforts ]
+// [ --- life-is-NOT-fair --- ]
+static void windows_stage1 (void)
+{
+ WIN_t *w;
+ int i;
+
+ for (i = 0; i < GROUPSMAX; i++) {
+ w = &Winstk[i];
+ w->winnum = i + 1;
+ w->rc = Rc.win[i];
+ w->captab[0] = Cap_norm;
+ w->captab[1] = Cap_norm;
+ w->captab[2] = w->cap_bold;
+ w->captab[3] = w->capclr_sum;
+ w->captab[4] = w->capclr_msg;
+ w->captab[5] = w->capclr_pmt;
+ w->captab[6] = w->capclr_hdr;
+ w->captab[7] = w->capclr_rowhigh;
+ w->captab[8] = w->capclr_rownorm;
+ w->next = w + 1;
+ w->prev = w - 1;
+ ++w;
+ }
+ /* fixup the circular chains... */
+ Winstk[3].next = &Winstk[0];
+ Winstk[0].prev = &Winstk[3];
+ Curwin = Winstk;
+}
+
+
+// This guy just completes the field group windows after the
+// rcfiles have been read and command line arguments parsed
+static void windows_stage2 (void)
+{
+ int i;
+
+ for (i = 0; i < GROUPSMAX; i++) {
+ win_names(&Winstk[i], Winstk[i].rc.winname);
+ capsmk(&Winstk[i]);
+ }
+ // rely on this next guy to force a call (eventually) to reframewins
+ wins_resize();
+}
+
+
+/*###### Main Screen routines ##########################################*/
+
+// Process keyboard input during the main loop
+static void do_key (unsigned c)
+{
+ // standardized 'secure mode' errors
+ static const char err_secure[] = "\aUnavailable in secure mode";
+ static const char err_num_cpus[] = "\aSorry, terminal is not big enough";
+#ifdef WARN_NOT_SMP
+ // standardized 'smp' errors
+ static const char err_smp[] = "\aSorry, only 1 cpu detected";
+#endif
+
+ switch (c) {
+ case '1':
+ if (Cpu_tot+7 > Screen_rows && CHKw(Curwin, View_CPUSUM)) {
+ show_msg(err_num_cpus);
+ break;
+ }
+#ifdef WARN_NOT_SMP
+ if (Cpu_tot > 1) TOGw(Curwin, View_CPUSUM);
+ else show_msg(err_smp);
+#else
+ TOGw(Curwin, View_CPUSUM);
+#endif
+ break;
+
+ case 'a':
+ if (Rc.mode_altscr) Curwin = Curwin->next;
+ break;
+
+ case 'A':
+ Rc.mode_altscr = !Rc.mode_altscr;
+ wins_resize();
+ break;
+
+ case 'b':
+ if (VIZCHKc) {
+ if (!CHKw(Curwin, Show_HICOLS | Show_HIROWS))
+ show_msg("\aNothing to highlight!");
+ else {
+ TOGw(Curwin, Show_HIBOLD);
+ capsmk(Curwin);
+ }
+ }
+ break;
+
+ case 'B':
+ TOGw(Curwin, View_NOBOLD);
+ capsmk(Curwin);
+ break;
+
+ case 'c':
+ VIZTOGc(Show_CMDLIN);
+ break;
+
+ case 'd':
+ case 's':
+ if (Secure_mode)
+ show_msg(err_secure);
+ else {
+ float tmp =
+ get_float(fmtmk("Change delay from %.1f to", Rc.delay_time));
+ if (tmp > -1) Rc.delay_time = tmp;
+ }
+ break;
+
+ case 'f':
+ if (VIZCHKc) fields_toggle();
+ break;
+
+ case 'F':
+ case 'O':
+ if (VIZCHKc) fields_sort();
+ break;
+
+ case 'g':
+ if (Rc.mode_altscr) {
+ char tmp[GETBUFSIZ];
+ strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (1-3 chars)", Curwin->rc.winname)));
+ if (tmp[0]) win_names(Curwin, tmp);
+ }
+ break;
+
+ case 'G':
+ win_select(0);
+ break;
+
+ case 'H':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_THREADS);
+ show_msg(fmtmk("Show threads %s"
+ , CHKw(Curwin, Show_THREADS) ? "On" : "Off"));
+ }
+ break;
+
+ case 'h':
+ case '?':
+ { char ch;
+ putp(Cap_clr_scr);
+ putp(Cap_curs_huge);
+ /* this string is well above ISO C89's minimum requirements! */
+ show_special(
+ 1,
+ fmtmk(
+ KEYS_help,
+ procps_version,
+ Curwin->grpname,
+ CHKw(Curwin, Show_CTIMES) ? "On" : "Off",
+ Rc.delay_time,
+ Secure_mode ? "On" : "Off",
+ Secure_mode ? "" : KEYS_help_unsecured
+ )
+ );
+ chin(0, &ch, 1);
+ if ('?' == ch || 'h' == ch) {
+ do {
+ putp(Cap_clr_scr);
+ show_special(1, fmtmk(WINDOWS_help
+ , Curwin->grpname
+ , Winstk[0].rc.winname
+ , Winstk[1].rc.winname
+ , Winstk[2].rc.winname
+ , Winstk[3].rc.winname));
+ chin(0, &ch, 1);
+ win_select(ch);
+ } while ('\n' != ch);
+ }
+ putp(Cap_curs_norm);
+ }
+ break;
+
+ case 'i':
+ VIZTOGc(Show_IDLEPS);
+ break;
+
+ case 'I':
+#ifdef WARN_NOT_SMP
+ if (Cpu_tot > 1) {
+ Rc.mode_irixps = !Rc.mode_irixps;
+ show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
+ } else
+ show_msg(err_smp);
+#else
+ Rc.mode_irixps = !Rc.mode_irixps;
+ show_msg(fmtmk("Irix mode %s", Rc.mode_irixps ? "On" : "Off"));
+#endif
+ if(Rc.mode_irixps && smp_num_cpus>1){
+ // good for 100 CPUs per process
+ pcpu_max_value = 9999.0;
+ Fieldstab[P_CPU].fmts = " %4.0f";
+ } else {
+ pcpu_max_value = 99.9;
+ Fieldstab[P_CPU].fmts = " %#4.1f";
+ }
+ break;
+
+ case 'k':
+ if (Secure_mode) {
+ show_msg(err_secure);
+ } else {
+ int sig, pid = get_int("PID to kill");
+ if (pid > 0) {
+ sig = signal_name_to_number(
+ ask4str(fmtmk("Kill PID %d with signal [%i]", pid, DEF_SIGNAL)));
+ if (sig == -1) sig = DEF_SIGNAL;
+ if (sig && kill(pid, sig))
+ show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s", pid, sig, strerror(errno)));
+ }
+ }
+ break;
+
+ case 'l':
+ TOGw(Curwin, View_LOADAV);
+ break;
+
+ case 'm':
+ TOGw(Curwin, View_MEMORY);
+ break;
+
+ case 'n':
+ case '#':
+ if (VIZCHKc) {
+ int num =
+ get_int(fmtmk("Maximum tasks = %d, change to (0 is unlimited)", Curwin->rc.maxtasks));
+ if (num > -1) Curwin->rc.maxtasks = num;
+ }
+ break;
+
+ case 'o':
+ if (VIZCHKc) fields_reorder();
+ break;
+
+ case 'q':
+ end_pgm(0);
+
+ case 'r':
+ if (Secure_mode)
+ show_msg(err_secure);
+ else {
+ int val, pid = get_int("PID to renice");
+ if (pid > 0) {
+ val = get_int(fmtmk("Renice PID %d to value", pid));
+ if (setpriority(PRIO_PROCESS, (unsigned)pid, val))
+ show_msg(fmtmk("\aRenice of PID %d to %d failed: %s", pid, val, strerror(errno)));
+ }
+ }
+ break;
+
+ case 'R':
+ VIZTOGc(Qsrt_NORMAL);
+ break;
+
+ case 'S':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_CTIMES);
+ show_msg(fmtmk("Cumulative time %s", CHKw(Curwin, Show_CTIMES) ? "On" : "Off"));
+ }
+ break;
+
+ case 't':
+ TOGw(Curwin, View_STATES);
+ break;
+
+// case 'u':
+// if (VIZCHKc)
+// strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)"));
+// break;
+
+ case 'u':
+// if (!VIZCHKc) break;
+ do {
+ const char *errmsg;
+ const char *answer;
+ answer = ask4str("Which user (blank for all)");
+ // FIXME: do this better:
+ if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
+ selection_type = 0;
+ selection_uid = -1;
+ break;
+ }
+ errmsg = parse_uid(answer, &selection_uid);
+ if (errmsg) {
+ show_msg(errmsg);
+ // Change settings here? I guess not.
+ break;
+ }
+ selection_type = 'u';
+ } while(0);
+ break;
+
+ case 'U':
+// if (!VIZCHKc) break;
+ do {
+ const char *errmsg;
+ const char *answer;
+ answer = ask4str("Which user (blank for all)");
+ // FIXME: do this better:
+ if (!answer || *answer=='\0' || *answer=='\n' || *answer=='\r' || *answer=='\t' || *answer==' ') {
+ selection_type = 0;
+ selection_uid = -1;
+ break;
+ }
+ errmsg = parse_uid(answer, &selection_uid);
+ if (errmsg) {
+ show_msg(errmsg);
+ // Change settings here? I guess not.
+ break;
+ }
+ selection_type = 'U';
+ } while(0);
+ break;
+
+ case 'w':
+ if (Rc.mode_altscr) Curwin = Curwin->prev;
+ break;
+
+ case 'W':
+ { const char *err = rc_write_whatever();
+ if (err)
+ show_msg(fmtmk("\aFailed '%s' open: %s", Rc_name, err));
+ else
+ show_msg(fmtmk("Wrote configuration to '%s'", Rc_name));
+ }
+ break;
+
+ case 'x':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_HICOLS);
+ capsmk(Curwin);
+ }
+ break;
+
+ case 'y':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_HIROWS);
+ capsmk(Curwin);
+ }
+ break;
+
+ case 'z':
+ if (VIZCHKc) {
+ TOGw(Curwin, Show_COLORS);
+ capsmk(Curwin);
+ }
+ break;
+
+ case 'Z':
+ wins_colors();
+ break;
+
+ case '-':
+ if (Rc.mode_altscr) TOGw(Curwin, VISIBLE_tsk);
+ break;
+
+ case '_':
+ if (Rc.mode_altscr) wins_reflag(Flags_TOG, VISIBLE_tsk);
+ break;
+
+ case '=':
+ Curwin->rc.maxtasks = 0;
+ SETw(Curwin, Show_IDLEPS | VISIBLE_tsk);
+ Monpidsidx = 0;
+ selection_type = '\0';
+ break;
+
+ case '+':
+ if (Rc.mode_altscr) SETw(Curwin, EQUWINS_cwo);
+ break;
+
+ case '<':
+ if (VIZCHKc) {
+ FLG_t *p = Curwin->procflags + Curwin->maxpflgs - 1;
+ while (*p != Curwin->rc.sortindx) --p;
+ if (--p >= Curwin->procflags)
+ Curwin->rc.sortindx = *p;
+ }
+ break;
+
+ case '>':
+ if (VIZCHKc) {
+ FLG_t *p = Curwin->procflags;
+ while (*p != Curwin->rc.sortindx) ++p;
+ if (++p < Curwin->procflags + Curwin->maxpflgs)
+ Curwin->rc.sortindx = *p;
+ }
+ break;
+
+ case 'M': // these keys represent old-top compatability
+ case 'N': // -- grouped here so that if users could ever
+ case 'P': // be weaned, we would just whack this part...
+ case 'T':
+ { static struct {
+ const char *xmsg;
+ const unsigned xkey;
+ const FLG_t sort;
+ } xtab[] = {
+ { "Memory", 'M', P_MEM, }, { "Numerical", 'N', P_PID, },
+ { "CPU", 'P', P_CPU, }, { "Time", 'T', P_TM2 }, };
+ int i;
+ for (i = 0; i < MAXTBL(xtab); ++i)
+ if (c == xtab[i].xkey) {
+ Curwin->rc.sortindx = xtab[i].sort;
+// show_msg(fmtmk("%s sort compatibility key honored", xtab[i].xmsg));
+ break;
+ }
+ }
+ break;
+
+ case '\n': // just ignore these, they'll have the effect
+ case ' ': // of refreshing display after waking us up !
+ break;
+
+ default:
+ show_msg("\aUnknown command - try 'h' for help");
+ }
+ // The following assignment will force a rebuild of all column headers and
+ // the PROC_FILLxxx flags. It's NOT simply lazy programming. Here are
+ // some keys that COULD require new column headers and/or libproc flags:
+ // 'A' - likely
+ // 'c' - likely when !Mode_altscr, maybe when Mode_altscr
+ // 'F' - maybe, if new field forced on
+ // 'f' - likely
+ // 'G' - likely
+ // 'O' - maybe, if new field forced on
+ // 'o' - maybe, if new field brought into view
+ // 'Z' - likely, if 'Curwin' changed when !Mode_altscr
+ // '-' - likely (restricted to Mode_altscr)
+ // '_' - likely (restricted to Mode_altscr)
+ // '=' - maybe, but only when Mode_altscr
+ // '+' - likely (restricted to Mode_altscr)
+ // ( At this point we have a human being involved and so have all the time )
+ // ( in the world. We can afford a few extra cpu cycles every now & then! )
+ Frames_libflags = 0;
+}
+
+
+// State display *Helper* function to calc and display the state
+// percentages for a single cpu. In this way, we can support
+// the following environments without the usual code bloat.
+// 1) single cpu machines
+// 2) modest smp boxes with room for each cpu's percentages
+// 3) massive smp guys leaving little or no room for process
+// display and thus requiring the cpu summary toggle
+static void summaryhlp (CPU_t *cpu, const char *pfx)
+{
+ // we'll trim to zero if we get negative time ticks,
+ // which has happened with some SMP kernels (pre-2.4?)
+#define TRIMz(x) ((tz = (SIC_t)(x)) < 0 ? 0 : tz)
+ SIC_t u_frme, s_frme, n_frme, i_frme, w_frme, x_frme, y_frme, z_frme, tot_frme, tz;
+ float scale;
+
+ u_frme = cpu->u - cpu->u_sav;
+ s_frme = cpu->s - cpu->s_sav;
+ n_frme = cpu->n - cpu->n_sav;
+ i_frme = TRIMz(cpu->i - cpu->i_sav);
+ w_frme = cpu->w - cpu->w_sav;
+ x_frme = cpu->x - cpu->x_sav;
+ y_frme = cpu->y - cpu->y_sav;
+ z_frme = cpu->z - cpu->z_sav;
+ tot_frme = u_frme + s_frme + n_frme + i_frme + w_frme + x_frme + y_frme + z_frme;
+ if (tot_frme < 1) tot_frme = 1;
+ scale = 100.0 / (float)tot_frme;
+
+ // display some kinda' cpu state percentages
+ // (who or what is explained by the passed prefix)
+ show_special(
+ 0,
+ fmtmk(
+ States_fmts,
+ pfx,
+ (float)u_frme * scale,
+ (float)s_frme * scale,
+ (float)n_frme * scale,
+ (float)i_frme * scale,
+ (float)w_frme * scale,
+ (float)x_frme * scale,
+ (float)y_frme * scale,
+ (float)z_frme * scale
+ )
+ );
+ Msg_row += 1;
+
+ // remember for next time around
+ cpu->u_sav = cpu->u;
+ cpu->s_sav = cpu->s;
+ cpu->n_sav = cpu->n;
+ cpu->i_sav = cpu->i;
+ cpu->w_sav = cpu->w;
+ cpu->x_sav = cpu->x;
+ cpu->y_sav = cpu->y;
+ cpu->z_sav = cpu->z;
+
+#undef TRIMz
+}
+
+
+// Begin a new frame by:
+// 1) Refreshing the all important proc table
+// 2) Displaying uptime and load average (maybe)
+// 3) Displaying task/cpu states (maybe)
+// 4) Displaying memory & swap usage (maybe)
+// and then, returning a pointer to the pointers to the proc_t's!
+static proc_t **summary_show (void)
+{
+ static proc_t **p_table = NULL;
+ static CPU_t *smpcpu = NULL;
+
+ // whoa first time, gotta' prime the pump...
+ if (!p_table) {
+ p_table = procs_refresh(NULL, Frames_libflags);
+ putp(Cap_clr_scr);
+ putp(Cap_rmam);
+#ifndef PROF
+ // sleep for half a second
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+ select(0, NULL, NULL, NULL, &tv); // ought to loop until done
+#endif
+ } else {
+ putp(Batch ? "\n\n" : Cap_home);
+ }
+ p_table = procs_refresh(p_table, Frames_libflags);
+
+ // Display Uptime and Loadavg
+ if (CHKw(Curwin, View_LOADAV)) {
+ if (!Rc.mode_altscr) {
+ show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime()));
+ } else {
+ show_special(
+ 0,
+ fmtmk(
+ CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line,
+ Curwin->grpname,
+ sprint_uptime()
+ )
+ );
+ }
+ Msg_row += 1;
+ }
+
+ // Display Task and Cpu(s) States
+ if (CHKw(Curwin, View_STATES)) {
+ show_special(
+ 0,
+ fmtmk(
+ STATES_line1,
+ Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied
+ )
+ );
+ Msg_row += 1;
+
+ smpcpu = cpus_refresh(smpcpu);
+
+ if (CHKw(Curwin, View_CPUSUM)) {
+ // display just the 1st /proc/stat line
+ summaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");
+ } else {
+ int i;
+ char tmp[SMLBUFSIZ];
+ // display each cpu's states separately
+ for (i = 0; i < Cpu_tot; i++) {
+ snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id);
+ summaryhlp(&smpcpu[i], tmp);
+ }
+ }
+ }
+
+ // Display Memory and Swap stats
+ meminfo();
+ if (CHKw(Curwin, View_MEMORY)) {
+ show_special(0, fmtmk(MEMORY_line1
+ , kb_main_total, kb_main_used, kb_main_free, kb_main_buffers));
+ show_special(0, fmtmk(MEMORY_line2
+ , kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached));
+ Msg_row += 2;
+ }
+
+ SETw(Curwin, NEWFRAM_cwo);
+ return p_table;
+}
+
+
+#define PAGES_TO_KB(n) (unsigned long)( (n) << page_to_kb_shift )
+
+// the following macro is our means to 'inline' emitting a column -- next to
+// procs_refresh, that's the most frequent and costly part of top's job !
+#define MKCOL(va...) do { \
+ if(likely(!( CHKw(q, Show_HICOLS) && q->rc.sortindx==i ))) { \
+ snprintf(cbuf, sizeof(cbuf), f, ## va); \
+ } else { \
+ snprintf(_z, sizeof(_z), f, ## va); \
+ snprintf(cbuf, sizeof(cbuf), "%s%s%s", \
+ q->capclr_rowhigh, \
+ _z, \
+ !(CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rownorm : "" \
+ ); \
+ pad += q->len_rowhigh; \
+ if (!(CHKw(q, Show_HIROWS) && 'R' == p->state)) pad += q->len_rownorm; \
+ } \
+} while (0)
+
+// Display information for a single task row.
+static void task_show (const WIN_t *q, const proc_t *p)
+{
+ char rbuf[ROWBUFSIZ];
+ char *rp = rbuf;
+ int j, x, pad;
+
+ *rp = '\0';
+
+ pad = Rc.mode_altscr;
+// if (pad) rp = scat(rp, " ");
+
+ for (x = 0; x < q->maxpflgs; x++) {
+ char cbuf[ROWBUFSIZ], _z[ROWBUFSIZ];
+ FLG_t i = q->procflags[x]; // support for our field/column
+ const char *f = Fieldstab[i].fmts; // macro AND sometimes the fmt
+ unsigned s = Fieldstab[i].scale; // string must be altered !
+ unsigned w = Fieldstab[i].width;
+
+ int advance = (x==0) && !Rc.mode_altscr;
+
+ switch (i) {
+ case P_CMD:
+ { char tmp[ROWBUFSIZ];
+ unsigned flags;
+ int maxcmd = q->maxcmdln;
+ if (CHKw(q, Show_CMDLIN)) flags = ESC_DEFUNCT | ESC_BRACKETS | ESC_ARGS;
+ else flags = ESC_DEFUNCT;
+ escape_command(tmp, p, sizeof tmp, &maxcmd, flags);
+ MKCOL(q->maxcmdln, q->maxcmdln, tmp);
+ }
+ break;
+ case P_COD:
+ MKCOL(scale_num(PAGES_TO_KB(p->trs), w, s));
+ break;
+ case P_CPN:
+ MKCOL((unsigned)p->processor);
+ break;
+ case P_CPU:
+ { float u = (float)p->pcpu * Frame_tscale;
+ if (u > pcpu_max_value) u = pcpu_max_value;
+ MKCOL(u);
+ }
+ break;
+ case P_DAT:
+ MKCOL(scale_num(PAGES_TO_KB(p->drs), w, s));
+ break;
+ case P_DRT:
+ MKCOL(scale_num((unsigned)p->dt, w, s));
+ break;
+ case P_FLG:
+ { char tmp[TNYBUFSIZ];
+ snprintf(tmp, sizeof(tmp), f, (long)p->flags);
+ for (j = 0; tmp[j]; j++) if ('0' == tmp[j]) tmp[j] = '.';
+ f = tmp;
+ MKCOL("");
+ }
+ break;
+ case P_FLT:
+ MKCOL(scale_num(p->maj_flt, w, s));
+ break;
+ case P_GRP:
+ MKCOL(p->egroup);
+ break;
+ case P_MEM:
+ MKCOL((float)PAGES_TO_KB(p->resident) * 100 / kb_main_total);
+ break;
+ case P_NCE:
+ MKCOL((int)p->nice);
+ break;
+ case P_PID:
+ MKCOL((unsigned)p->XXXID);
+ break;
+ case P_PPD:
+ MKCOL((unsigned)p->ppid);
+ break;
+ case P_PRI:
+ if (unlikely(-99 > p->priority) || unlikely(999 < p->priority)) {
+ f = " RT";
+ MKCOL("");
+ } else
+ MKCOL((int)p->priority);
+ break;
+ case P_RES:
+ MKCOL(scale_num(PAGES_TO_KB(p->resident), w, s));
+ break;
+ case P_SHR:
+ MKCOL(scale_num(PAGES_TO_KB(p->share), w, s));
+ break;
+ case P_STA:
+ MKCOL(p->state);
+ break;
+ case P_SWP:
+ MKCOL(scale_num(PAGES_TO_KB(p->size - p->resident), w, s));
+ break;
+ case P_TME:
+ case P_TM2:
+ { TIC_t t = p->utime + p->stime;
+ if (CHKw(q, Show_CTIMES))
+ t += (p->cutime + p->cstime);
+ MKCOL(scale_tics(t, w));
+ }
+ break;
+ case P_TTY:
+ { char tmp[TNYBUFSIZ];
+ dev_to_tty(tmp, (int)w, p->tty, p->XXXID, ABBREV_DEV);
+ MKCOL(tmp);
+ }
+ break;
+ case P_UID:
+ MKCOL((unsigned)p->euid);
+ break;
+ case P_URE:
+ MKCOL(p->euser);
+ break;
+ case P_URR:
+ MKCOL(p->ruser);
+ break;
+ case P_VRT:
+ MKCOL(scale_num(PAGES_TO_KB(p->size), w, s));
+ break;
+ case P_WCH:
+ if (No_ksyms) {
+ f = " %08lx ";
+ MKCOL((long)p->wchan);
+ } else {
+ MKCOL(lookup_wchan(p->wchan, p->XXXID));
+ }
+ break;
+
+ } /* end: switch 'procflag' */
+
+ rp = scat(rp, cbuf+advance);
+ } /* end: for 'maxpflgs' */
+
+ PUFF(
+ "\n%s%.*s%s%s",
+ (CHKw(q, Show_HIROWS) && 'R' == p->state) ? q->capclr_rowhigh : q->capclr_rownorm,
+ Screen_cols + pad,
+ rbuf,
+ Caps_off,
+ "" /*Cap_clr_eol*/
+ );
+
+#undef MKCOL
+}
+
+
+// Squeeze as many tasks as we can into a single window,
+// after sorting the passed proc table.
+static void window_show (proc_t **ppt, WIN_t *q, int *lscr)
+{
+#ifdef SORT_SUPRESS
+ // the 1 flag that DOES and 2 flags that MAY impact our proc table qsort
+#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES )
+ static FLG_t sav_indx = 0;
+ static int sav_flgs = -1;
+#endif
+ int i, lwin;
+
+ // Display Column Headings -- and distract 'em while we sort (maybe)
+ PUFF("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol);
+
+#ifdef SORT_SUPRESS
+ if (CHKw(Curwin, NEWFRAM_cwo)
+ || sav_indx != q->rc.sortindx
+ || sav_flgs != (q->rc.winflags & srtMASK)) {
+ sav_indx = q->rc.sortindx;
+ sav_flgs = (q->rc.winflags & srtMASK);
+#endif
+ if (CHKw(q, Qsrt_NORMAL)) Frame_srtflg = 1; // this one's always needed!
+ else Frame_srtflg = -1;
+ Frame_ctimes = CHKw(q, Show_CTIMES); // this and next, only maybe
+ Frame_cmdlin = CHKw(q, Show_CMDLIN);
+ qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort);
+#ifdef SORT_SUPRESS
+ }
+#endif
+ // account for column headings
+ (*lscr)++;
+ lwin = 1;
+ i = 0;
+
+ while ( ppt[i]->tid != -1 && *lscr < Max_lines && (!q->winlines || (lwin <= q->winlines)) ) {
+ if ((CHKw(q, Show_IDLEPS) || ('S' != ppt[i]->state && 'Z' != ppt[i]->state && 'T' != ppt[i]->state))
+ && good_uid(ppt[i]) ) {
+ // Display a process Row
+ task_show(q, ppt[i]);
+ (*lscr)++;
+ ++lwin;
+ }
+ ++i;
+ }
+ // for this frame that window's toast, cleanup for next time
+ q->winlines = 0;
+ OFFw(Curwin, FLGSOFF_cwo);
+
+#ifdef SORT_SUPRESS
+#undef srtMASK
+#endif
+}
+
+
+/*###### Entry point plus two ##########################################*/
+
+// This guy's just a *Helper* function who apportions the
+// remaining amount of screen real estate under multiple windows
+static void framehlp (int wix, int max)
+{
+ int i, rsvd, size, wins;
+
+ // calc remaining number of visible windows + total 'user' lines
+ for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) {
+ if (CHKw(&Winstk[i], VISIBLE_tsk)) {
+ rsvd += Winstk[i].rc.maxtasks;
+ ++wins;
+ if (max <= rsvd) break;
+ }
+ }
+ if (!wins) wins = 1;
+ // set aside 'rsvd' & deduct 1 line/window for the columns heading
+ size = (max - wins) - rsvd;
+ if (0 <= size) size = max;
+ size = (max - wins) / wins;
+
+ // for remaining windows, set WIN_t winlines to either the user's
+ // maxtask (1st choice) or our 'foxized' size calculation
+ // (foxized adj. - 'fair and balanced')
+ for (i = wix ; i < GROUPSMAX; i++) {
+ if (CHKw(&Winstk[i], VISIBLE_tsk)) {
+ Winstk[i].winlines =
+ Winstk[i].rc.maxtasks ? Winstk[i].rc.maxtasks : size;
+ }
+ }
+}
+
+
+// Initiate the Frame Display Update cycle at someone's whim!
+// This routine doesn't do much, mostly he just calls others.
+//
+// (Whoa, wait a minute, we DO caretake those row guys, plus)
+// (we CALCULATE that IMPORTANT Max_lines thingy so that the)
+// (*subordinate* functions invoked know WHEN the user's had)
+// (ENOUGH already. And at Frame End, it SHOULD be apparent)
+// (WE am d'MAN -- clearing UNUSED screen LINES and ensuring)
+// (the CURSOR is STUCK in just the RIGHT place, know what I)
+// (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say)
+// (THAT about THIS function again, Ok? Good that's better.)
+static void frame_make (void)
+{
+ proc_t **ppt;
+ int i, scrlins;
+
+ // note: all libproc flags are managed by
+ // reframewins(), who also builds each window's column headers
+ if (!Frames_libflags) {
+ reframewins();
+ memset(Pseudo_scrn, '\0', Pseudo_size);
+ }
+ Pseudo_row = Msg_row = scrlins = 0;
+ ppt = summary_show();
+ Max_lines = (Screen_rows - Msg_row) - 1;
+
+ if (CHKw(Curwin, EQUWINS_cwo))
+ wins_reflag(Flags_OFF, EQUWINS_cwo);
+
+ // sure hope each window's columns header begins with a newline...
+ putp(tg2(0, Msg_row));
+
+ if (!Rc.mode_altscr) {
+ // only 1 window to show so, piece o' cake
+ Curwin->winlines = Curwin->rc.maxtasks;
+ window_show(ppt, Curwin, &scrlins);
+ } else {
+ // maybe NO window is visible but assume, pieces o' cakes
+ for (i = 0 ; i < GROUPSMAX; i++) {
+ if (CHKw(&Winstk[i], VISIBLE_tsk)) {
+ framehlp(i, Max_lines - scrlins);
+ window_show(ppt, &Winstk[i], &scrlins);
+ }
+ if (Max_lines <= scrlins) break;
+ }
+ }
+ // clear to end-of-screen (critical if last window is 'idleps off'),
+ // then put the cursor in-its-place, and rid us of any prior frame's msg
+ // (main loop must iterate such that we're always called before sleep)
+ PUTT(
+ "%s%s%s%s",
+ scrlins < Max_lines ? "\n" : "",
+ scrlins < Max_lines ? Cap_clr_eos : "",
+ tg2(0, Msg_row),
+ Cap_clr_eol
+ );
+ fflush(stdout);
+}
+
+
+int main (int dont_care_argc, char *argv[])
+{
+ (void)dont_care_argc;
+ before(*argv);
+ // +-------------+
+ windows_stage1(); // top (sic) slice
+ configs_read(); // > spread etc, <
+ parse_args(&argv[1]); // > lean stuff, <
+ whack_terminal(); // > onions etc. <
+ windows_stage2(); // as bottom slice
+ // +-------------+
+ signal(SIGALRM, end_pgm);
+ signal(SIGHUP, end_pgm);
+ signal(SIGINT, end_pgm);
+ signal(SIGPIPE, end_pgm);
+ signal(SIGQUIT, end_pgm);
+ signal(SIGTERM, end_pgm);
+ signal(SIGTSTP, suspend);
+ signal(SIGTTIN, suspend);
+ signal(SIGTTOU, suspend);
+ signal(SIGCONT, wins_resize_sighandler);
+ signal(SIGWINCH, wins_resize_sighandler);
+
+ for (;;) {
+ if (need_resize){
+ need_resize = 0;
+ wins_resize();
+ }
+ frame_make();
+
+ if (Msg_awaiting) show_msg(Msg_delayed);
+ if (Loops > 0) --Loops;
+ if (!Loops) end_pgm(0);
+
+ tv.tv_sec = Rc.delay_time;
+ tv.tv_usec = (Rc.delay_time - (int)Rc.delay_time) * 1000000;
+
+ if (Batch) {
+ select(0, NULL, NULL, NULL, &tv); // ought to loop until done
+ } else {
+ long file_flags;
+ int rc;
+ char c;
+ fd_set fs;
+ FD_ZERO(&fs);
+ FD_SET(STDIN_FILENO, &fs);
+ file_flags = fcntl(STDIN_FILENO, F_GETFL);
+ if(file_flags==-1) file_flags=0;
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
+
+ // check 1st, in case tv zeroed (by sig handler) before it got set
+ rc = chin(0, &c, 1);
+ if (rc <= 0) {
+ // EOF is pretty much a "can't happen" except for a kernel bug.
+ // We should quickly die via SIGHUP, and thus not spin here.
+ // if (rc == 0) end_pgm(0); /* EOF from terminal */
+ fcntl(STDIN_FILENO, F_SETFL, file_flags);
+ select(1, &fs, NULL, NULL, &tv);
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK|file_flags);
+ }
+ if (chin(0, &c, 1) > 0) {
+ fcntl(STDIN_FILENO, F_SETFL, file_flags);
+ do_key((unsigned)c);
+ } else {
+ fcntl(STDIN_FILENO, F_SETFL, file_flags);
+ }
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+// top.h - Header file: show Linux processes
+//
+// Copyright (c) 2002, by: James C. Warner
+// All rights reserved. 8921 Hilloway Road
+// Eden Prairie, Minnesota 55347 USA
+// <warnerjc@worldnet.att.net>
+//
+// This file may be used subject to the terms and conditions of the
+// GNU Library General Public License Version 2, or any later version
+// at your option, as published by the Free Software Foundation.
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Library General Public License for more details.
+//
+// For their contributions to this program, the author wishes to thank:
+// Albert D. Cahalan, <albert@users.sf.net>
+// Craig Small, <csmall@small.dropbear.id.au>
+//
+// Changes by Albert Cahalan, 2002, 2004.
+
+#ifndef _Itop
+#define _Itop
+
+// Defines intended to be experimented with ------------------------
+//#define CASEUP_HEXES // show any hex values in upper case
+//#define CASEUP_SCALE // show scaled time/num suffix upper case
+//#define CASEUP_SUMMK // show memory summary kilobytes with 'K'
+//#define SORT_SUPRESS // *attempt* to reduce qsort overhead
+//#define WARN_NOT_SMP // restrict '1' & 'I' commands to true smp
+
+// Development/Debugging defines -----------------------------------
+//#define ATEOJ_REPORT // report a bunch of stuff, at end-of-job
+//#define PRETEND2_5_X // pretend we're linux 2.5.x (for IO-wait)
+//#define PRETENDNOCAP // use a terminal without essential caps
+//#define STDOUT_IOLBF // disable our own stdout _IOFBF override
+
+#ifdef PRETEND2_5_X
+#define linux_version_code LINUX_VERSION(2,5,43)
+#endif
+
+/*###### Some Miscellaneous constants ##################################*/
+
+// The default delay twix updates
+#define DEF_DELAY 3.0
+
+// The length of time a 'message' is displayed
+#define MSG_SLEEP 2
+
+// The default value for the 'k' (kill) request
+#define DEF_SIGNAL SIGTERM
+
+// Specific process id monitoring support (command line only)
+#define MONPIDMAX 20
+
+// Power-of-two sizes lead to trouble; the largest power of
+// two factor should be the cache line size. It'll mean the
+// array indexing math gets slower, but cache aliasing is
+// avoided.
+#define CACHE_TWEAK_FACTOR 64
+
+// Miscellaneous buffer sizes with liberal values -- mostly
+// just to pinpoint source code usage/dependancies
+#define SCREENMAX ( 512 + CACHE_TWEAK_FACTOR)
+// the above might seem pretty stingy, until you consider that with every
+// one of top's fields displayed we're talking a 160 byte column header --
+// so that will provide for all fields plus a 350+ byte command line
+#define WINNAMSIZ 4
+#define CAPTABMAX 9
+#define PFLAGSSIZ 32
+#define CAPBUFSIZ 32
+#define CLRBUFSIZ 64
+#define GETBUFSIZ 32
+#define TNYBUFSIZ 32
+#define SMLBUFSIZ ( 256 + CACHE_TWEAK_FACTOR)
+#define OURPATHSZ (1024 + CACHE_TWEAK_FACTOR)
+#define MEDBUFSIZ (1024 + CACHE_TWEAK_FACTOR)
+#define BIGBUFSIZ (2048 + CACHE_TWEAK_FACTOR)
+#define USRNAMSIZ GETBUFSIZ
+#define ROWBUFSIZ SCREENMAX + CLRBUFSIZ
+
+
+/*###### Some Miscellaneous Macro definitions ##########################*/
+
+// Yield table size as 'int'
+#define MAXTBL(t) (int)(sizeof(t) / sizeof(t[0]))
+
+// Used as return arguments in *some* of the sort callbacks
+#define SORT_lt ( Frame_srtflg > 0 ? 1 : -1 )
+#define SORT_gt ( Frame_srtflg > 0 ? -1 : 1 )
+#define SORT_eq 0
+
+// Used to create *most* of the sort callback functions
+#define SCB_NUM1(f,n) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ if ( (*P)->n < (*Q)->n ) return SORT_lt; \
+ if (likely( (*P)->n > (*Q)->n )) return SORT_gt; \
+ return SORT_eq; }
+#define SCB_NUM2(f,n1,n2) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ if ( ((*P)->n1 - (*P)->n2) < ((*Q)->n1 - (*Q)->n2) ) return SORT_lt; \
+ if (likely( ((*P)->n1 - (*P)->n2) > ((*Q)->n1 - (*Q)->n2) )) return SORT_gt; \
+ return SORT_eq; }
+#define SCB_NUMx(f,n) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ return Frame_srtflg * ( (*Q)->n - (*P)->n ); }
+#define SCB_STRx(f,s) \
+ static int sort_ ## f (const proc_t **P, const proc_t **Q) { \
+ return Frame_srtflg * strcmp((*Q)->s, (*P)->s); }
+
+// The following two macros are used to 'inline' those portions of the
+// display process requiring formatting, while protecting against any
+// potential embedded 'millesecond delay' escape sequences.
+
+// PUTT - Put to Tty (used in many places)
+// - for temporary, possibly interactive, 'replacement' output
+// - may contain ANY valid terminfo escape sequences
+// - need NOT represent an entire screen row
+#define PUTT(fmt,arg...) do { \
+ char _str[ROWBUFSIZ]; \
+ snprintf(_str, sizeof(_str), fmt, ## arg); \
+ putp(_str); \
+ } while (0)
+
+// PUFF - Put for Frame (used in only 3 places)
+// - for more permanent frame-oriented 'update' output
+// - may NOT contain cursor motion terminfo escapes
+// - assumed to represent a complete screen ROW
+// - subject to optimization, thus MAY be discarded
+
+// The evil version (53892 byte stripped top, oddly enough)
+#define _PUFF(fmt,arg...) \
+do { \
+ char _str[ROWBUFSIZ]; \
+ int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \
+ putp ( Batch ? _str : \
+ ({ \
+ char *restrict const _pse = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \
+ memcmp(_pse, _str, _len) ? memcpy(_pse, _str, _len) : "\n"; \
+ }) \
+ ); \
+} while (0)
+
+// The good version (53876 byte stripped top)
+#define PUFF(fmt,arg...) \
+do { \
+ char _str[ROWBUFSIZ]; \
+ char *_ptr; \
+ int _len = 1 + snprintf(_str, sizeof(_str), fmt, ## arg); \
+ if (Batch) _ptr = _str; \
+ else { \
+ _ptr = &Pseudo_scrn[Pseudo_row++ * Pseudo_cols]; \
+ if (memcmp(_ptr, _str, _len)) { \
+ memcpy(_ptr, _str, _len); \
+ } else { \
+ _ptr = "\n"; \
+ } \
+ } \
+ putp(_ptr); \
+} while (0)
+
+
+/*------ Special Macros (debug and/or informative) ---------------------*/
+
+// Orderly end, with any sort of message - see fmtmk
+#define debug_END(s) { \
+ static void std_err (const char *); \
+ fputs(Cap_clr_scr, stdout); \
+ std_err(s); \
+ }
+
+// A poor man's breakpoint, if he's too lazy to learn gdb
+#define its_YOUR_fault { *((char *)0) = '!'; }
+
+
+/*###### Some Typedef's and Enum's #####################################*/
+
+// This typedef just ensures consistent 'process flags' handling
+typedef unsigned FLG_t;
+
+// These typedefs attempt to ensure consistent 'ticks' handling
+typedef unsigned long long TIC_t;
+typedef long long SIC_t;
+
+// Sort support, callback funtion signature
+typedef int (*QFP_t)(const void *, const void *);
+
+// This structure consolidates the information that's used
+// in a variety of display roles.
+typedef struct FLD_t {
+ const char keys [4]; // order: New-on New-off Old-on Old-off
+ // misaligned on 64-bit, but part of a table -- oh well
+ const char *head; // name for col heads + toggle/reorder fields
+ const char *fmts; // sprintf format string for field display
+ const int width; // field width, if applicable
+ const int scale; // scale_num type, if applicable
+ const QFP_t sort; // sort function
+ const char *desc; // description for toggle/reorder fields
+ const int lflg; // PROC_FILLxxx flag(s) needed by this field
+} FLD_t;
+
+// This structure stores one piece of critical 'history'
+// information from one frame to the next -- we don't calc
+// and save data that goes unused
+typedef struct HST_t {
+ TIC_t tics;
+ int pid;
+} HST_t;
+
+// This structure stores a frame's cpu tics used in history
+// calculations. It exists primarily for SMP support but serves
+// all environments.
+typedef struct CPU_t {
+ TIC_t u, n, s, i, w, x, y, z; // as represented in /proc/stat
+ TIC_t u_sav, s_sav, n_sav, i_sav, w_sav, x_sav, y_sav, z_sav; // in the order of our display
+ unsigned id; // the CPU ID number
+} CPU_t;
+
+// These 2 types support rcfile compatibility
+typedef struct RCW_t { // the 'window' portion of an rcfile
+ FLG_t sortindx; // sort field, represented as a procflag
+ int winflags, // 'view', 'show' and 'sort' mode flags
+ maxtasks, // user requested maximum, 0 equals all
+ summclr, // color num used in summ info
+ msgsclr, // " in msgs/pmts
+ headclr, // " in cols head
+ taskclr; // " in task rows
+ char winname [WINNAMSIZ], // window name, user changeable
+ fieldscur [PFLAGSSIZ]; // fields displayed and ordered
+} RCW_t;
+
+typedef struct RCF_t { // the complete rcfile (new style)
+ int mode_altscr; // 'A' - Alt display mode (multi task windows)
+ int mode_irixps; // 'I' - Irix vs. Solaris mode (SMP-only)
+ float delay_time; // 'd' or 's' - How long to sleep twixt updates
+ int win_index; // Curwin, as index
+ RCW_t win [4]; // a 'WIN_t.rc' for each of the 4 windows
+} RCF_t;
+
+// The scaling 'type' used with scale_num() -- this is how
+// the passed number is interpreted should scaling be necessary
+enum scale_num {
+ SK_no, SK_Kb, SK_Mb, SK_Gb, SK_Tb
+};
+
+// Flags for each possible field
+enum pflag {
+ P_PID, P_PPD, P_URR, P_UID, P_URE, P_GRP, P_TTY,
+ P_PRI, P_NCE,
+ P_CPN, P_CPU, P_TME, P_TM2,
+ P_MEM, P_VRT, P_SWP, P_RES, P_COD, P_DAT, P_SHR,
+ P_FLT, P_DRT,
+ P_STA, P_CMD, P_WCH, P_FLG
+};
+
+
+///////////////////////////////////////////////////////////////////////////
+// Special Section: multiple windows/field groups -------------
+// (kind of a header within a header: constants, macros & types)
+
+#define GROUPSMAX 4 // the max number of simultaneous windows
+#define GRPNAMSIZ WINNAMSIZ+2 // window's name + number as in: '#:...'
+
+#define Flags_TOG 1 // these are used to direct wins_reflag
+#define Flags_SET 2
+#define Flags_OFF 3
+
+// The Persistent 'Mode' flags!
+// These are preserved in the rc file, as a single integer and the
+// letter shown is the corresponding 'command' toggle
+
+// 'View_' flags affect the summary (minimum), taken from 'Curwin'
+#define View_CPUSUM 0x8000 // '1' - show combined cpu stats (vs. each)
+#define View_LOADAV 0x4000 // 'l' - display load avg and uptime summary
+#define View_STATES 0x2000 // 't' - display task/cpu(s) states summary
+#define View_MEMORY 0x1000 // 'm' - display memory summary
+#define View_NOBOLD 0x0001 // 'B' - disable 'bold' attribute globally
+
+// 'Show_' & 'Qsrt_' flags are for task display in a visible window
+#define Show_THREADS 0x10000 // 'H' - show threads in each task
+#define Show_COLORS 0x0800 // 'z' - show in color (vs. mono)
+#define Show_HIBOLD 0x0400 // 'b' - rows and/or cols bold (vs. reverse)
+#define Show_HICOLS 0x0200 // 'x' - show sort column highlighted
+#define Show_HIROWS 0x0100 // 'y' - show running tasks highlighted
+#define Show_CMDLIN 0x0080 // 'c' - show cmdline vs. name
+#define Show_CTIMES 0x0040 // 'S' - show times as cumulative
+#define Show_IDLEPS 0x0020 // 'i' - show idle processes (all tasks)
+#define Qsrt_NORMAL 0x0010 // 'R' - reversed column sort (high to low)
+
+// these flag(s) have no command as such - they're for internal use
+#define VISIBLE_tsk 0x0008 // tasks are showable when in 'Mode_altscr'
+#define NEWFRAM_cwo 0x0004 // new frame (if anyone cares) - in Curwin
+#define EQUWINS_cwo 0x0002 // rebalance tasks next frame (off 'i'/ 'n')
+ // ...set in Curwin, but impacts all windows
+
+// Current-window-only flags -- always turned off at end-of-window!
+#define FLGSOFF_cwo EQUWINS_cwo | NEWFRAM_cwo
+
+// Default flags if there's no rcfile to provide user customizations
+#define DEF_WINFLGS ( \
+ View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY | View_NOBOLD | \
+ Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Qsrt_NORMAL | \
+ VISIBLE_tsk \
+)
+
+ // Used to test/manipulate the window flags
+#define CHKw(q,f) (int)((q)->rc.winflags & (f))
+#define TOGw(q,f) (q)->rc.winflags ^= (f)
+#define SETw(q,f) (q)->rc.winflags |= (f)
+#define OFFw(q,f) (q)->rc.winflags &= ~(f)
+#define VIZCHKc (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \
+ ? 1 : win_warn()
+#define VIZTOGc(f) (!Rc.mode_altscr || Curwin->rc.winflags & VISIBLE_tsk) \
+ ? TOGw(Curwin, f) : win_warn()
+
+// This structure stores configurable information for each window.
+// By expending a little effort in its creation and user requested
+// maintainence, the only real additional per frame cost of having
+// windows is an extra sort -- but that's just on ptrs!
+typedef struct WIN_t {
+ struct WIN_t *next, // next window in window stack
+ *prev; // prior window in window stack
+ char *captab [CAPTABMAX]; // captab needed by show_special
+ int winnum, // window's num (array pos + 1)
+ winlines; // task window's rows (volatile)
+ FLG_t procflags [PFLAGSSIZ]; // fieldscur subset, as enum
+ int maxpflgs, // number of procflags (upcase fieldscur)
+ maxcmdln; // max length of a process' command line
+ int len_rownorm, // lengths of the corresponding terminfo
+ len_rowhigh; // strings to avoid repeated strlen calls
+ RCW_t rc; // stuff that gets saved in the rcfile
+ char capclr_sum [CLRBUFSIZ], // terminfo strings built from
+ capclr_msg [CLRBUFSIZ], // above clrs (& rebuilt too),
+ capclr_pmt [CLRBUFSIZ], // but NO recurring costs !!!
+ capclr_hdr [CLRBUFSIZ], // note: sum, msg and pmt strs
+ capclr_rowhigh [CLRBUFSIZ], // are only used when this
+ capclr_rownorm [CLRBUFSIZ]; // window is the 'Curwin'!
+ char cap_bold [CAPBUFSIZ]; // support for View_NOBOLD toggle
+ char grpname [GRPNAMSIZ], // window number:name, printable
+ columnhdr [SCREENMAX], // column headings for procflags
+ colusrnam [USRNAMSIZ]; // if selected by the 'u' command
+} WIN_t;
+
+
+/*###### Display Support *Data* ########################################*/
+
+// Configuration files support
+#define SYS_RCFILESPEC "/etc/toprc"
+#define RCF_EYECATCHER "RCfile for "
+#define RCF_DEPRECATED "Id:a, "
+
+// The default fields displayed and their order,
+#define DEF_FIELDS "AEHIOQTWKNMbcdfgjplrsuvyzX"
+// Pre-configured field groupss
+#define JOB_FIELDS "ABcefgjlrstuvyzMKNHIWOPQDX"
+#define MEM_FIELDS "ANOPQRSTUVbcdefgjlmyzWHIKX"
+#define USR_FIELDS "ABDECGfhijlopqrstuvyzMKNWX"
+// Used by fields_sort, placed here for peace-of-mind
+#define NUL_FIELDS "abcdefghijklmnopqrstuvwxyz"
+
+
+// The default values for the local config file
+#define DEF_RCFILE { \
+ 0, 1, DEF_DELAY, 0, { \
+ { P_CPU, DEF_WINFLGS, 0, \
+ COLOR_RED, COLOR_RED, COLOR_YELLOW, COLOR_RED, \
+ "Def", DEF_FIELDS }, \
+ { P_PID, DEF_WINFLGS, 0, \
+ COLOR_CYAN, COLOR_CYAN, COLOR_WHITE, COLOR_CYAN, \
+ "Job", JOB_FIELDS }, \
+ { P_MEM, DEF_WINFLGS, 0, \
+ COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLUE, COLOR_MAGENTA, \
+ "Mem", MEM_FIELDS }, \
+ { P_URE, DEF_WINFLGS, 0, \
+ COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW, \
+ "Usr", USR_FIELDS } \
+ } }
+
+
+// Summary Lines specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define LOADAV_line "%s -%s\n"
+#define LOADAV_line_alt "%s\06 -%s\n"
+#define STATES_line1 "Tasks:\03" \
+ " %3u \02total,\03 %3u \02running,\03 %3u \02sleeping,\03 %3u \02stopped,\03 %3u \02zombie\03\n"
+#define STATES_line2x4 "%s\03" \
+ " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle\03\n"
+#define STATES_line2x5 "%s\03" \
+ " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03 %#5.1f%% \02nice,\03 %#5.1f%% \02idle,\03 %#5.1f%% \02IO-wait\03\n"
+#define STATES_line2x6 "%s\03" \
+ " %#4.1f%% \02us,\03 %#4.1f%% \02sy,\03 %#4.1f%% \02ni,\03 %#4.1f%% \02id,\03 %#4.1f%% \02wa,\03 %#4.1f%% \02hi,\03 %#4.1f%% \02si\03\n"
+#define STATES_line2x7 "%s\03" \
+ "%#5.1f%%\02us,\03%#5.1f%%\02sy,\03%#5.1f%%\02ni,\03%#5.1f%%\02id,\03%#5.1f%%\02wa,\03%#5.1f%%\02hi,\03%#5.1f%%\02si,\03%#5.1f%%\02st\03\n"
+#ifdef CASEUP_SUMMK
+#define MEMORY_line1 "Mem: \03" \
+ " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02buffers\03\n"
+#define MEMORY_line2 "Swap:\03" \
+ " %8luK \02total,\03 %8luK \02used,\03 %8luK \02free,\03 %8luK \02cached\03\n"
+#else
+#define MEMORY_line1 "Mem: \03" \
+ " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02buffers\03\n"
+#define MEMORY_line2 "Swap:\03" \
+ " %8luk \02total,\03 %8luk \02used,\03 %8luk \02free,\03 %8luk \02cached\03\n"
+#endif
+
+// Keyboard Help specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define KEYS_help \
+ "Help for Interactive Commands\02 - %s\n" \
+ "Window \01%s\06: \01Cumulative mode \03%s\02. \01System\06: \01Delay \03%.1f secs\02; \01Secure mode \03%s\02.\n" \
+ "\n" \
+ " Z\05,\01B\05 Global: '\01Z\02' change color mappings; '\01B\02' disable/enable bold\n" \
+ " l,t,m Toggle Summaries: '\01l\02' load avg; '\01t\02' task/cpu stats; '\01m\02' mem info\n" \
+ " 1,I Toggle SMP view: '\0011\02' single/separate states; '\01I\02' Irix/Solaris mode\n" \
+ "\n" \
+ " f,o . Fields/Columns: '\01f\02' add or remove; '\01o\02' change display order\n" \
+ " F or O . Select sort field\n" \
+ " <,> . Move sort field: '\01<\02' next col left; '\01>\02' next col right\n" \
+ " R,H . Toggle: '\01R\02' normal/reverse sort; '\01H\02' show threads\n" \
+ " c,i,S . Toggle: '\01c\02' cmd name/line; '\01i\02' idle tasks; '\01S\02' cumulative time\n" \
+ " x\05,\01y\05 . Toggle highlights: '\01x\02' sort field; '\01y\02' running tasks\n" \
+ " z\05,\01b\05 . Toggle: '\01z\02' color/mono; '\01b\02' bold/reverse (only if 'x' or 'y')\n" \
+ " u . Show specific user only\n" \
+ " n or # . Set maximum tasks displayed\n" \
+ "\n" \
+ "%s" \
+ " W Write configuration file\n" \
+ " q Quit\n" \
+ " ( commands shown with '.' require a \01visible\02 task display \01window\02 ) \n" \
+ "Press '\01h\02' or '\01?\02' for help with \01Windows\02,\n" \
+ "any other key to continue " \
+ ""
+
+// This guy goes into the help text (maybe)
+#define KEYS_help_unsecured \
+ " k,r Manipulate tasks: '\01k\02' kill; '\01r\02' renice\n" \
+ " d or s Set update interval\n" \
+ ""
+
+// Fields Reorder/Toggle specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions
+// note: the leading newline below serves really dumb terminals;
+// if there's no 'cursor_home', the screen will be a mess
+// but this part will still be functional.
+#define FIELDS_current \
+ "\n%sCurrent Fields\02: \01 %s \04 for window \01%s\06\n%s " \
+ ""
+
+// Some extra explanatory text which accompanies the Fields display.
+// note: the newlines cannot actually be used, they just serve as
+// substring delimiters for the 'display_fields' routine.
+#define FIELDS_xtra \
+ "Flags field:\n" \
+ " 0x00000001 PF_ALIGNWARN\n" \
+ " 0x00000002 PF_STARTING\n" \
+ " 0x00000004 PF_EXITING\n" \
+ " 0x00000040 PF_FORKNOEXEC\n" \
+ " 0x00000100 PF_SUPERPRIV\n" \
+ " 0x00000200 PF_DUMPCORE\n" \
+ " 0x00000400 PF_SIGNALED\n" \
+ " 0x00000800 PF_MEMALLOC\n" \
+ " 0x00002000 PF_FREE_PAGES (2.5)\n" \
+ " 0x00008000 debug flag (2.5)\n" \
+ " 0x00024000 special threads (2.5)\n" \
+ " 0x001D0000 special states (2.5)\n" \
+ " 0x00100000 PF_USEDFPU (thru 2.4)\n" \
+ ""
+/* no room, sacrificed this one: 'Killed for out-of-memory' */
+/* " 0x00001000 PF_MEMDIE (2.5)\n" ....................... */
+
+// Sort Select specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions
+// note: the leading newline below serves really dumb terminals;
+// if there's no 'cursor_home', the screen will be a mess
+// but this part will still be functional.
+#define SORT_fields \
+ "\n%sCurrent Sort Field\02: \01 %c \04 for window \01%s\06\n%s " \
+ ""
+
+// Some extra explanatory text which accompanies the Sort display.
+// note: the newlines cannot actually be used, they just serve as
+// substring delimiters for the 'display_fields' routine.
+#define SORT_xtra \
+ "Note1:\n" \
+ " If a selected sort field can't be\n" \
+ " shown due to screen width or your\n" \
+ " field order, the '<' and '>' keys\n" \
+ " will be unavailable until a field\n" \
+ " within viewable range is chosen.\n" \
+ "\n" \
+ "Note2:\n" \
+ " Field sorting uses internal values,\n" \
+ " not those in column display. Thus,\n" \
+ " the TTY & WCHAN fields will violate\n" \
+ " strict ASCII collating sequence.\n" \
+ " (shame on you if WCHAN is chosen)\n" \
+ ""
+
+// Colors Help specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define COLOR_help \
+ "Help for color mapping\02 - %s\n" \
+ "current window: \01%s\06\n" \
+ "\n" \
+ " color - 04:25:44 up 8 days, 50 min, 7 users, load average:\n" \
+ " Tasks:\03 64 \02total,\03 2 \03running,\03 62 \02sleeping,\03 0 \02stopped,\03\n" \
+ " Cpu(s):\03 76.5%% \02user,\03 11.2%% \02system,\03 0.0%% \02nice,\03 12.3%% \02idle\03\n" \
+ " \01 Nasty Message! \04 -or- \01Input Prompt\05\n" \
+ " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \
+ " 17284 \10pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bash \10\n" \
+ " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R < color -b \07\n" \
+ " 11005 \10? \07 9 0 0.0 0:02.50 2852 1008 S amor -ses \10\n" \
+ " available toggles: \01B\02 =disable bold globally (\01%s\02),\n" \
+ " \01z\02 =color/mono (\01%s\02), \01b\02 =tasks \"bold\"/reverse (\01%s\02)\n" \
+ "\n" \
+ "Select \01target\02 as upper case letter:\n" \
+ " S\02 = Summary Data,\01 M\02 = Messages/Prompts,\n" \
+ " H\02 = Column Heads,\01 T\02 = Task Information\n" \
+ "Select \01color\02 as number:\n" \
+ " 0\02 = black,\01 1\02 = red, \01 2\02 = green,\01 3\02 = yellow,\n" \
+ " 4\02 = blue, \01 5\02 = magenta,\01 6\02 = cyan, \01 7\02 = white\n" \
+ "\n" \
+ "Selected: \01target\02 \01 %c \04; \01color\02 \01 %d \04\n" \
+ " press 'q' to abort changes to window '\01%s\02'\n" \
+ " press 'a' or 'w' to commit & change another, <Enter> to commit and end " \
+ ""
+
+// Windows/Field Group Help specially formatted string(s) --
+// see 'show_special' for syntax details + other cautions.
+#define WINDOWS_help \
+ "Help for Windows / Field Groups\02 - \"Current Window\" = \01 %s \06\n" \
+ "\n" \
+ ". Use multiple \01windows\02, each with separate config opts (color,fields,sort,etc)\n" \
+ ". The 'current' window controls the \01Summary Area\02 and responds to your \01Commands\02\n" \
+ " . that window's \01task display\02 can be turned \01Off\02 & \01On\02, growing/shrinking others\n" \
+ " . with \01NO\02 task display, some commands will be \01disabled\02 ('i','R','n','c', etc)\n" \
+ " until a \01different window\02 has been activated, making it the 'current' window\n" \
+ ". You \01change\02 the 'current' window by: \01 1\02) cycling forward/backward;\01 2\02) choosing\n" \
+ " a specific field group; or\01 3\02) exiting the color mapping screen\n" \
+ ". Commands \01available anytime -------------\02\n" \
+ " A . Alternate display mode toggle, show \01Single\02 / \01Multiple\02 windows\n" \
+ " G . Choose another field group and make it 'current', or change now\n" \
+ " by selecting a number from: \01 1\02 =%s;\01 2\02 =%s;\01 3\02 =%s; or\01 4\02 =%s\n" \
+ ". Commands \01requiring\02 '\01A\02' mode\01 -------------\02\n" \
+ " g . Change the \01Name\05 of the 'current' window/field group\n" \
+ " \01*\04 a , w . Cycle through all four windows: '\01a\05' Forward; '\01w\05' Backward\n" \
+ " \01*\04 - , _ . Show/Hide: '\01-\05' \01Current\02 window; '\01_\05' all \01Visible\02/\01Invisible\02\n" \
+ " The screen will be divided evenly between task displays. But you can make\n" \
+ " some \01larger\02 or \01smaller\02, using '\01n\02' and '\01i\02' commands. Then later you could:\n" \
+ " \01*\04 = , + . Rebalance tasks: '\01=\05' \01Current\02 window; '\01+\05' \01Every\02 window\n" \
+ " (this also forces the \01current\02 or \01every\02 window to become visible)\n" \
+ "\n" \
+ "In '\01A\02' mode, '\01*\04' keys are your \01essential\02 commands. Please try the '\01a\02' and '\01w\02'\n" \
+ "commands plus the 'G' sub-commands NOW. Press <Enter> to make 'Current' " \
+ ""
+
+
+/*###### For Piece of mind #############################################*/
+
+ /* just sanity check(s)... */
+#if USRNAMSIZ < GETBUFSIZ
+# error "Jeeze, USRNAMSIZ Must NOT be less than GETBUFSIZ !"
+#endif
+
+
+
+#endif /* _Itop */
--- /dev/null
+.\" -*-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 <greenfie@gauss.rutgers.edu> and
+Michael K. Johnson <johnsonm@sunsite.unc.edu>.
+
+Please send bug reports to <albert@users.sf.net>
+.SH "SEE ALSO"
+.BR ps (1),
+.BR top (1),
+.BR utmp (5),
+.BR w (1)
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include "proc/whattime.h"
+#include "proc/version.h"
+
+int main(int argc, char *argv[]) {
+ if(argc == 1) {
+ print_uptime();
+ return 0;
+ }
+ if((argc == 2) && (!strcmp(argv[1], "-V"))) {
+ display_version();
+ return 0;
+ }
+ fprintf(stderr, "usage: uptime [-V]\n -V display version\n");
+ return 1;
+}
--- /dev/null
+#!/bin/sh
+#
+# Wow, using $* causes great pain with embedded spaces in arguments.
+# The "$@" won't break that into 2 arguments.
+#
+LD_LIBRARY_PATH=proc exec ./vmstat "$@"
--- /dev/null
+.\" This page Copyright (C) 1994 Henry Ware <al172@yfn.ysu.edu>
+.\" Distributed under the GPL, Copyleft 1994.
+.TH VMSTAT 8 "27 July 1994 " "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual"
+.SH NAME
+vmstat \- Report virtual memory statistics
+.SH SYNOPSIS
+.ft B
+.B vmstat
+.RB [ "\-a" ]
+.RB [ "\-n" ]
+.RI [ delay " [ " count ]]
+.br
+.B vmstat
+.RB [ "\-f" ]
+.RB [ "\-s" ]
+.RB [ "\-m" ]
+.br
+.B vmstat
+.RB [ "\-S unit"]
+.br
+.B vmstat
+.RB [ "\-d"]
+.br
+.B vmstat
+.RB [ "\-p disk partition"]
+.br
+.B vmstat
+.RB [ "\-V" ]
+.SH DESCRIPTION
+\fBvmstat\fP reports information about processes, memory, paging,
+block IO, traps, and cpu activity.
+
+The first report produced gives averages since the last reboot. Additional
+reports give information on a sampling period of length \fIdelay\fP.
+The process and memory reports are instantaneous in either case.
+
+.SS Options
+The \fB-a\fP switch displays active/inactive memory, given a 2.5.41 kernel or better.
+.PP
+The \fB-f\fP switch displays the number of forks since boot.
+This includes the fork, vfork, and clone system calls, and is
+equivalent to the total number of tasks created. Each process
+is represented by one or more tasks, depending on thread usage.
+This display does not repeat.
+.PP
+The \fB-m\fP displays slabinfo.
+.PP
+The \fB-n\fP switch causes the header to be displayed only once rather than periodically.
+.PP
+The \fB-s\fP switch displays a table of various event counters
+and memory statistics. This display does not repeat.
+.PP
+.I delay
+is the delay between updates in seconds. If no delay is specified,
+only one report is printed with the average values since boot.
+.PP
+.I count
+is the number of updates. If no count is specified and delay is
+defined, \fIcount\fP defaults to infinity.
+.PP
+The \fB-d\fP reports disk statistics (2.5.70 or above required)
+.PP
+The \fB-p\fP followed by some partition name for detailed statistics (2.5.70 or above required)
+.PP
+The \fB-S\fP followed by k or K or m or M switches outputs between 1000, 1024, 1000000, or 1048576 bytes
+.PP
+The \fB-V\fP switch results in displaying version information.
+.PP
+.SH FIELD DESCRIPTION FOR VM MODE
+.SS
+.B "Procs"
+.nf
+r: The number of processes waiting for run time.
+b: The number of processes in uninterruptible sleep.
+.fi
+.PP
+.SS
+.B "Memory"
+.nf
+swpd: the amount of virtual memory used.
+free: the amount of idle memory.
+buff: the amount of memory used as buffers.
+cache: the amount of memory used as cache.
+inact: the amount of inactive memory. (-a option)
+active: the amount of active memory. (-a option)
+.fi
+.PP
+.SS
+.B "Swap"
+.nf
+si: Amount of memory swapped in from disk (/s).
+so: Amount of memory swapped to disk (/s).
+.fi
+.PP
+.SS
+.B "IO"
+.nf
+bi: Blocks received from a block device (blocks/s).
+bo: Blocks sent to a block device (blocks/s).
+.fi
+.PP
+.SS
+.B "System"
+.nf
+in: The number of interrupts per second, including the clock.
+cs: The number of context switches per second.
+.if
+.PP
+.SS
+.B "CPU "
+These are percentages of total CPU time.
+.nf
+us: Time spent running non-kernel code. (user time, including nice time)
+sy: Time spent running kernel code. (system time)
+id: Time spent idle. Prior to Linux 2.5.41, this includes IO-wait time.
+wa: Time spent waiting for IO. Prior to Linux 2.5.41, included in idle.
+st: Time stolen from a virtual machine. Prior to Linux 2.6.11, unknown.
+
+.PP
+.SH FIELD DESCRIPTION FOR DISK MODE
+.SS
+.B "Reads"
+.nf
+total: Total reads completed successfully
+merged: grouped reads (resulting in one I/O)
+sectors: Sectors read successfully
+ms: milliseconds spent reading
+.fi
+.PP
+.SS
+.B "Writes"
+.nf
+total: Total writes completed successfully
+merged: grouped writes (resulting in one I/O)
+sectors: Sectors written successfully
+ms: milliseconds spent writing
+.fi
+.PP
+.SS
+.B "IO"
+.nf
+cur: I/O in progress
+s: seconds spent for I/O
+.fi
+
+.PP
+.SH FIELD DESCRIPTION FOR DISK PARTITION MODE
+.nf
+reads: Total number of reads issued to this partition
+read sectors: Total read sectors for partition
+writes : Total number of writes issued to this partition
+requested writes: Total number of write requests made for partition
+
+.fi
+
+.PP
+.SH FIELD DESCRIPTION FOR SLAB MODE
+.nf
+cache: Cache name
+num: Number of currently active objects
+total: Total number of available objects
+size: Size of each object
+pages: Number of pages with at least one active object
+totpages: Total number of allocated pages
+pslab: Number of pages per slab
+.fi
+
+.SH NOTES
+.B "vmstat "
+does not require special permissions.
+.PP
+These reports are intended to help identify system bottlenecks. Linux
+.B "vmstat "
+does not count itself as a running process.
+.PP
+All linux blocks are currently 1024 bytes. Old kernels may report
+blocks as 512 bytes, 2048 bytes, or 4096 bytes.
+.PP
+Since procps 3.1.9, vmstat lets you choose units (k, K, m, M) default is K (1024 bytes) in the default mode
+.PP
+vmstat uses slabinfo 1.1 FIXME
+.SH FILES
+.ta
+.nf
+/proc/meminfo
+/proc/stat
+/proc/*/stat
+.fi
+
+.SH "SEE ALSO"
+iostat(1), sar(1), mpstat(1), ps(1), top(1), free(1)
+.PP
+.SH BUGS
+Does not tabulate the block io per device or count the number of system calls.
+.SH AUTHORS
+.nf
+Written by Henry Ware <al172@yfn.ysu.edu>.
+Fabian Fr\('ed\('erick <ffrederick@users.sourceforge.net> (diskstat, slab, partitions...)
--- /dev/null
+// old: "Copyright 1994 by Henry Ware <al172@yfn.ysu.edu>. Copyleft same year."
+// most code copyright 2002 Albert Cahalan
+//
+// 27/05/2003 (Fabian Frederick) : Add unit conversion + interface
+// Export proc/stat access to libproc
+// Adapt vmstat helpfile
+// 31/05/2003 (Fabian) : Add diskstat support (/libproc)
+// June 2003 (Fabian) : -S <x> -s & -s -S <x> patch
+// June 2003 (Fabian) : -Adding diskstat against 3.1.9, slabinfo
+// -patching 'header' in disk & slab
+// July 2003 (Fabian) : -Adding disk partition output
+// -Adding disk table
+// -Syncing help / usage
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/dir.h>
+#include <dirent.h>
+
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+
+static unsigned long dataUnit=1024;
+static char szDataUnit [16];
+#define UNIT_B 1
+#define UNIT_k 1000
+#define UNIT_K 1024
+#define UNIT_m 1000000
+#define UNIT_M 1048576
+
+#define VMSTAT 0
+#define DISKSTAT 0x00000001
+#define VMSUMSTAT 0x00000002
+#define SLABSTAT 0x00000004
+#define PARTITIONSTAT 0x00000008
+#define DISKSUMSTAT 0x00000010
+
+static int statMode=VMSTAT;
+
+#define FALSE 0
+#define TRUE 1
+
+static int a_option; /* "-a" means "show active/inactive" */
+
+static unsigned sleep_time = 1;
+static unsigned long num_updates;
+
+static unsigned int height; // window height
+static unsigned int moreheaders=TRUE;
+
+
+/////////////////////////////////////////////////////////////////////////
+
+static void usage(void) NORETURN;
+static void usage(void) {
+ fprintf(stderr,"usage: vmstat [-V] [-n] [delay [count]]\n");
+ fprintf(stderr," -V prints version.\n");
+ fprintf(stderr," -n causes the headers not to be reprinted regularly.\n");
+ fprintf(stderr," -a print inactive/active page stats.\n");
+ fprintf(stderr," -d prints disk statistics\n");
+ fprintf(stderr," -D prints disk table\n");
+ fprintf(stderr," -p prints disk partition statistics\n");
+ fprintf(stderr," -s prints vm table\n");
+ fprintf(stderr," -m prints slabinfo\n");
+ fprintf(stderr," -S unit size\n");
+ fprintf(stderr," delay is the delay between updates in seconds. \n");
+ fprintf(stderr," unit size k:1000 K:1024 m:1000000 M:1048576 (default is K)\n");
+ fprintf(stderr," count is the number of updates.\n");
+ exit(EXIT_FAILURE);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if 0
+// produce: " 6 ", "123 ", "123k ", etc.
+static int format_1024(unsigned long long val64, char *restrict dst){
+ unsigned oldval;
+ const char suffix[] = " kmgtpe";
+ unsigned level = 0;
+ unsigned val32;
+
+ if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do
+ val32 = val64;
+ return sprintf(dst,"%3u ",val32);
+ }
+
+ while(val64 > 0xffffffffull){
+ level++;
+ val64 /= 1024;
+ }
+
+ val32 = val64;
+
+ while(val32 > 999){
+ level++;
+ oldval = val32;
+ val32 /= 1024;
+ }
+
+ if(val32 < 10){
+ unsigned fract = (oldval % 1024) * 10 / 1024;
+ return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]);
+ }
+ return sprintf(dst, "%3u%c ", val32, suffix[level]);
+}
+
+
+// produce: " 6 ", "123 ", "123k ", etc.
+static int format_1000(unsigned long long val64, char *restrict dst){
+ unsigned oldval;
+ const char suffix[] = " kmgtpe";
+ unsigned level = 0;
+ unsigned val32;
+
+ if(val64 < 1000){ // special case to avoid "6.0 " when plain " 6 " would do
+ val32 = val64;
+ return sprintf(dst,"%3u ",val32);
+ }
+
+ while(val64 > 0xffffffffull){
+ level++;
+ val64 /= 1000;
+ }
+
+ val32 = val64;
+
+ while(val32 > 999){
+ level++;
+ oldval = val32;
+ val32 /= 1000;
+ }
+
+ if(val32 < 10){
+ unsigned fract = (oldval % 1000) / 100;
+ return sprintf(dst, "%u.%u%c ", val32, fract, suffix[level]);
+ }
+ return sprintf(dst, "%3u%c ", val32, suffix[level]);
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////
+
+static void new_header(void){
+ printf("procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n");
+ printf(
+ "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s\n",
+ "r","b",
+ "swpd", "free", a_option?"inact":"buff", a_option?"active":"cache",
+ "si","so",
+ "bi","bo",
+ "in","cs",
+ "us","sy","id","wa"
+ );
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static unsigned long unitConvert(unsigned int size){
+ float cvSize;
+ cvSize=(float)size/dataUnit*((statMode==SLABSTAT)?1:1024);
+ return ((unsigned long) cvSize);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void new_format(void) {
+ const char format[]="%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u\n";
+ unsigned int tog=0; /* toggle switch for cleaner code */
+ unsigned int i;
+ unsigned int hz = Hertz;
+ unsigned int running,blocked,dummy_1,dummy_2;
+ jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2], cpu_iow[2], cpu_xxx[2], cpu_yyy[2], cpu_zzz[2];
+ jiff duse, dsys, didl, diow, dstl, Div, divo2;
+ unsigned long pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
+ unsigned int intr[2], ctxt[2];
+ unsigned int sleep_half;
+ unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul;
+ int debt = 0; // handle idle ticks running backwards
+
+ sleep_half=(sleep_time/2);
+ new_header();
+ meminfo();
+
+ getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl,cpu_iow,cpu_xxx,cpu_yyy,cpu_zzz,
+ pgpgin,pgpgout,pswpin,pswpout,
+ intr,ctxt,
+ &running,&blocked,
+ &dummy_1, &dummy_2);
+
+ duse= *cpu_use + *cpu_nic;
+ dsys= *cpu_sys + *cpu_xxx + *cpu_yyy;
+ didl= *cpu_idl;
+ diow= *cpu_iow;
+ dstl= *cpu_zzz;
+ Div= duse+dsys+didl+diow+dstl;
+ divo2= Div/2UL;
+ printf(format,
+ running, blocked,
+ unitConvert(kb_swap_used), unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+ (unsigned)( (*pswpin * unitConvert(kb_per_page) * hz + divo2) / Div ),
+ (unsigned)( (*pswpout * unitConvert(kb_per_page) * hz + divo2) / Div ),
+ (unsigned)( (*pgpgin * hz + divo2) / Div ),
+ (unsigned)( (*pgpgout * hz + divo2) / Div ),
+ (unsigned)( (*intr * hz + divo2) / Div ),
+ (unsigned)( (*ctxt * hz + divo2) / Div ),
+ (unsigned)( (100*duse + divo2) / Div ),
+ (unsigned)( (100*dsys + divo2) / Div ),
+ (unsigned)( (100*didl + divo2) / Div ),
+ (unsigned)( (100*diow + divo2) / Div ) /* ,
+ (unsigned)( (100*dstl + divo2) / Div ) */
+ );
+
+ for(i=1;i<num_updates;i++) { /* \\\\\\\\\\\\\\\\\\\\ main loop ////////////////// */
+ sleep(sleep_time);
+ if (moreheaders && ((i%height)==0)) new_header();
+ tog= !tog;
+
+ meminfo();
+
+ getstat(cpu_use+tog,cpu_nic+tog,cpu_sys+tog,cpu_idl+tog,cpu_iow+tog,cpu_xxx+tog,cpu_yyy+tog,cpu_zzz+tog,
+ pgpgin+tog,pgpgout+tog,pswpin+tog,pswpout+tog,
+ intr+tog,ctxt+tog,
+ &running,&blocked,
+ &dummy_1,&dummy_2);
+
+ duse= cpu_use[tog]-cpu_use[!tog] + cpu_nic[tog]-cpu_nic[!tog];
+ dsys= cpu_sys[tog]-cpu_sys[!tog] + cpu_xxx[tog]-cpu_xxx[!tog] + cpu_yyy[tog]-cpu_yyy[!tog];
+ didl= cpu_idl[tog]-cpu_idl[!tog];
+ diow= cpu_iow[tog]-cpu_iow[!tog];
+ dstl= cpu_zzz[tog]-cpu_zzz[!tog];
+
+ /* idle can run backwards for a moment -- kernel "feature" */
+ if(debt){
+ didl = (int)didl + debt;
+ debt = 0;
+ }
+ if( (int)didl < 0 ){
+ debt = (int)didl;
+ didl = 0;
+ }
+
+ Div= duse+dsys+didl+diow+dstl;
+ divo2= Div/2UL;
+ printf(format,
+ running, blocked,
+ unitConvert(kb_swap_used),unitConvert(kb_main_free),
+ unitConvert(a_option?kb_inactive:kb_main_buffers),
+ unitConvert(a_option?kb_active:kb_main_cached),
+ (unsigned)( ( (pswpin [tog] - pswpin [!tog])*unitConvert(kb_per_page)+sleep_half )/sleep_time ), /*si*/
+ (unsigned)( ( (pswpout[tog] - pswpout[!tog])*unitConvert(kb_per_page)+sleep_half )/sleep_time ), /*so*/
+ (unsigned)( ( pgpgin [tog] - pgpgin [!tog] +sleep_half )/sleep_time ), /*bi*/
+ (unsigned)( ( pgpgout[tog] - pgpgout[!tog] +sleep_half )/sleep_time ), /*bo*/
+ (unsigned)( ( intr [tog] - intr [!tog] +sleep_half )/sleep_time ), /*in*/
+ (unsigned)( ( ctxt [tog] - ctxt [!tog] +sleep_half )/sleep_time ), /*cs*/
+ (unsigned)( (100*duse+divo2)/Div ), /*us*/
+ (unsigned)( (100*dsys+divo2)/Div ), /*sy*/
+ (unsigned)( (100*didl+divo2)/Div ), /*id*/
+ (unsigned)( (100*diow+divo2)/Div )/*, //wa
+ (unsigned)( (100*dstl+divo2)/Div ) //st */
+ );
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void diskpartition_header(const char *partition_name){
+ printf("%-10s %10s %10s %10s %10s\n",partition_name, "reads ", "read sectors", "writes ", "requested writes");
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static int diskpartition_format(const char* partition_name){
+ FILE *fDiskstat;
+ struct disk_stat *disks;
+ struct partition_stat *partitions, *current_partition=NULL;
+ unsigned long ndisks, j, k, npartitions;
+ const char format[] = "%20u %10llu %10u %10u\n";
+
+ fDiskstat=fopen("/proc/diskstats","rb");
+ if(!fDiskstat){
+ fprintf(stderr, "Your kernel doesn't support diskstat. (2.5.70 or above required)\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fclose(fDiskstat);
+ ndisks=getdiskstat(&disks,&partitions);
+ npartitions=getpartitions_num(disks, ndisks);
+ for(k=0; k<npartitions; k++){
+ if(!strcmp(partition_name, partitions[k].partition_name)){
+ current_partition=&(partitions[k]);
+ }
+ }
+ if(!current_partition){
+ return -1;
+ }
+ diskpartition_header(partition_name);
+ printf (format,
+ current_partition->reads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes);
+ fflush(stdout);
+ free(disks);
+ free(partitions);
+ for(j=1; j<num_updates; j++){
+ if (moreheaders && ((j%height)==0)) diskpartition_header(partition_name);
+ sleep(sleep_time);
+ ndisks=getdiskstat(&disks,&partitions);
+ npartitions=getpartitions_num(disks, ndisks);
+ current_partition=NULL;
+ for(k=0; k<npartitions; k++){
+ if(!strcmp(partition_name, partitions[k].partition_name)){
+ current_partition=&(partitions[k]);
+ }
+ }
+ if(!current_partition){
+ return -1;
+ }
+ printf (format,
+ current_partition->reads,current_partition->reads_sectors,current_partition->writes,current_partition->requested_writes);
+ fflush(stdout);
+ free(disks);
+ free(partitions);
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void diskheader(void){
+ printf("disk- ------------reads------------ ------------writes----------- -----IO------\n");
+
+ printf("%5s %6s %6s %7s %7s %6s %6s %7s %7s %6s %6s\n",
+ " ", "total", "merged","sectors","ms","total","merged","sectors","ms","cur","sec");
+
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void diskformat(void){
+ FILE *fDiskstat;
+ struct disk_stat *disks;
+ struct partition_stat *partitions;
+ unsigned long ndisks,i,j,k;
+ const char format[]="%-5s %6u %6u %7llu %7u %6u %6u %7llu %7u %6u %6u\n";
+ if ((fDiskstat=fopen("/proc/diskstats", "rb"))){
+ fclose(fDiskstat);
+ ndisks=getdiskstat(&disks,&partitions);
+ for(k=0; k<ndisks; k++){
+ if (moreheaders && ((k%height)==0)) diskheader();
+ printf(format,
+ disks[k].disk_name,
+ disks[k].reads,
+ disks[k].merged_reads,
+ disks[k].reads_sectors,
+ disks[k].milli_reading,
+ disks[k].writes,
+ disks[k].merged_writes,
+ disks[k].written_sectors,
+ disks[k].milli_writing,
+ disks[k].inprogress_IO?disks[k].inprogress_IO/1000:0,
+ disks[k].milli_spent_IO?disks[k].milli_spent_IO/1000:0/*,
+ disks[i].weighted_milli_spent_IO/1000*/
+ );
+ fflush(stdout);
+ }
+ free(disks);
+ free(partitions);
+ for(j=1; j<num_updates; j++){
+ sleep(sleep_time);
+ ndisks=getdiskstat(&disks,&partitions);
+ for(i=0; i<ndisks; i++,k++){
+ if (moreheaders && ((k%height)==0)) diskheader();
+ printf(format,
+ disks[i].disk_name,
+ disks[i].reads,
+ disks[i].merged_reads,
+ disks[i].reads_sectors,
+ disks[i].milli_reading,
+ disks[i].writes,
+ disks[i].merged_writes,
+ disks[i].written_sectors,
+ disks[i].milli_writing,
+ disks[i].inprogress_IO?disks[i].inprogress_IO/1000:0,
+ disks[i].milli_spent_IO?disks[i].milli_spent_IO/1000:0/*,
+ disks[i].weighted_milli_spent_IO/1000*/
+ );
+ fflush(stdout);
+ }
+ free(disks);
+ free(partitions);
+ }
+ }else{
+ fprintf(stderr, "Your kernel doesn't support diskstat (2.5.70 or above required)\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void slabheader(void){
+ printf("%-24s %6s %6s %6s %6s\n","Cache","Num", "Total", "Size", "Pages");
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void slabformat (void){
+ FILE *fSlab;
+ struct slab_cache *slabs;
+ unsigned long nSlab,i,j,k;
+ const char format[]="%-24s %6u %6u %6u %6u\n";
+
+ fSlab=fopen("/proc/slabinfo", "rb");
+ if(!fSlab){
+ fprintf(stderr, "Your kernel doesn't support slabinfo.\n");
+ return;
+ }
+
+ nSlab = getslabinfo(&slabs);
+ for(k=0; k<nSlab; k++){
+ if (moreheaders && ((k%height)==0)) slabheader();
+ printf(format,
+ slabs[k].name,
+ slabs[k].active_objs,
+ slabs[k].num_objs,
+ slabs[k].objsize,
+ slabs[k].objperslab
+ );
+ }
+ free(slabs);
+ for(j=1,k=1; j<num_updates; j++) {
+ sleep(sleep_time);
+ nSlab = getslabinfo(&slabs);
+ for(i=0; i<nSlab; i++,k++){
+ if (moreheaders && ((k%height)==0)) slabheader();
+ printf(format,
+ slabs[i].name,
+ slabs[i].active_objs,
+ slabs[i].num_objs,
+ slabs[i].objsize,
+ slabs[i].objperslab
+ );
+ }
+ free(slabs);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void disksum_format(void) {
+
+ FILE *fDiskstat;
+ struct disk_stat *disks;
+ struct partition_stat *partitions;
+ int ndisks, i;
+ unsigned long reads, merged_reads, read_sectors, milli_reading, writes,
+ merged_writes, written_sectors, milli_writing, inprogress_IO,
+ milli_spent_IO, weighted_milli_spent_IO;
+
+ reads=merged_reads=read_sectors=milli_reading=writes=merged_writes= \
+ written_sectors=milli_writing=inprogress_IO=milli_spent_IO= \
+ weighted_milli_spent_IO=0;
+
+ if ((fDiskstat=fopen("/proc/diskstats", "rb"))){
+ fclose(fDiskstat);
+ ndisks=getdiskstat(&disks, &partitions);
+ printf("%13d disks \n", ndisks);
+ printf("%13d partitions \n", getpartitions_num(disks, ndisks));
+
+ for(i=0; i<ndisks; i++){
+ reads+=disks[i].reads;
+ merged_reads+=disks[i].merged_reads;
+ read_sectors+=disks[i].reads_sectors;
+ milli_reading+=disks[i].milli_reading;
+ writes+=disks[i].writes;
+ merged_writes+=disks[i].merged_writes;
+ written_sectors+=disks[i].written_sectors;
+ milli_writing+=disks[i].milli_writing;
+ inprogress_IO+=disks[i].inprogress_IO?disks[i].inprogress_IO/1000:0;
+ milli_spent_IO+=disks[i].milli_spent_IO?disks[i].milli_spent_IO/1000:0;
+ }
+
+ printf("%13lu total reads\n",reads);
+ printf("%13lu merged reads\n",merged_reads);
+ printf("%13lu read sectors\n",read_sectors);
+ printf("%13lu milli reading\n",milli_reading);
+ printf("%13lu writes\n",writes);
+ printf("%13lu merged writes\n",merged_writes);
+ printf("%13lu written sectors\n",written_sectors);
+ printf("%13lu milli writing\n",milli_writing);
+ printf("%13lu inprogress IO\n",inprogress_IO);
+ printf("%13lu milli spent IO\n",milli_spent_IO);
+
+ free(disks);
+ free(partitions);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void sum_format(void) {
+ unsigned int running, blocked, btime, processes;
+ jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
+ unsigned long pgpgin, pgpgout, pswpin, pswpout;
+ unsigned int intr, ctxt;
+
+ meminfo();
+
+ getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
+ &cpu_iow, &cpu_xxx, &cpu_yyy, &cpu_zzz,
+ &pgpgin, &pgpgout, &pswpin, &pswpout,
+ &intr, &ctxt,
+ &running, &blocked,
+ &btime, &processes);
+
+ printf("%13lu %s total memory\n", unitConvert(kb_main_total),szDataUnit);
+ printf("%13lu %s used memory\n", unitConvert(kb_main_used),szDataUnit);
+ printf("%13lu %s active memory\n", unitConvert(kb_active),szDataUnit);
+ printf("%13lu %s inactive memory\n", unitConvert(kb_inactive),szDataUnit);
+ printf("%13lu %s free memory\n", unitConvert(kb_main_free),szDataUnit);
+ printf("%13lu %s buffer memory\n", unitConvert(kb_main_buffers),szDataUnit);
+ printf("%13lu %s swap cache\n", unitConvert(kb_main_cached),szDataUnit);
+ printf("%13lu %s total swap\n", unitConvert(kb_swap_total),szDataUnit);
+ printf("%13lu %s used swap\n", unitConvert(kb_swap_used),szDataUnit);
+ printf("%13lu %s free swap\n", unitConvert(kb_swap_free),szDataUnit);
+ printf("%13Lu non-nice user cpu ticks\n", cpu_use);
+ printf("%13Lu nice user cpu ticks\n", cpu_nic);
+ printf("%13Lu system cpu ticks\n", cpu_sys);
+ printf("%13Lu idle cpu ticks\n", cpu_idl);
+ printf("%13Lu IO-wait cpu ticks\n", cpu_iow);
+ printf("%13Lu IRQ cpu ticks\n", cpu_xxx);
+ printf("%13Lu softirq cpu ticks\n", cpu_yyy);
+ printf("%13Lu stolen cpu ticks\n", cpu_zzz);
+ printf("%13lu pages paged in\n", pgpgin);
+ printf("%13lu pages paged out\n", pgpgout);
+ printf("%13lu pages swapped in\n", pswpin);
+ printf("%13lu pages swapped out\n", pswpout);
+ printf("%13u interrupts\n", intr);
+ printf("%13u CPU context switches\n", ctxt);
+ printf("%13u boot time\n", btime);
+ printf("%13u forks\n", processes);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void fork_format(void) {
+ unsigned int running, blocked, btime, processes;
+ jiff cpu_use, cpu_nic, cpu_sys, cpu_idl, cpu_iow, cpu_xxx, cpu_yyy, cpu_zzz;
+ unsigned long pgpgin, pgpgout, pswpin, pswpout;
+ unsigned int intr, ctxt;
+
+ getstat(&cpu_use, &cpu_nic, &cpu_sys, &cpu_idl,
+ &cpu_iow, &cpu_xxx, &cpu_yyy, &cpu_zzz,
+ &pgpgin, &pgpgout, &pswpin, &pswpout,
+ &intr, &ctxt,
+ &running, &blocked,
+ &btime, &processes);
+
+ printf("%13u forks\n", processes);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[]) {
+ char partition[16];
+ argc=0; /* redefined as number of integer arguments */
+ for (argv++;*argv;argv++) {
+ if ('-' ==(**argv)) {
+ switch (*(++(*argv))) {
+
+ case 'V':
+ display_version();
+ exit(0);
+ case 'd':
+ statMode |= DISKSTAT;
+ break;
+ case 'a':
+ /* active/inactive mode */
+ a_option=1;
+ break;
+ case 'f':
+ // FIXME: check for conflicting args
+ fork_format();
+ exit(0);
+ case 'm':
+ statMode |= SLABSTAT;
+ break;
+ case 'D':
+ statMode |= DISKSUMSTAT;
+ break;
+ case 'n':
+ /* print only one header */
+ moreheaders=FALSE;
+ break;
+ case 'p':
+ statMode |= PARTITIONSTAT;
+ if (argv[1]){
+ char *cp = *++argv;
+ if(!memcmp(cp,"/dev/",5)) cp += 5;
+ snprintf(partition, sizeof partition, "%s", cp);
+ }else{
+ fprintf(stderr, "-p requires an argument\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'S':
+ if (argv[1]){
+ ++argv;
+ if (!strcmp(*argv, "k")) dataUnit=UNIT_k;
+ else if (!strcmp(*argv, "K")) dataUnit=UNIT_K;
+ else if (!strcmp(*argv, "m")) dataUnit=UNIT_m;
+ else if (!strcmp(*argv, "M")) dataUnit=UNIT_M;
+ else {fprintf(stderr, "-S requires k, K, m or M (default is kb)\n");
+ exit(EXIT_FAILURE);
+ }
+ strcpy(szDataUnit, *argv);
+ }else {fprintf(stderr, "-S requires an argument\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 's':
+ statMode |= VMSUMSTAT;
+ break;
+ default:
+ /* no other aguments defined yet. */
+ usage();
+ }
+ }else{
+ argc++;
+ switch (argc) {
+ case 1:
+ if ((sleep_time = atoi(*argv)) == 0)
+ usage();
+ num_updates = ULONG_MAX;
+ break;
+ case 2:
+ num_updates = atol(*argv);
+ break;
+ default:
+ usage();
+ } /* switch */
+ }
+}
+ if (moreheaders) {
+ int tmp=winhi()-3;
+ height=((tmp>0)?tmp:22);
+ }
+ setlinebuf(stdout);
+ switch(statMode){
+ case(VMSTAT): new_format();
+ break;
+ case(VMSUMSTAT): sum_format();
+ break;
+ case(DISKSTAT): diskformat();
+ break;
+ case(PARTITIONSTAT): if(diskpartition_format(partition)==-1)
+ printf("Partition was not found\n");
+ break;
+ case(SLABSTAT): slabformat();
+ break;
+ case(DISKSUMSTAT): disksum_format();
+ break;
+ default: usage();
+ break;
+ }
+ return 0;
+}
+
+
--- /dev/null
+.\" -*-Nroff-*-
+.\"
+.TH W 1 "8 Dec 1993 " " " "Linux User's Manual"
+.SH NAME
+w \- Show who is logged on and what they are doing.
+.SH SYNOPSIS
+.B w \-
+.RB [ husfV ]
+.RI [ user ]
+.SH DESCRIPTION
+.B "w "
+displays information about the users currently on the machine,
+and their processes.
+The header shows, in this order, the current time,
+how long the system has been running,
+how many users are currently logged on,
+and the system load averages for the past 1, 5, and 15 minutes.
+.sp
+The following entries are displayed for each user:
+login name, the tty name, the remote host, login time, idle time, JCPU, PCPU,
+and the command line of their current process.
+.sp
+The JCPU time is the time used by all processes attached to the tty. It
+does not include past background jobs, but does include currently
+running background jobs.
+.sp
+The PCPU time is the time used by the current process, named in the "what"
+field.
+
+.PP
+.SH "COMMAND\-LINE OPTIONS"
+.TP 0.5i
+.B "\-h "
+Don't print the header.
+.TP 0.5i
+.B "\-u "
+Ignores the username while figuring out the current process and cpu
+times. To demonstrate this, do a "su" and do a "w" and a "w -u".
+.TP 0.5i
+.B "\-s "
+Use the short format.
+Don't print the login time, JCPU or PCPU times.
+.TP 0.5i
+.B "\-f "
+Toggle printing the
+.B from
+(remote hostname) field. The default as
+released is for the
+.B from
+field to not be printed, although your system administrator or
+distribution maintainer may have compiled a version in which the
+.B from
+field is shown by default.
+.TP 0.5i
+.B "\-V "
+Display version information.
+.TP 0.5i
+.B "user "
+Show information about the specified user only.
+
+.SH FILES
+.TP
+.I /var/run/utmp
+information about who is currently logged on
+.TP
+.I /proc
+process information
+.PP
+
+.SH "SEE ALSO"
+.BR free (1),
+.BR ps (1),
+.BR top (1),
+.BR uptime (1),
+.BR utmp (5),
+.BR who (1)
+
+.SH AUTHORS
+.B w
+was re-written almost entirely by Charles Blake, based on the version by Larry
+Greenfield <greenfie@gauss.rutgers.edu> and Michael K. Johnson
+<johnsonm@redhat.com>.
+
+Please send bug reports to <albert@users.sf.net>
--- /dev/null
+/* w - show what logged in users are doing. Almost entirely rewritten from
+ * scratch by Charles Blake circa June 1996. Some vestigal traces of the
+ * original may exist. That was done in 1993 by Larry Greenfield with some
+ * fixes by Michael K. Johnson.
+ *
+ * Changes by Albert Cahalan, 2002.
+ */
+#include "proc/version.h"
+#include "proc/whattime.h"
+#include "proc/readproc.h"
+#include "proc/devname.h"
+#include "proc/procps.h"
+#include "proc/sysinfo.h"
+#include "proc/escape.h"
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <locale.h>
+#include <termios.h>
+
+static int ignoreuser = 0; /* for '-u' */
+static proc_t **procs; /* our snapshot of the process table */
+
+typedef struct utmp utmp_t;
+
+#ifdef W_SHOWFROM
+# define FROM_STRING "on"
+#else
+# define FROM_STRING "off"
+#endif
+
+/* Uh... same thing as UT_NAMESIZE */
+#define USERSZ (sizeof u->ut_user)
+
+
+/* This routine is careful since some programs leave utmp strings
+ * unprintable. Always outputs at least 16 chars padded with spaces
+ * on the right if necessary.
+ */
+static void print_host(const char *restrict host, int len) {
+ const char *last;
+ int width = 0;
+
+ /* FIXME: there should really be a way to configure this... */
+ /* for now, we'll just limit it to the 16 that the libc5 version
+ * of utmp uses.
+ */
+ if (len > 16) len = 16;
+ last = host + len;
+ for ( ; host < last ; host++){
+ if (isprint(*host) && *host != ' ') {
+ fputc(*host, stdout);
+ ++width;
+ } else {
+ break;
+ }
+ }
+ // space-fill, and a '-' too if needed to ensure the column exists
+ if(width < 16) fputs("- "+width, stdout);
+}
+
+/***** compact 7 char format for time intervals (belongs in libproc?) */
+static void print_time_ival7(time_t t, int centi_sec, FILE* fout) {
+ if((long)t < (long)0){ /* system clock changed? */
+ printf(" ? ");
+ return;
+ }
+ if (t >= 48*60*60) /* > 2 days */
+ fprintf(fout, " %2ludays", t/(24*60*60));
+ else if (t >= 60*60) /* > 1 hour */
+ fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60));
+ else if (t > 60) /* > 1 minute */
+ fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
+ else
+ fprintf(fout, " %2lu.%02us", t, centi_sec);
+}
+
+/**** stat the device file to get an idle time */
+static time_t idletime(const char *restrict const tty) {
+ struct stat sbuf;
+ if (stat(tty, &sbuf) != 0)
+ return 0;
+ return time(NULL) - sbuf.st_atime;
+}
+
+/***** 7 character formatted login time */
+static void print_logintime(time_t logt, FILE* fout) {
+ char weekday[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
+ month [][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
+ "Aug", "Sep", "Oct", "Nov", "Dec" };
+ time_t curt;
+ struct tm *logtm, *curtm;
+ int today;
+
+ curt = time(NULL);
+ curtm = localtime(&curt);
+ /* localtime returns a pointer to static memory */
+ today = curtm->tm_yday;
+ logtm = localtime(&logt);
+ if (curt - logt > 12*60*60 && logtm->tm_yday != today) {
+ if (curt - logt > 6*24*60*60)
+ fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon],
+ logtm->tm_year % 100);
+ else
+ fprintf(fout, " %3s%02d ", weekday[logtm->tm_wday], logtm->tm_hour);
+ } else {
+ fprintf(fout, " %02d:%02d ", logtm->tm_hour, logtm->tm_min);
+ }
+}
+
+
+/* This function scans the process table accumulating total cpu times for
+ * any processes "associated" with this login session. It also searches
+ * for the "best" process to report as "(w)hat" the user for that login
+ * session is doing currently. This the essential core of 'w'.
+ */
+static const proc_t *getproc(const utmp_t *restrict const u, const char *restrict const tty, unsigned long long *restrict const jcpu, int *restrict const found_utpid) {
+ int line;
+ proc_t **pptr = procs;
+ const proc_t *best = NULL;
+ const proc_t *secondbest = NULL;
+ unsigned uid = ~0U;
+
+ *found_utpid = 0;
+ if(!ignoreuser){
+ char buf[UT_NAMESIZE+1];
+ struct passwd *passwd_data; /* pointer to static data */
+ strncpy(buf,u->ut_user,UT_NAMESIZE);
+ buf[UT_NAMESIZE] = '\0';
+ passwd_data = getpwnam(buf);
+ if(!passwd_data) return NULL;
+ uid = passwd_data->pw_uid;
+ /* OK to have passwd_data go out of scope here */
+ }
+ line = tty_to_dev(tty);
+ *jcpu = 0;
+ for(; *pptr; pptr++) {
+ const proc_t *restrict const tmp = *pptr;
+ if(unlikely(tmp->tgid == u->ut_pid)) {
+ *found_utpid = 1;
+ best = tmp;
+ }
+ if(tmp->tty != line) continue;
+ (*jcpu) += tmp->utime + tmp->stime;
+ secondbest = tmp;
+ /* same time-logic here as for "best" below */
+ if(! (secondbest && tmp->start_time <= secondbest->start_time) ){
+ secondbest = tmp;
+ }
+ if(!ignoreuser && uid != tmp->euid && uid != tmp->ruid) continue;
+ if(tmp->tgid != tmp->tpgid) continue;
+ if(best && tmp->start_time <= best->start_time) continue;
+ best = tmp;
+ }
+ return best ? best : secondbest;
+}
+
+
+/***** showinfo */
+static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) {
+ unsigned long long jcpu;
+ int ut_pid_found;
+ unsigned i;
+ char uname[USERSZ + 1] = "",
+ tty[5 + sizeof u->ut_line + 1] = "/dev/";
+ const proc_t *best;
+
+ for (i=0; i < sizeof(u->ut_line); i++) /* clean up tty if garbled */
+ if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/'))
+ tty[i+5] = u->ut_line[i];
+ else
+ tty[i+5] = '\0';
+
+ best = getproc(u, tty + 5, &jcpu, &ut_pid_found);
+
+ /* just skip if stale utmp entry (i.e. login proc doesn't exist). If there
+ * is a desire a cmdline flag could be added to optionally show it with a
+ * prefix of (stale) in front of cmd or something like that.
+ */
+ if (!ut_pid_found)
+ return;
+
+ strncpy(uname, u->ut_user, USERSZ); /* force NUL term for printf */
+ if (formtype) {
+ printf("%-9.8s%-9.8s", uname, u->ut_line);
+ if (from)
+ print_host(u->ut_host, sizeof u->ut_host);
+ print_logintime(u->ut_time, stdout);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+ else
+ print_time_ival7(idletime(tty), 0, stdout);
+ print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout);
+ if (best) {
+ unsigned long long pcpu = best->utime + best->stime;
+ print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout);
+ } else
+ printf(" ? ");
+ } else {
+ printf("%-9.8s%-9.8s", u->ut_user, u->ut_line);
+ if (from)
+ print_host(u->ut_host, sizeof u->ut_host);
+ if (*u->ut_line == ':') /* idle unknown for xdm logins */
+ printf(" ?xdm? ");
+ else
+ print_time_ival7(idletime(tty), 0, stdout);
+ }
+ fputs(" ", stdout);
+ if (likely(best)) {
+ char cmdbuf[512];
+ escape_command(cmdbuf, best, sizeof cmdbuf, &maxcmd, ESC_ARGS);
+ fputs(cmdbuf,stdout);
+ } else {
+ printf("-");
+ }
+ fputc('\n', stdout);
+}
+
+/***** main */
+int main(int argc, char **argv) {
+ char *user = NULL;
+ utmp_t *u;
+ struct winsize win;
+ int header=1, longform=1, from=1, args, maxcmd=80, ch;
+
+#ifndef W_SHOWFROM
+ from = 0;
+#endif
+
+ setlocale(LC_ALL, "");
+ for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++)
+ switch (ch) {
+ case 'h': header = 0; break;
+ case 'l': longform = 1; break;
+ case 's': longform = 0; break;
+ case 'f': from = !from; break;
+ case 'V': display_version(); exit(0);
+ case 'u': ignoreuser = 1; break;
+ default:
+ printf("usage: w -hlsufV [user]\n"
+ " -h skip header\n"
+ " -l long listing (default)\n"
+ " -s short listing\n"
+ " -u ignore uid of processes\n"
+ " -f toggle FROM field (default %s)\n"
+ " -V display version\n", FROM_STRING);
+ exit(1);
+ }
+
+ if ((argv[optind]))
+ user = (argv[optind]);
+
+ if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
+ maxcmd = win.ws_col;
+ if (maxcmd < 71) {
+ fprintf(stderr, "%d column window is too narrow\n", maxcmd);
+ exit(1);
+ }
+ maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0);
+ if (maxcmd < 3)
+ fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
+
+ procs = readproctab(PROC_FILLCOM | PROC_FILLUSR | PROC_FILLSTAT);
+
+ if (header) { /* print uptime and headers */
+ print_uptime();
+ printf("USER TTY ");
+ if (from)
+ printf("FROM ");
+ if (longform)
+ printf(" LOGIN@ IDLE JCPU PCPU WHAT\n");
+ else
+ printf(" IDLE WHAT\n");
+ }
+
+ utmpname(UTMP_FILE);
+ setutent();
+ if (user) {
+ for (;;) {
+ u = getutent();
+ if (unlikely(!u)) break;
+ if (u->ut_type != USER_PROCESS) continue;
+ if (!strncmp(u->ut_user, user, USERSZ)) showinfo(u, longform, maxcmd, from);
+ }
+ } else {
+ for (;;) {
+ u = getutent();
+ if (unlikely(!u)) break;
+ if (u->ut_type != USER_PROCESS) continue;
+ if (*u->ut_user) showinfo(u, longform, maxcmd, from);
+ }
+ }
+ endutent();
+
+ return 0;
+}
--- /dev/null
+.TH WATCH 1 "1999 Apr 3" " " "Linux User's Manual"
+.SH NAME
+watch \- execute a program periodically, showing output fullscreen
+.SH SYNOPSIS
+.B watch
+.I [\-dhvt] [\-n <seconds>] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=<seconds>] [\-\-no\-title] [\-\-version] <command>
+.SH DESCRIPTION
+.BR watch
+runs
+.I command
+repeatedly, displaying its output (the first screenfull). This allows you to
+watch the program output change over time. By default, the program is run
+every 2 seconds; use
+.I -n
+or
+.I --interval
+to specify a different interval.
+.PP
+The
+.I -d
+or
+.I --differences
+flag will highlight the differences between successive updates. The
+.I --cumulative
+option makes highlighting "sticky", presenting a running display of all
+positions that have ever changed. The
+.I -t
+or
+.I --no-title
+option turns off the header showing the interval, command, and current
+time at the top of the display, as well as the following blank line.
+.PP
+.BR watch
+will run until interrupted.
+.SH NOTE
+Note that
+.I command
+is given to "sh -c"
+which means that you may need to use extra quoting to get the desired effect.
+.PP
+Note that POSIX option processing is used (i.e., option processing stops at
+the first non-option argument). This means that flags after
+.I command
+don't get interpreted by
+.BR watch
+itself.
+.SH EXAMPLES
+.PP
+To watch for mail, you might do
+.IP
+watch \-n 60 from
+.PP
+To watch the contents of a directory change, you could use
+.IP
+watch \-d ls \-l
+.PP
+If you're only interested in files owned by user joe, you might use
+.IP
+watch \-d 'ls \-l | fgrep joe'
+.PP
+To see the effects of quoting, try these out
+.IP
+watch echo $$
+.IP
+watch echo '$$'
+.IP
+watch echo "'"'$$'"'"
+.PP
+You can watch for your administrator to install the latest kernel with
+.IP
+watch uname -r
+.PP
+(Just kidding.)
+.SH BUGS
+Upon terminal resize, the screen will not be correctly repainted until the
+next scheduled update. All
+.I --differences
+highlighting is lost on that update as well.
+.PP
+Non-printing characters are stripped from program output. Use "cat -v" as
+part of the command pipeline if you want to see them.
+.SH AUTHORS
+The original
+.B watch
+was written by Tony Rems <rembo@unisoft.com> in 1991, with mods and
+corrections by Francois Pinard. It was reworked and new features added by
+Mike Coleman <mkc@acm.org> in 1999.
--- /dev/null
+/* watch -- execute a program repeatedly, displaying output fullscreen
+ *
+ * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
+ * (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 <mkc@acm.org>.
+ *
+ * Changes by Albert Cahalan, 2002-2003.
+ */
+
+#define VERSION "0.2.0"
+
+#include <ctype.h>
+#include <getopt.h>
+#include <signal.h>
+#include <ncurses.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <termios.h>
+#include <locale.h>
+#include "proc/procps.h"
+
+#ifdef FORCE_8BIT
+#undef isprint
+#define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
+#endif
+
+static struct option longopts[] = {
+ {"differences", optional_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {"interval", required_argument, 0, 'n'},
+ {"no-title", no_argument, 0, 't'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0}
+};
+
+static char usage[] =
+ "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
+
+static char *progname;
+
+static int curses_started = 0;
+static int height = 24, width = 80;
+static int screen_size_changed = 0;
+static int first_screen = 1;
+static int show_title = 2; // number of lines used, 2 or 0
+
+#define min(x,y) ((x) > (y) ? (y) : (x))
+
+static void do_usage(void) NORETURN;
+static void do_usage(void)
+{
+ fprintf(stderr, usage, progname);
+ exit(1);
+}
+
+static void do_exit(int status) NORETURN;
+static void do_exit(int status)
+{
+ if (curses_started)
+ endwin();
+ exit(status);
+}
+
+/* signal handler */
+static void die(int notused) NORETURN;
+static void die(int notused)
+{
+ (void) notused;
+ do_exit(0);
+}
+
+static void
+winch_handler(int notused)
+{
+ (void) notused;
+ screen_size_changed = 1;
+}
+
+static char env_col_buf[24];
+static char env_row_buf[24];
+static int incoming_cols;
+static int incoming_rows;
+
+static void
+get_terminal_size(void)
+{
+ struct winsize w;
+ if(!incoming_cols){ // have we checked COLUMNS?
+ const char *s = getenv("COLUMNS");
+ incoming_cols = -1;
+ if(s && *s){
+ long t;
+ char *endptr;
+ t = strtol(s, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)666)) incoming_cols = (int)t;
+ width = incoming_cols;
+ snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
+ putenv(env_col_buf);
+ }
+ }
+ if(!incoming_rows){ // have we checked LINES?
+ const char *s = getenv("LINES");
+ incoming_rows = -1;
+ if(s && *s){
+ long t;
+ char *endptr;
+ t = strtol(s, &endptr, 0);
+ if(!*endptr && (t>0) && (t<(long)666)) incoming_rows = (int)t;
+ height = incoming_rows;
+ snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
+ putenv(env_row_buf);
+ }
+ }
+ if (incoming_cols<0 || incoming_rows<0){
+ if (ioctl(2, TIOCGWINSZ, &w) == 0) {
+ if (incoming_rows<0 && w.ws_row > 0){
+ height = w.ws_row;
+ snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
+ putenv(env_row_buf);
+ }
+ if (incoming_cols<0 && w.ws_col > 0){
+ width = w.ws_col;
+ snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
+ putenv(env_col_buf);
+ }
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int optc;
+ int option_differences = 0,
+ option_differences_cumulative = 0,
+ option_help = 0, option_version = 0;
+ double interval = 2;
+ char *command;
+ int command_length = 0; /* not including final \0 */
+
+ setlocale(LC_ALL, "");
+ progname = argv[0];
+
+ while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
+ != EOF) {
+ switch (optc) {
+ case 'd':
+ option_differences = 1;
+ if (optarg)
+ option_differences_cumulative = 1;
+ break;
+ case 'h':
+ option_help = 1;
+ break;
+ case 't':
+ show_title = 0;
+ break;
+ case 'n':
+ {
+ char *str;
+ interval = strtod(optarg, &str);
+ if (!*optarg || *str)
+ do_usage();
+ if(interval < 0.1)
+ interval = 0.1;
+ if(interval > ~0u/1000000)
+ interval = ~0u/1000000;
+ }
+ break;
+ case 'v':
+ option_version = 1;
+ break;
+ default:
+ do_usage();
+ break;
+ }
+ }
+
+ if (option_version) {
+ fprintf(stderr, "%s\n", VERSION);
+ if (!option_help)
+ exit(0);
+ }
+
+ if (option_help) {
+ fprintf(stderr, usage, progname);
+ fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
+ fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
+ fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
+ fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
+ fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
+ fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr);
+ exit(0);
+ }
+
+ if (optind >= argc)
+ do_usage();
+
+ command = strdup(argv[optind++]);
+ command_length = strlen(command);
+ for (; optind < argc; optind++) {
+ char *endp;
+ int s = strlen(argv[optind]);
+ command = realloc(command, command_length + s + 2); /* space and \0 */
+ endp = command + command_length;
+ *endp = ' ';
+ memcpy(endp + 1, argv[optind], s);
+ command_length += 1 + s; /* space then string length */
+ command[command_length] = '\0';
+ }
+
+ get_terminal_size();
+
+ /* Catch keyboard interrupts so we can put tty back in a sane state. */
+ signal(SIGINT, die);
+ signal(SIGTERM, die);
+ signal(SIGHUP, die);
+ signal(SIGWINCH, winch_handler);
+
+ /* Set up tty for curses use. */
+ curses_started = 1;
+ initscr();
+ nonl();
+ noecho();
+ cbreak();
+
+ for (;;) {
+ time_t t = time(NULL);
+ char *ts = ctime(&t);
+ int tsl = strlen(ts);
+ char *header;
+ FILE *p;
+ int x, y;
+ int oldeolseen = 1;
+
+ if (screen_size_changed) {
+ get_terminal_size();
+ resizeterm(height, width);
+ clear();
+ /* redrawwin(stdscr); */
+ screen_size_changed = 0;
+ first_screen = 1;
+ }
+
+ if (show_title) {
+ // left justify interval and command,
+ // right justify time, clipping all to fit window width
+ asprintf(&header, "Every %.1fs: %.*s",
+ interval, min(width - 1, command_length), command);
+ mvaddstr(0, 0, header);
+ if (strlen(header) > (size_t) (width - tsl - 1))
+ mvaddstr(0, width - tsl - 4, "... ");
+ mvaddstr(0, width - tsl + 1, ts);
+ free(header);
+ }
+
+ if (!(p = popen(command, "r"))) {
+ perror("popen");
+ do_exit(2);
+ }
+
+ for (y = show_title; y < height; y++) {
+ int eolseen = 0, tabpending = 0;
+ for (x = 0; x < width; x++) {
+ int c = ' ';
+ int attr = 0;
+
+ if (!eolseen) {
+ /* if there is a tab pending, just spit spaces until the
+ next stop instead of reading characters */
+ if (!tabpending)
+ do
+ c = getc(p);
+ while (c != EOF && !isprint(c)
+ && c != '\n'
+ && c != '\t');
+ if (c == '\n')
+ if (!oldeolseen && x == 0) {
+ x = -1;
+ continue;
+ } else
+ eolseen = 1;
+ else if (c == '\t')
+ tabpending = 1;
+ if (c == EOF || c == '\n' || c == '\t')
+ c = ' ';
+ if (tabpending && (((x + 1) % 8) == 0))
+ tabpending = 0;
+ }
+ move(y, x);
+ if (option_differences) {
+ chtype oldch = inch();
+ char oldc = oldch & A_CHARTEXT;
+ attr = !first_screen
+ && ((char)c != oldc
+ ||
+ (option_differences_cumulative
+ && (oldch & A_ATTRIBUTES)));
+ }
+ if (attr)
+ standout();
+ addch(c);
+ if (attr)
+ standend();
+ }
+ oldeolseen = eolseen;
+ }
+
+ pclose(p);
+
+ first_screen = 0;
+ refresh();
+ usleep(interval * 1000000);
+ }
+
+ endwin();
+
+ return 0;
+}